glsl: Linker support for ARB_shader_atomic_counters.
v2: Add comments on the purpose of the auxiliary data structures. Check for atomic counter overlaps. Use the contains_atomic() convenience method. Add static assert with the number of expected shader stages. v3: Don't resize atomic arrays. v4: Add comment on the reason why we don't resize atomic counter arrays. Use 'strcmp(...) == 0' instead of '!strcmp(...)'. v5 (idr): Don't use STL in the linker. Signed-off-by: Francisco Jerez <currojerez@riseup.net> Signed-off-by: Ian Romanick <ian.d.romanick@intel.com> Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
This commit is contained in:
parent
e63bb29853
commit
5c114939b4
|
@ -47,6 +47,7 @@ LIBGLSL_FILES = \
|
|||
$(GLSL_SRCDIR)/ir_validate.cpp \
|
||||
$(GLSL_SRCDIR)/ir_variable_refcount.cpp \
|
||||
$(GLSL_SRCDIR)/linker.cpp \
|
||||
$(GLSL_SRCDIR)/link_atomics.cpp \
|
||||
$(GLSL_SRCDIR)/link_functions.cpp \
|
||||
$(GLSL_SRCDIR)/link_interface_blocks.cpp \
|
||||
$(GLSL_SRCDIR)/link_uniforms.cpp \
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright © 2013 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ir.h"
|
||||
#include "ir_uniform.h"
|
||||
#include "linker.h"
|
||||
#include "program/hash_table.h"
|
||||
#include "main/macros.h"
|
||||
|
||||
namespace {
|
||||
/*
|
||||
* Atomic counter as seen by the program.
|
||||
*/
|
||||
struct active_atomic_counter {
|
||||
unsigned id;
|
||||
ir_variable *var;
|
||||
};
|
||||
|
||||
/*
|
||||
* Atomic counter buffer referenced by the program. There is a one
|
||||
* to one correspondence between these and the objects that can be
|
||||
* queried using glGetActiveAtomicCounterBufferiv().
|
||||
*/
|
||||
struct active_atomic_buffer {
|
||||
active_atomic_buffer()
|
||||
: counters(0), num_counters(0), stage_references(), size(0)
|
||||
{}
|
||||
|
||||
~active_atomic_buffer()
|
||||
{
|
||||
free(counters);
|
||||
}
|
||||
|
||||
void push_back(unsigned id, ir_variable *var)
|
||||
{
|
||||
counters = (active_atomic_counter *)
|
||||
realloc(counters, sizeof(active_atomic_counter) * (num_counters + 1));
|
||||
|
||||
counters[num_counters].id = id;
|
||||
counters[num_counters].var = var;
|
||||
num_counters++;
|
||||
}
|
||||
|
||||
active_atomic_counter *counters;
|
||||
unsigned num_counters;
|
||||
unsigned stage_references[MESA_SHADER_TYPES];
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
int
|
||||
cmp_actives(const void *a, const void *b)
|
||||
{
|
||||
const active_atomic_counter *const first = (active_atomic_counter *) a;
|
||||
const active_atomic_counter *const second = (active_atomic_counter *) b;
|
||||
|
||||
return int(first->var->atomic.offset) - int(second->var->atomic.offset);
|
||||
}
|
||||
|
||||
bool
|
||||
check_atomic_counters_overlap(const ir_variable *x, const ir_variable *y)
|
||||
{
|
||||
return ((x->atomic.offset >= y->atomic.offset &&
|
||||
x->atomic.offset < y->atomic.offset + y->type->atomic_size()) ||
|
||||
(y->atomic.offset >= x->atomic.offset &&
|
||||
y->atomic.offset < x->atomic.offset + x->type->atomic_size()));
|
||||
}
|
||||
|
||||
active_atomic_buffer *
|
||||
find_active_atomic_counters(struct gl_context *ctx,
|
||||
struct gl_shader_program *prog,
|
||||
unsigned *num_buffers)
|
||||
{
|
||||
active_atomic_buffer *const buffers =
|
||||
new active_atomic_buffer[ctx->Const.MaxAtomicBufferBindings];
|
||||
|
||||
*num_buffers = 0;
|
||||
|
||||
for (unsigned i = 0; i < MESA_SHADER_TYPES; ++i) {
|
||||
struct gl_shader *sh = prog->_LinkedShaders[i];
|
||||
if (sh == NULL)
|
||||
continue;
|
||||
|
||||
foreach_list(node, sh->ir) {
|
||||
ir_variable *var = ((ir_instruction *)node)->as_variable();
|
||||
|
||||
if (var && var->type->contains_atomic()) {
|
||||
unsigned id;
|
||||
bool found = prog->UniformHash->get(id, var->name);
|
||||
assert(found);
|
||||
active_atomic_buffer *buf = &buffers[var->binding];
|
||||
|
||||
/* If this is the first time the buffer is used, increment
|
||||
* the counter of buffers used.
|
||||
*/
|
||||
if (buf->size == 0)
|
||||
(*num_buffers)++;
|
||||
|
||||
buf->push_back(id, var);
|
||||
|
||||
buf->stage_references[i]++;
|
||||
buf->size = MAX2(buf->size, var->atomic.offset +
|
||||
var->type->atomic_size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < ctx->Const.MaxAtomicBufferBindings; i++) {
|
||||
if (buffers[i].size == 0)
|
||||
continue;
|
||||
|
||||
qsort(buffers[i].counters, buffers[i].num_counters,
|
||||
sizeof(active_atomic_counter),
|
||||
cmp_actives);
|
||||
|
||||
for (unsigned j = 1; j < buffers[i].num_counters; j++) {
|
||||
/* If an overlapping counter found, it must be a reference to the
|
||||
* same counter from a different shader stage.
|
||||
*/
|
||||
if (check_atomic_counters_overlap(buffers[i].counters[j-1].var,
|
||||
buffers[i].counters[j].var)
|
||||
&& strcmp(buffers[i].counters[j-1].var->name,
|
||||
buffers[i].counters[j].var->name) != 0) {
|
||||
linker_error(prog, "Atomic counter %s declared at offset %d "
|
||||
"which is already in use.",
|
||||
buffers[i].counters[j].var->name,
|
||||
buffers[i].counters[j].var->atomic.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffers;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
link_assign_atomic_counter_resources(struct gl_context *ctx,
|
||||
struct gl_shader_program *prog)
|
||||
{
|
||||
unsigned num_buffers;
|
||||
active_atomic_buffer *abs =
|
||||
find_active_atomic_counters(ctx, prog, &num_buffers);
|
||||
|
||||
prog->AtomicBuffers = rzalloc_array(prog, gl_active_atomic_buffer,
|
||||
num_buffers);
|
||||
prog->NumAtomicBuffers = num_buffers;
|
||||
|
||||
unsigned i = 0;
|
||||
for (unsigned binding = 0;
|
||||
binding < ctx->Const.MaxAtomicBufferBindings;
|
||||
binding++) {
|
||||
|
||||
/* If the binding was not used, skip.
|
||||
*/
|
||||
if (abs[binding].size == 0)
|
||||
continue;
|
||||
|
||||
active_atomic_buffer &ab = abs[binding];
|
||||
gl_active_atomic_buffer &mab = prog->AtomicBuffers[i];
|
||||
|
||||
/* Assign buffer-specific fields. */
|
||||
mab.Binding = binding;
|
||||
mab.MinimumSize = ab.size;
|
||||
mab.Uniforms = rzalloc_array(prog->AtomicBuffers, GLuint,
|
||||
ab.num_counters);
|
||||
mab.NumUniforms = ab.num_counters;
|
||||
|
||||
/* Assign counter-specific fields. */
|
||||
for (unsigned j = 0; j < ab.num_counters; j++) {
|
||||
ir_variable *const var = ab.counters[j].var;
|
||||
const unsigned id = ab.counters[j].id;
|
||||
gl_uniform_storage *const storage = &prog->UniformStorage[id];
|
||||
|
||||
mab.Uniforms[j] = id;
|
||||
var->atomic.buffer_index = i;
|
||||
storage->atomic_buffer_index = i;
|
||||
storage->offset = var->atomic.offset;
|
||||
storage->array_stride = (var->type->is_array() ?
|
||||
var->type->element_type()->atomic_size() : 0);
|
||||
}
|
||||
|
||||
/* Assign stage-specific fields. */
|
||||
for (unsigned j = 0; j < MESA_SHADER_TYPES; ++j)
|
||||
mab.StageReferences[j] =
|
||||
(ab.stage_references[j] ? GL_TRUE : GL_FALSE);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
delete [] abs;
|
||||
assert(i == num_buffers);
|
||||
}
|
||||
|
||||
void
|
||||
link_check_atomic_counter_resources(struct gl_context *ctx,
|
||||
struct gl_shader_program *prog)
|
||||
{
|
||||
STATIC_ASSERT(MESA_SHADER_TYPES == 3);
|
||||
static const char *shader_names[MESA_SHADER_TYPES] = {
|
||||
"vertex", "geometry", "fragment"
|
||||
};
|
||||
const unsigned max_atomic_counters[MESA_SHADER_TYPES] = {
|
||||
ctx->Const.VertexProgram.MaxAtomicCounters,
|
||||
ctx->Const.GeometryProgram.MaxAtomicCounters,
|
||||
ctx->Const.FragmentProgram.MaxAtomicCounters
|
||||
};
|
||||
const unsigned max_atomic_buffers[MESA_SHADER_TYPES] = {
|
||||
ctx->Const.VertexProgram.MaxAtomicBuffers,
|
||||
ctx->Const.GeometryProgram.MaxAtomicBuffers,
|
||||
ctx->Const.FragmentProgram.MaxAtomicBuffers
|
||||
};
|
||||
unsigned num_buffers;
|
||||
active_atomic_buffer *const abs =
|
||||
find_active_atomic_counters(ctx, prog, &num_buffers);
|
||||
unsigned atomic_counters[MESA_SHADER_TYPES] = {};
|
||||
unsigned atomic_buffers[MESA_SHADER_TYPES] = {};
|
||||
unsigned total_atomic_counters = 0;
|
||||
unsigned total_atomic_buffers = 0;
|
||||
|
||||
/* Sum the required resources. Note that this counts buffers and
|
||||
* counters referenced by several shader stages multiple times
|
||||
* against the combined limit -- That's the behavior the spec
|
||||
* requires.
|
||||
*/
|
||||
for (unsigned i = 0; i < ctx->Const.MaxAtomicBufferBindings; i++) {
|
||||
if (abs[i].size == 0)
|
||||
continue;
|
||||
|
||||
for (unsigned j = 0; j < MESA_SHADER_TYPES; ++j) {
|
||||
const unsigned n = abs[i].stage_references[j];
|
||||
|
||||
if (n) {
|
||||
atomic_counters[j] += n;
|
||||
total_atomic_counters += n;
|
||||
atomic_buffers[j]++;
|
||||
total_atomic_buffers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that they are within the supported limits. */
|
||||
for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
|
||||
if (atomic_counters[i] > max_atomic_counters[i])
|
||||
linker_error(prog, "Too many %s shader atomic counters",
|
||||
shader_names[i]);
|
||||
|
||||
if (atomic_buffers[i] > max_atomic_buffers[i])
|
||||
linker_error(prog, "Too many %s shader atomic counter buffers",
|
||||
shader_names[i]);
|
||||
}
|
||||
|
||||
if (total_atomic_counters > ctx->Const.MaxCombinedAtomicCounters)
|
||||
linker_error(prog, "Too many combined atomic counters");
|
||||
|
||||
if (total_atomic_buffers > ctx->Const.MaxCombinedAtomicBuffers)
|
||||
linker_error(prog, "Too many combined atomic buffers");
|
||||
|
||||
delete [] abs;
|
||||
}
|
|
@ -651,6 +651,14 @@ cross_validate_globals(struct gl_shader_program *prog,
|
|||
existing->explicit_binding = true;
|
||||
}
|
||||
|
||||
if (var->type->contains_atomic() &&
|
||||
var->atomic.offset != existing->atomic.offset) {
|
||||
linker_error(prog, "offset specifications for %s "
|
||||
"`%s' have differing values\n",
|
||||
mode_string(var), var->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Validate layout qualifiers for gl_FragDepth.
|
||||
*
|
||||
* From the AMD/ARB_conservative_depth specs:
|
||||
|
@ -1485,8 +1493,12 @@ update_array_sizes(struct gl_shader_program *prog)
|
|||
/* GL_ARB_uniform_buffer_object says that std140 uniforms
|
||||
* will not be eliminated. Since we always do std140, just
|
||||
* don't resize arrays in UBOs.
|
||||
*
|
||||
* Atomic counters are supposed to get deterministic
|
||||
* locations assigned based on the declaration ordering and
|
||||
* sizes, array compaction would mess that up.
|
||||
*/
|
||||
if (var->is_in_uniform_block())
|
||||
if (var->is_in_uniform_block() || var->type->contains_atomic())
|
||||
continue;
|
||||
|
||||
unsigned int size = var->max_array_access;
|
||||
|
@ -1991,6 +2003,10 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
|
|||
prog->UniformBlockStageIndex[i] = NULL;
|
||||
}
|
||||
|
||||
ralloc_free(prog->AtomicBuffers);
|
||||
prog->AtomicBuffers = NULL;
|
||||
prog->NumAtomicBuffers = 0;
|
||||
|
||||
/* Separate the shaders into groups based on their type.
|
||||
*/
|
||||
struct gl_shader **vert_shader_list;
|
||||
|
@ -2342,9 +2358,12 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
|
|||
|
||||
update_array_sizes(prog);
|
||||
link_assign_uniform_locations(prog);
|
||||
link_assign_atomic_counter_resources(ctx, prog);
|
||||
store_fragdepth_layout(prog);
|
||||
|
||||
check_resources(ctx, prog);
|
||||
link_check_atomic_counter_resources(ctx, prog);
|
||||
|
||||
if (!prog->LinkStatus)
|
||||
goto done;
|
||||
|
||||
|
|
|
@ -69,6 +69,14 @@ validate_interstage_interface_blocks(struct gl_shader_program *prog,
|
|||
const gl_shader *producer,
|
||||
const gl_shader *consumer);
|
||||
|
||||
extern void
|
||||
link_assign_atomic_counter_resources(struct gl_context *ctx,
|
||||
struct gl_shader_program *prog);
|
||||
|
||||
extern void
|
||||
link_check_atomic_counter_resources(struct gl_context *ctx,
|
||||
struct gl_shader_program *prog);
|
||||
|
||||
/**
|
||||
* Class for processing all of the leaf fields of a variable that corresponds
|
||||
* to a program resource.
|
||||
|
|
Loading…
Reference in New Issue