diff options
Diffstat (limited to 'lib/mesa/src/compiler/glsl')
40 files changed, 5442 insertions, 89 deletions
diff --git a/lib/mesa/src/compiler/glsl/gl_nir.h b/lib/mesa/src/compiler/glsl/gl_nir.h new file mode 100644 index 000000000..59d5f65e6 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2018 Timothy Arceri + * + * 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. + */ + +#ifndef GL_NIR_H +#define GL_NIR_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct nir_shader; +struct gl_shader_program; + +bool gl_nir_lower_atomics(nir_shader *shader, + const struct gl_shader_program *shader_program, + bool use_binding_as_idx); + +bool gl_nir_lower_samplers(nir_shader *shader, + const struct gl_shader_program *shader_program); +bool gl_nir_lower_samplers_as_deref(nir_shader *shader, + const struct gl_shader_program *shader_program); + +#ifdef __cplusplus +} +#endif + +#endif /* GL_NIR_H */ diff --git a/lib/mesa/src/compiler/glsl/gl_nir_link_atomics.c b/lib/mesa/src/compiler/glsl/gl_nir_link_atomics.c new file mode 100644 index 000000000..887ac1b9d --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_link_atomics.c @@ -0,0 +1,282 @@ +/* + * Copyright © 2018 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 "nir.h" +#include "linker_util.h" +#include "gl_nir_linker.h" +#include "compiler/glsl/ir_uniform.h" /* for gl_uniform_storage */ +#include "main/context.h" + +/* This file do the common link for GLSL atomic counter uniforms, using NIR, + * instead of IR as the counter-part glsl/link_uniforms.cpp + * + * Also note that this is tailored for ARB_gl_spirv needs and particularities + */ + +struct active_atomic_counter_uniform { + unsigned loc; + nir_variable *var; +}; + +struct active_atomic_buffer { + struct active_atomic_counter_uniform *uniforms; + unsigned num_uniforms; + unsigned uniform_buffer_size; + unsigned stage_counter_references[MESA_SHADER_STAGES]; + unsigned size; +}; + +static void +add_atomic_counter(const void *ctx, + struct active_atomic_buffer *buffer, + unsigned uniform_loc, + nir_variable *var) +{ + if (buffer->num_uniforms >= buffer->uniform_buffer_size) { + if (buffer->uniform_buffer_size == 0) + buffer->uniform_buffer_size = 1; + else + buffer->uniform_buffer_size *= 2; + buffer->uniforms = reralloc(ctx, + buffer->uniforms, + struct active_atomic_counter_uniform, + buffer->uniform_buffer_size); + } + + struct active_atomic_counter_uniform *uniform = + buffer->uniforms + buffer->num_uniforms; + uniform->loc = uniform_loc; + uniform->var = var; + buffer->num_uniforms++; +} + +static void +process_atomic_variable(const struct glsl_type *t, + struct gl_shader_program *prog, + unsigned *uniform_loc, + nir_variable *var, + struct active_atomic_buffer *buffers, + unsigned *num_buffers, + int *offset, + unsigned shader_stage) +{ + /* FIXME: Arrays of arrays get counted separately. For example: + * x1[3][3][2] = 9 uniforms, 18 atomic counters + * x2[3][2] = 3 uniforms, 6 atomic counters + * x3[2] = 1 uniform, 2 atomic counters + * + * However this code marks all the counters as active even when they + * might not be used. + */ + if (glsl_type_is_array(t) && + glsl_type_is_array(glsl_get_array_element(t))) { + for (unsigned i = 0; i < glsl_get_length(t); i++) { + process_atomic_variable(glsl_get_array_element(t), + prog, + uniform_loc, + var, + buffers, num_buffers, + offset, + shader_stage); + } + } else { + struct active_atomic_buffer *buf = buffers + var->data.binding; + struct gl_uniform_storage *const storage = + &prog->data->UniformStorage[*uniform_loc]; + + /* If this is the first time the buffer is used, increment + * the counter of buffers used. + */ + if (buf->size == 0) + (*num_buffers)++; + + add_atomic_counter(buffers, /* ctx */ + buf, + *uniform_loc, + var); + + /* When checking for atomic counters we should count every member in + * an array as an atomic counter reference. + */ + if (glsl_type_is_array(t)) + buf->stage_counter_references[shader_stage] += glsl_get_length(t); + else + buf->stage_counter_references[shader_stage]++; + buf->size = MAX2(buf->size, *offset + glsl_atomic_size(t)); + + storage->offset = *offset; + *offset += glsl_atomic_size(t); + + (*uniform_loc)++; + } +} + +static struct active_atomic_buffer * +find_active_atomic_counters(struct gl_context *ctx, + struct gl_shader_program *prog, + unsigned *num_buffers) +{ + struct active_atomic_buffer *buffers = + rzalloc_array(NULL, /* ctx */ + struct active_atomic_buffer, + ctx->Const.MaxAtomicBufferBindings); + *num_buffers = 0; + + for (unsigned i = 0; i < MESA_SHADER_STAGES; ++i) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (sh == NULL) + continue; + + nir_shader *nir = sh->Program->nir; + + nir_foreach_variable(var, &nir->uniforms) { + if (!glsl_contains_atomic(var->type)) + continue; + + int offset = var->data.offset; + unsigned uniform_loc = var->data.location; + + process_atomic_variable(var->type, + prog, + &uniform_loc, + var, + buffers, + num_buffers, + &offset, + i); + } + } + + return buffers; +} + +void +gl_nir_link_assign_atomic_counter_resources(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + unsigned num_buffers; + unsigned num_atomic_buffers[MESA_SHADER_STAGES] = {0}; + struct active_atomic_buffer *abs = + find_active_atomic_counters(ctx, prog, &num_buffers); + + prog->data->AtomicBuffers = + rzalloc_array(prog->data, struct gl_active_atomic_buffer, num_buffers); + prog->data->NumAtomicBuffers = num_buffers; + + unsigned buffer_idx = 0; + for (unsigned binding = 0; + binding < ctx->Const.MaxAtomicBufferBindings; + binding++) { + + /* If the binding was not used, skip. + */ + if (abs[binding].size == 0) + continue; + + struct active_atomic_buffer *ab = abs + binding; + struct gl_active_atomic_buffer *mab = + prog->data->AtomicBuffers + buffer_idx; + + /* Assign buffer-specific fields. */ + mab->Binding = binding; + mab->MinimumSize = ab->size; + mab->Uniforms = rzalloc_array(prog->data->AtomicBuffers, GLuint, + ab->num_uniforms); + mab->NumUniforms = ab->num_uniforms; + + /* Assign counter-specific fields. */ + for (unsigned j = 0; j < ab->num_uniforms; j++) { + nir_variable *var = ab->uniforms[j].var; + struct gl_uniform_storage *storage = + &prog->data->UniformStorage[ab->uniforms[j].loc]; + + mab->Uniforms[j] = ab->uniforms[j].loc; + + storage->atomic_buffer_index = buffer_idx; + storage->offset = var->data.offset; + if (glsl_type_is_array(var->type)) { + const struct glsl_type *without_array = + glsl_without_array(var->type); + storage->array_stride = glsl_atomic_size(without_array); + } else { + storage->array_stride = 0; + } + if (!glsl_type_is_matrix(var->type)) + storage->matrix_stride = 0; + } + + /* Assign stage-specific fields. */ + for (unsigned stage = 0; stage < MESA_SHADER_STAGES; ++stage) { + if (ab->stage_counter_references[stage]) { + mab->StageReferences[stage] = GL_TRUE; + num_atomic_buffers[stage]++; + } else { + mab->StageReferences[stage] = GL_FALSE; + } + } + + buffer_idx++; + } + + /* Store a list pointers to atomic buffers per stage and store the index + * to the intra-stage buffer list in uniform storage. + */ + for (unsigned stage = 0; stage < MESA_SHADER_STAGES; ++stage) { + if (prog->_LinkedShaders[stage] == NULL || + num_atomic_buffers[stage] <= 0) + continue; + + struct gl_program *gl_prog = prog->_LinkedShaders[stage]->Program; + gl_prog->info.num_abos = num_atomic_buffers[stage]; + gl_prog->sh.AtomicBuffers = + rzalloc_array(gl_prog, + struct gl_active_atomic_buffer *, + num_atomic_buffers[stage]); + + gl_prog->nir->info.num_abos = num_atomic_buffers[stage]; + + unsigned intra_stage_idx = 0; + for (unsigned i = 0; i < num_buffers; i++) { + struct gl_active_atomic_buffer *atomic_buffer = + &prog->data->AtomicBuffers[i]; + if (!atomic_buffer->StageReferences[stage]) + continue; + + gl_prog->sh.AtomicBuffers[intra_stage_idx] = atomic_buffer; + + for (unsigned u = 0; u < atomic_buffer->NumUniforms; u++) { + GLuint uniform_loc = atomic_buffer->Uniforms[u]; + struct gl_opaque_uniform_index *opaque = + prog->data->UniformStorage[uniform_loc].opaque + stage; + opaque->index = intra_stage_idx; + opaque->active = true; + } + + intra_stage_idx++; + } + } + + assert(buffer_idx == num_buffers); + + ralloc_free(abs); +} diff --git a/lib/mesa/src/compiler/glsl/gl_nir_link_uniform_initializers.c b/lib/mesa/src/compiler/glsl/gl_nir_link_uniform_initializers.c new file mode 100644 index 000000000..8eefa71c8 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_link_uniform_initializers.c @@ -0,0 +1,292 @@ +/* + * Copyright © 2018 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 "nir.h" +#include "gl_nir_linker.h" +#include "compiler/glsl/ir_uniform.h" /* for gl_uniform_storage */ +#include "main/context.h" +#include "main/mtypes.h" + +struct set_opaque_binding_closure { + struct gl_shader_program *shader_prog; + struct gl_program *prog; + const nir_variable *var; + int binding; + int location; +}; + +static void +set_opaque_binding(struct set_opaque_binding_closure *data, + const struct glsl_type *type) +{ + if (glsl_type_is_array(type) && + glsl_type_is_array(glsl_get_array_element(type))) { + const struct glsl_type *element_type = glsl_get_array_element(type); + + for (unsigned int i = 0; i < glsl_get_length(type); i++) + set_opaque_binding(data, element_type); + + return; + } + + if (data->location < 0 || + data->location >= data->prog->sh.data->NumUniformStorage) + return; + + struct gl_uniform_storage *storage = + data->prog->sh.data->UniformStorage + data->location++; + + const unsigned elements = MAX2(storage->array_elements, 1); + + for (unsigned int i = 0; i < elements; i++) + storage->storage[i].i = data->binding++; + + for (int sh = 0; sh < MESA_SHADER_STAGES; sh++) { + struct gl_linked_shader *shader = data->shader_prog->_LinkedShaders[sh]; + + if (!shader) + continue; + if (!storage->opaque[sh].active) + continue; + + if (glsl_type_is_sampler(storage->type)) { + for (unsigned i = 0; i < elements; i++) { + const unsigned index = storage->opaque[sh].index + i; + + if (storage->is_bindless) { + if (index >= shader->Program->sh.NumBindlessSamplers) + break; + shader->Program->sh.BindlessSamplers[index].unit = + storage->storage[i].i; + shader->Program->sh.BindlessSamplers[index].bound = true; + shader->Program->sh.HasBoundBindlessSampler = true; + } else { + if (index >= ARRAY_SIZE(shader->Program->SamplerUnits)) + break; + shader->Program->SamplerUnits[index] = + storage->storage[i].i; + } + } + } else if (glsl_type_is_image(type)) { + for (unsigned i = 0; i < elements; i++) { + const unsigned index = storage->opaque[sh].index + i; + + if (storage->is_bindless) { + if (index >= shader->Program->sh.NumBindlessImages) + break; + shader->Program->sh.BindlessImages[index].unit = + storage->storage[i].i; + shader->Program->sh.BindlessImages[index].bound = true; + shader->Program->sh.HasBoundBindlessImage = true; + } else { + if (index >= ARRAY_SIZE(shader->Program->sh.ImageUnits)) + break; + shader->Program->sh.ImageUnits[index] = + storage->storage[i].i; + } + } + } + } +} + +static void +copy_constant_to_storage(union gl_constant_value *storage, + const nir_constant *val, + const struct glsl_type *type, + unsigned int boolean_true) +{ + const enum glsl_base_type base_type = glsl_get_base_type(type); + const unsigned n_columns = glsl_get_matrix_columns(type); + const unsigned n_rows = glsl_get_vector_elements(type); + int i = 0; + + for (unsigned int column = 0; column < n_columns; column++) { + for (unsigned int row = 0; row < n_rows; row++) { + switch (base_type) { + case GLSL_TYPE_UINT: + storage[i].u = val->values[column].u32[row]; + break; + case GLSL_TYPE_INT: + case GLSL_TYPE_SAMPLER: + storage[i].i = val->values[column].i32[row]; + break; + case GLSL_TYPE_FLOAT: + storage[i].f = val->values[column].f32[row]; + break; + case GLSL_TYPE_DOUBLE: + case GLSL_TYPE_UINT64: + case GLSL_TYPE_INT64: + /* XXX need to check on big-endian */ + memcpy(&storage[i * 2].u, + &val->values[column].f64[row], + sizeof(double)); + break; + case GLSL_TYPE_BOOL: + storage[i].b = val->values[column].u32[row] ? boolean_true : 0; + break; + case GLSL_TYPE_ARRAY: + case GLSL_TYPE_STRUCT: + case GLSL_TYPE_IMAGE: + case GLSL_TYPE_ATOMIC_UINT: + case GLSL_TYPE_INTERFACE: + case GLSL_TYPE_VOID: + case GLSL_TYPE_SUBROUTINE: + case GLSL_TYPE_FUNCTION: + case GLSL_TYPE_ERROR: + case GLSL_TYPE_UINT16: + case GLSL_TYPE_INT16: + case GLSL_TYPE_UINT8: + case GLSL_TYPE_INT8: + case GLSL_TYPE_FLOAT16: + /* All other types should have already been filtered by other + * paths in the caller. + */ + assert(!"Should not get here."); + break; + } + i++; + } + } +} + +struct set_uniform_initializer_closure { + struct gl_shader_program *shader_prog; + struct gl_program *prog; + const nir_variable *var; + int location; + unsigned int boolean_true; +}; + +static void +set_uniform_initializer(struct set_uniform_initializer_closure *data, + const struct glsl_type *type, + const nir_constant *val) +{ + const struct glsl_type *t_without_array = glsl_without_array(type); + + if (glsl_type_is_struct(type)) { + for (unsigned int i = 0; i < glsl_get_length(type); i++) { + const struct glsl_type *field_type = glsl_get_struct_field(type, i); + set_uniform_initializer(data, field_type, val->elements[i]); + } + return; + } + + if (glsl_type_is_struct(t_without_array) || + (glsl_type_is_array(type) && + glsl_type_is_array(glsl_get_array_element(type)))) { + const struct glsl_type *element_type = glsl_get_array_element(type); + + for (unsigned int i = 0; i < glsl_get_length(type); i++) + set_uniform_initializer(data, element_type, val->elements[i]); + + return; + } + + if (data->location < 0 || + data->location >= data->prog->sh.data->NumUniformStorage) + return; + + struct gl_uniform_storage *storage = + data->prog->sh.data->UniformStorage + data->location++; + + if (glsl_type_is_array(type)) { + const struct glsl_type *element_type = glsl_get_array_element(type); + const enum glsl_base_type base_type = glsl_get_base_type(element_type); + const unsigned int elements = glsl_get_components(element_type); + unsigned int idx = 0; + unsigned dmul = glsl_base_type_is_64bit(base_type) ? 2 : 1; + + assert(glsl_get_length(type) >= storage->array_elements); + for (unsigned int i = 0; i < storage->array_elements; i++) { + copy_constant_to_storage(&storage->storage[idx], + val->elements[i], + element_type, + data->boolean_true); + + idx += elements * dmul; + } + } else { + copy_constant_to_storage(storage->storage, + val, + type, + data->boolean_true); + + if (glsl_type_is_sampler(storage->type)) { + for (int sh = 0; sh < MESA_SHADER_STAGES; sh++) { + struct gl_linked_shader *shader = + data->shader_prog->_LinkedShaders[sh]; + + if (shader && storage->opaque[sh].active) { + unsigned index = storage->opaque[sh].index; + + shader->Program->SamplerUnits[index] = storage->storage[0].i; + } + } + } + } +} + +void +gl_nir_set_uniform_initializers(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (!sh) + continue; + + nir_shader *nir = sh->Program->nir; + assert(nir); + + nir_foreach_variable(var, &nir->uniforms) { + if (var->constant_initializer) { + struct set_uniform_initializer_closure data = { + .shader_prog = prog, + .prog = sh->Program, + .var = var, + .location = var->data.location, + .boolean_true = ctx->Const.UniformBooleanTrue + }; + set_uniform_initializer(&data, + var->type, + var->constant_initializer); + } else if (var->data.explicit_binding) { + const struct glsl_type *without_array = + glsl_without_array(var->type); + + if (glsl_type_is_sampler(without_array) || + glsl_type_is_image(without_array)) { + struct set_opaque_binding_closure data = { + .shader_prog = prog, + .prog = sh->Program, + .var = var, + .binding = var->data.binding, + .location = var->data.location + }; + set_opaque_binding(&data, var->type); + } + } + } + } +} diff --git a/lib/mesa/src/compiler/glsl/gl_nir_link_uniforms.c b/lib/mesa/src/compiler/glsl/gl_nir_link_uniforms.c new file mode 100644 index 000000000..1a491dc2e --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_link_uniforms.c @@ -0,0 +1,529 @@ +/* + * Copyright © 2018 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 "nir.h" +#include "gl_nir_linker.h" +#include "compiler/glsl/ir_uniform.h" /* for gl_uniform_storage */ +#include "linker_util.h" +#include "main/context.h" +#include "main/mtypes.h" + +/* This file do the common link for GLSL uniforms, using NIR, instead of IR as + * the counter-part glsl/link_uniforms.cpp + * + * Also note that this is tailored for ARB_gl_spirv needs and particularities + * (like need to work/link without name available, explicit location for + * normal uniforms as mandatory, and so on). + */ + +#define UNMAPPED_UNIFORM_LOC ~0u + +static void +nir_setup_uniform_remap_tables(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + prog->UniformRemapTable = rzalloc_array(prog, + struct gl_uniform_storage *, + prog->NumUniformRemapTable); + union gl_constant_value *data = + rzalloc_array(prog->data, + union gl_constant_value, prog->data->NumUniformDataSlots); + if (!prog->UniformRemapTable || !data) { + linker_error(prog, "Out of memory during linking.\n"); + return; + } + prog->data->UniformDataSlots = data; + + unsigned data_pos = 0; + + /* Reserve all the explicit locations of the active uniforms. */ + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { + struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i]; + + if (prog->data->UniformStorage[i].remap_location == UNMAPPED_UNIFORM_LOC) + continue; + + /* How many new entries for this uniform? */ + const unsigned entries = MAX2(1, uniform->array_elements); + unsigned num_slots = glsl_get_component_slots(uniform->type); + + uniform->storage = &data[data_pos]; + + /* Set remap table entries point to correct gl_uniform_storage. */ + for (unsigned j = 0; j < entries; j++) { + unsigned element_loc = uniform->remap_location + j; + prog->UniformRemapTable[element_loc] = uniform; + + data_pos += num_slots; + } + } + + /* Reserve locations for rest of the uniforms. */ + link_util_update_empty_uniform_locations(prog); + + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { + struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i]; + + if (uniform->is_shader_storage) + continue; + + /* Built-in uniforms should not get any location. */ + if (uniform->builtin) + continue; + + /* Explicit ones have been set already. */ + if (uniform->remap_location != UNMAPPED_UNIFORM_LOC) + continue; + + /* How many entries for this uniform? */ + const unsigned entries = MAX2(1, uniform->array_elements); + + unsigned location = + link_util_find_empty_block(prog, &prog->data->UniformStorage[i]); + + if (location == -1) { + location = prog->NumUniformRemapTable; + + /* resize remap table to fit new entries */ + prog->UniformRemapTable = + reralloc(prog, + prog->UniformRemapTable, + struct gl_uniform_storage *, + prog->NumUniformRemapTable + entries); + prog->NumUniformRemapTable += entries; + } + + /* set the base location in remap table for the uniform */ + uniform->remap_location = location; + + unsigned num_slots = glsl_get_component_slots(uniform->type); + + uniform->storage = &data[data_pos]; + + /* Set remap table entries point to correct gl_uniform_storage. */ + for (unsigned j = 0; j < entries; j++) { + unsigned element_loc = uniform->remap_location + j; + prog->UniformRemapTable[element_loc] = uniform; + + data_pos += num_slots; + } + } +} + +static struct gl_uniform_storage * +find_previous_uniform_storage(struct gl_shader_program *prog, + int location) +{ + /* This would only work for uniform with explicit location, as all the + * uniforms without location (ie: atomic counters) would have a initial + * location equal to -1. We early return in that case. + */ + if (location == -1) + return NULL; + + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) + if (prog->data->UniformStorage[i].remap_location == location) + return &prog->data->UniformStorage[i]; + + return NULL; +} + +/* Used to build a tree representing the glsl_type so that we can have a place + * to store the next index for opaque types. Array types are expanded so that + * they have a single child which is used for all elements of the array. + * Struct types have a child for each member. The tree is walked while + * processing a uniform so that we can recognise when an opaque type is + * encountered a second time in order to reuse the same range of indices that + * was reserved the first time. That way the sampler indices can be arranged + * so that members of an array are placed sequentially even if the array is an + * array of structs containing other opaque members. + */ +struct type_tree_entry { + /* For opaque types, this will be the next index to use. If we haven’t + * encountered this member yet, it will be UINT_MAX. + */ + unsigned next_index; + unsigned array_size; + struct type_tree_entry *parent; + struct type_tree_entry *next_sibling; + struct type_tree_entry *children; +}; + +struct nir_link_uniforms_state { + /* per-whole program */ + unsigned num_hidden_uniforms; + unsigned num_values; + unsigned max_uniform_location; + unsigned next_sampler_index; + unsigned next_image_index; + + /* per-shader stage */ + unsigned num_shader_samplers; + unsigned num_shader_images; + unsigned num_shader_uniform_components; + unsigned shader_samplers_used; + unsigned shader_shadow_samplers; + + nir_variable *current_var; + + struct type_tree_entry *current_type; +}; + +static struct type_tree_entry * +build_type_tree_for_type(const struct glsl_type *type) +{ + struct type_tree_entry *entry = malloc(sizeof *entry); + + entry->array_size = 1; + entry->next_index = UINT_MAX; + entry->children = NULL; + entry->next_sibling = NULL; + entry->parent = NULL; + + if (glsl_type_is_array(type)) { + entry->array_size = glsl_get_length(type); + entry->children = build_type_tree_for_type(glsl_get_array_element(type)); + entry->children->parent = entry; + } else if (glsl_type_is_struct(type)) { + struct type_tree_entry *last = NULL; + + for (unsigned i = 0; i < glsl_get_length(type); i++) { + const struct glsl_type *field_type = glsl_get_struct_field(type, i); + struct type_tree_entry *field_entry = + build_type_tree_for_type(field_type); + + if (last == NULL) + entry->children = field_entry; + else + last->next_sibling = field_entry; + + field_entry->parent = entry; + + last = field_entry; + } + } + + return entry; +} + +static void +free_type_tree(struct type_tree_entry *entry) +{ + struct type_tree_entry *p, *next; + + for (p = entry->children; p; p = next) { + next = p->next_sibling; + free_type_tree(p); + } + + free(entry); +} + +static unsigned +get_next_index(struct nir_link_uniforms_state *state, + const struct gl_uniform_storage *uniform, + unsigned *next_index) +{ + /* If we’ve already calculated an index for this member then we can just + * offset from there. + */ + if (state->current_type->next_index == UINT_MAX) { + /* Otherwise we need to reserve enough indices for all of the arrays + * enclosing this member. + */ + + unsigned array_size = 1; + + for (const struct type_tree_entry *p = state->current_type; + p; + p = p->parent) { + array_size *= p->array_size; + } + + state->current_type->next_index = *next_index; + *next_index += array_size; + } + + unsigned index = state->current_type->next_index; + + state->current_type->next_index += MAX2(1, uniform->array_elements); + + return index; +} + + +/** + * Creates the neccessary entries in UniformStorage for the uniform. Returns + * the number of locations used or -1 on failure. + */ +static int +nir_link_uniform(struct gl_context *ctx, + struct gl_shader_program *prog, + struct gl_program *stage_program, + gl_shader_stage stage, + const struct glsl_type *type, + int location, + struct nir_link_uniforms_state *state) +{ + struct gl_uniform_storage *uniform = NULL; + + /* gl_uniform_storage can cope with one level of array, so if the type is a + * composite type or an array where each element occupies more than one + * location than we need to recursively process it. + */ + if (glsl_type_is_struct(type) || + (glsl_type_is_array(type) && + (glsl_type_is_array(glsl_get_array_element(type)) || + glsl_type_is_struct(glsl_get_array_element(type))))) { + int location_count = 0; + struct type_tree_entry *old_type = state->current_type; + + state->current_type = old_type->children; + + for (unsigned i = 0; i < glsl_get_length(type); i++) { + const struct glsl_type *field_type; + + if (glsl_type_is_struct(type)) + field_type = glsl_get_struct_field(type, i); + else + field_type = glsl_get_array_element(type); + + int entries = nir_link_uniform(ctx, prog, stage_program, stage, + field_type, location, + state); + if (entries == -1) + return -1; + + if (location != -1) + location += entries; + location_count += entries; + + if (glsl_type_is_struct(type)) + state->current_type = state->current_type->next_sibling; + } + + state->current_type = old_type; + + return location_count; + } else { + /* Create a new uniform storage entry */ + prog->data->UniformStorage = + reralloc(prog->data, + prog->data->UniformStorage, + struct gl_uniform_storage, + prog->data->NumUniformStorage + 1); + if (!prog->data->UniformStorage) { + linker_error(prog, "Out of memory during linking.\n"); + return -1; + } + + uniform = &prog->data->UniformStorage[prog->data->NumUniformStorage]; + prog->data->NumUniformStorage++; + + /* Initialize its members */ + memset(uniform, 0x00, sizeof(struct gl_uniform_storage)); + /* ARB_gl_spirv: names are considered optional debug info, so the linker + * needs to work without them, and returning them is optional. For + * simplicity we ignore names. + */ + uniform->name = NULL; + + const struct glsl_type *type_no_array = glsl_without_array(type); + if (glsl_type_is_array(type)) { + uniform->type = type_no_array; + uniform->array_elements = glsl_get_length(type); + } else { + uniform->type = type; + uniform->array_elements = 0; + } + uniform->active_shader_mask |= 1 << stage; + + if (location >= 0) { + /* Uniform has an explicit location */ + uniform->remap_location = location; + } else { + uniform->remap_location = UNMAPPED_UNIFORM_LOC; + } + + uniform->hidden = state->current_var->data.how_declared == nir_var_hidden; + if (uniform->hidden) + state->num_hidden_uniforms++; + + /* @FIXME: the initialization of the following will be done as we + * implement support for their specific features, like SSBO, atomics, + * etc. + */ + uniform->block_index = -1; + uniform->offset = -1; + uniform->matrix_stride = -1; + uniform->array_stride = -1; + uniform->row_major = false; + uniform->builtin = false; + uniform->is_shader_storage = false; + uniform->atomic_buffer_index = -1; + uniform->top_level_array_size = 0; + uniform->top_level_array_stride = 0; + uniform->is_bindless = false; + + /* The following are not for features not supported by ARB_gl_spirv */ + uniform->num_compatible_subroutines = 0; + + unsigned entries = MAX2(1, uniform->array_elements); + + if (glsl_type_is_sampler(type_no_array)) { + int sampler_index = + get_next_index(state, uniform, &state->next_sampler_index); + + state->num_shader_samplers++; + + uniform->opaque[stage].active = true; + uniform->opaque[stage].index = sampler_index; + + const unsigned shadow = glsl_sampler_type_is_shadow(type_no_array); + + for (unsigned i = sampler_index; + i < MIN2(state->next_sampler_index, MAX_SAMPLERS); + i++) { + stage_program->sh.SamplerTargets[i] = + glsl_get_sampler_target(type_no_array); + state->shader_samplers_used |= 1U << i; + state->shader_shadow_samplers |= shadow << i; + } + } else if (glsl_type_is_image(type_no_array)) { + /* @FIXME: image_index should match that of the same image + * uniform in other shaders. This means we need to match image + * uniforms by location (GLSL does it by variable name, but we + * want to avoid that). + */ + int image_index = state->next_image_index; + state->next_image_index += entries; + + state->num_shader_images++; + + uniform->opaque[stage].active = true; + uniform->opaque[stage].index = image_index; + + /* Set image access qualifiers */ + enum gl_access_qualifier image_access = + state->current_var->data.image.access; + const GLenum access = + (image_access & ACCESS_NON_WRITEABLE) ? + ((image_access & ACCESS_NON_READABLE) ? GL_NONE : + GL_READ_ONLY) : + ((image_access & ACCESS_NON_READABLE) ? GL_WRITE_ONLY : + GL_READ_WRITE); + for (unsigned i = image_index; + i < MIN2(state->next_image_index, MAX_IMAGE_UNIFORMS); + i++) { + stage_program->sh.ImageAccess[i] = access; + } + } + + unsigned values = glsl_get_component_slots(type); + state->num_shader_uniform_components += values; + state->num_values += values; + + if (state->max_uniform_location < uniform->remap_location + entries) + state->max_uniform_location = uniform->remap_location + entries; + + return MAX2(uniform->array_elements, 1); + } +} + +bool +gl_nir_link_uniforms(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + /* First free up any previous UniformStorage items */ + ralloc_free(prog->data->UniformStorage); + prog->data->UniformStorage = NULL; + prog->data->NumUniformStorage = 0; + + /* Iterate through all linked shaders */ + struct nir_link_uniforms_state state = {0,}; + + for (unsigned shader_type = 0; shader_type < MESA_SHADER_STAGES; shader_type++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[shader_type]; + if (!sh) + continue; + + nir_shader *nir = sh->Program->nir; + assert(nir); + + state.num_shader_samplers = 0; + state.num_shader_images = 0; + state.num_shader_uniform_components = 0; + state.shader_samplers_used = 0; + state.shader_shadow_samplers = 0; + + nir_foreach_variable(var, &nir->uniforms) { + struct gl_uniform_storage *uniform = NULL; + + /* Check if the uniform has been processed already for + * other stage. If so, validate they are compatible and update + * the active stage mask. + */ + uniform = find_previous_uniform_storage(prog, var->data.location); + if (uniform) { + uniform->active_shader_mask |= 1 << shader_type; + var->data.location = uniform - prog->data->UniformStorage; + + continue; + } + + int location = var->data.location; + /* From now on the variable’s location will be its uniform index */ + var->data.location = prog->data->NumUniformStorage; + + state.current_var = var; + + struct type_tree_entry *type_tree = + build_type_tree_for_type(var->type); + state.current_type = type_tree; + + int res = nir_link_uniform(ctx, prog, sh->Program, shader_type, var->type, + location, &state); + + free_type_tree(type_tree); + + if (res == -1) + return false; + } + + sh->Program->SamplersUsed = state.shader_samplers_used; + sh->shadow_samplers = state.shader_shadow_samplers; + sh->Program->info.num_textures = state.num_shader_samplers; + sh->Program->info.num_images = state.num_shader_images; + sh->num_uniform_components = state.num_shader_uniform_components; + sh->num_combined_uniform_components = sh->num_uniform_components; + } + + prog->data->NumHiddenUniforms = state.num_hidden_uniforms; + prog->NumUniformRemapTable = state.max_uniform_location; + prog->data->NumUniformDataSlots = state.num_values; + + nir_setup_uniform_remap_tables(ctx, prog); + gl_nir_set_uniform_initializers(ctx, prog); + + return true; +} diff --git a/lib/mesa/src/compiler/glsl/gl_nir_link_xfb.c b/lib/mesa/src/compiler/glsl/gl_nir_link_xfb.c new file mode 100644 index 000000000..bcef1e186 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_link_xfb.c @@ -0,0 +1,316 @@ +/* + * Copyright © 2018 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 "nir.h" +#include "gl_nir_linker.h" +#include "ir_uniform.h" /* for gl_uniform_storage */ +#include "linker_util.h" +#include "main/context.h" + +/* + * This file does the linking of GLSL transform feedback using NIR. + * + * Note: This linking pass is currently tailored for ARB_gl_spirv needs and + * particularities. + */ + +struct active_xfb_buffer { + GLuint stride; + GLuint num_varyings; +}; + +struct active_xfb_varyings { + unsigned num_varyings; + unsigned num_outputs; + unsigned buffer_size; + struct nir_variable **varyings; + struct active_xfb_buffer buffers[MAX_FEEDBACK_BUFFERS]; +}; + +static unsigned +get_num_outputs(nir_variable *var) +{ + return glsl_count_attribute_slots(var->type, + false /* is_vertex_input */); +} + +static void +add_xfb_varying(struct active_xfb_varyings *active_varyings, + nir_variable *var) +{ + if (active_varyings->num_varyings >= active_varyings->buffer_size) { + if (active_varyings->buffer_size == 0) + active_varyings->buffer_size = 1; + else + active_varyings->buffer_size *= 2; + + active_varyings->varyings = realloc(active_varyings->varyings, + sizeof(nir_variable*) * + active_varyings->buffer_size); + } + + active_varyings->varyings[active_varyings->num_varyings++] = var; + + active_varyings->num_outputs += get_num_outputs(var); +} + +static int +cmp_xfb_offset(const void *x_generic, const void *y_generic) +{ + const nir_variable *const *x = x_generic; + const nir_variable *const *y = y_generic; + + if ((*x)->data.xfb_buffer != (*y)->data.xfb_buffer) + return (*x)->data.xfb_buffer - (*y)->data.xfb_buffer; + return (*x)->data.offset - (*y)->data.offset; +} + +static void +get_active_xfb_varyings(struct gl_shader_program *prog, + struct active_xfb_varyings *active_varyings) +{ + for (unsigned i = 0; i < MESA_SHADER_STAGES; ++i) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (sh == NULL) + continue; + + nir_shader *nir = sh->Program->nir; + + nir_foreach_variable(var, &nir->outputs) { + if (var->data.explicit_xfb_buffer && + var->data.explicit_xfb_stride) { + assert(var->data.xfb_buffer < MAX_FEEDBACK_BUFFERS); + active_varyings->buffers[var->data.xfb_buffer].stride = + var->data.xfb_stride; + } + + if (!var->data.explicit_xfb_buffer || + !var->data.explicit_offset) + continue; + + active_varyings->buffers[var->data.xfb_buffer].num_varyings++; + + add_xfb_varying(active_varyings, var); + } + } + + /* The xfb_offset qualifier does not have to be used in increasing order + * however some drivers expect to receive the list of transform feedback + * declarations in order so sort it now for convenience. + */ + qsort(active_varyings->varyings, + active_varyings->num_varyings, + sizeof(*active_varyings->varyings), + cmp_xfb_offset); +} + +static unsigned +add_varying_outputs(nir_variable *var, + const struct glsl_type *type, + unsigned location_offset, + unsigned dest_offset, + struct gl_transform_feedback_output *output) +{ + unsigned num_outputs = 0; + + if (glsl_type_is_array(type) || glsl_type_is_matrix(type)) { + unsigned length = glsl_get_length(type); + const struct glsl_type *child_type = glsl_get_array_element(type); + unsigned component_slots = glsl_get_component_slots(child_type); + + for (unsigned i = 0; i < length; i++) { + unsigned child_outputs = add_varying_outputs(var, + child_type, + location_offset, + dest_offset, + output + num_outputs); + num_outputs += child_outputs; + location_offset += child_outputs; + dest_offset += component_slots; + } + } else if (glsl_type_is_struct(type)) { + unsigned length = glsl_get_length(type); + for (unsigned i = 0; i < length; i++) { + const struct glsl_type *child_type = glsl_get_struct_field(type, i); + unsigned child_outputs = add_varying_outputs(var, + child_type, + location_offset, + dest_offset, + output + num_outputs); + num_outputs += child_outputs; + location_offset += child_outputs; + dest_offset += glsl_get_component_slots(child_type); + } + } else { + unsigned location = var->data.location + location_offset; + unsigned location_frac = var->data.location_frac; + unsigned num_components = glsl_get_component_slots(type); + + while (num_components > 0) { + unsigned output_size = MIN2(num_components, 4 - location_frac); + + output->OutputRegister = location; + output->OutputBuffer = var->data.xfb_buffer; + output->NumComponents = output_size; + output->StreamId = var->data.stream; + output->DstOffset = var->data.offset / 4 + dest_offset; + output->ComponentOffset = location_frac; + + dest_offset += output_size; + num_components -= output_size; + num_outputs++; + output++; + location++; + location_frac = 0; + } + } + + return num_outputs; +} + +void +gl_nir_link_assign_xfb_resources(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + /* + * From ARB_gl_spirv spec: + * + * "- If the *Xfb* Execution Mode is set, any output variable that is at + * least partially captured: + * * must be decorated with an *XfbBuffer*, declaring the capturing buffer + * * must have at least one captured output variable in the capturing + * buffer decorated with an *XfbStride* (and all such *XfbStride* values + * for the capturing buffer must be equal) + * - If the *Xfb* Execution Mode is set, any captured output: + * * must be a non-structure decorated with *Offset* or a member of a + * structure whose type member is decorated with *Offset*" + * + * Note the "must be", meaning that explicit buffer, offset and stride are + * mandatory. So as this is intended to work with SPIR-V shaders we don't + * need to calculate the offset or the stride. + */ + + struct gl_program *xfb_prog = prog->last_vert_prog; + + if (xfb_prog == NULL) + return; + + /* free existing varyings, if any */ + for (unsigned i = 0; i < prog->TransformFeedback.NumVarying; i++) + free(prog->TransformFeedback.VaryingNames[i]); + free(prog->TransformFeedback.VaryingNames); + + struct active_xfb_varyings active_varyings = { 0 }; + + get_active_xfb_varyings(prog, &active_varyings); + + for (unsigned buf = 0; buf < MAX_FEEDBACK_BUFFERS; buf++) + prog->TransformFeedback.BufferStride[buf] = active_varyings.buffers[buf].stride; + + prog->TransformFeedback.NumVarying = active_varyings.num_varyings; + prog->TransformFeedback.VaryingNames = + malloc(sizeof(GLchar *) * active_varyings.num_varyings); + + struct gl_transform_feedback_info *linked_xfb = + rzalloc(xfb_prog, struct gl_transform_feedback_info); + xfb_prog->sh.LinkedTransformFeedback = linked_xfb; + + linked_xfb->Outputs = + rzalloc_array(xfb_prog, + struct gl_transform_feedback_output, + active_varyings.num_outputs); + linked_xfb->NumOutputs = active_varyings.num_outputs; + + linked_xfb->Varyings = + rzalloc_array(xfb_prog, + struct gl_transform_feedback_varying_info, + active_varyings.num_varyings); + linked_xfb->NumVarying = active_varyings.num_varyings; + + struct gl_transform_feedback_output *output = linked_xfb->Outputs; + for (unsigned i = 0; i < active_varyings.num_varyings; i++) { + struct nir_variable *var = active_varyings.varyings[i]; + + /* From ARB_gl_spirv spec: + * + * "19. How should the program interface query operations behave for + * program objects created from SPIR-V shaders? + * + * DISCUSSION: we previously said we didn't need reflection to work + * for SPIR-V shaders (at least for the first version), however we + * are left with specifying how it should "not work". The primary + * issue is that SPIR-V binaries are not required to have names + * associated with variables. They can be associated in debug + * information, but there is no requirement for that to be present, + * and it should not be relied upon." + * + * Options:" + * + * <skip> + * + * "RESOLVED. Pick (c), but also allow debug names to be returned + * if an implementation wants to." + * + * So names are considered optional debug info, so the linker needs to + * work without them, and returning them is optional. For simplicity at + * this point we are ignoring names + */ + prog->TransformFeedback.VaryingNames[i] = NULL; + + unsigned varying_outputs = add_varying_outputs(var, + var->type, + 0, /* location_offset */ + 0, /* dest_offset */ + output); + assert(varying_outputs == get_num_outputs(var)); + output = output + varying_outputs; + + struct gl_transform_feedback_varying_info *varying = + linked_xfb->Varyings + i; + + /* ARB_gl_spirv: see above. */ + varying->Name = NULL; + varying->Type = glsl_get_gl_type(var->type); + varying->BufferIndex = var->data.xfb_buffer; + varying->Size = glsl_get_length(var->type); + varying->Offset = var->data.offset; + } + + /* Make sure MaxTransformFeedbackBuffers is <= 32 so the bitmask for + * tracking the number of buffers doesn't overflow. + */ + unsigned buffers = 0; + assert(ctx->Const.MaxTransformFeedbackBuffers <= sizeof(buffers) * 8); + + for (unsigned buf = 0; buf < MAX_FEEDBACK_BUFFERS; buf++) { + if (active_varyings.buffers[buf].stride > 0) { + linked_xfb->Buffers[buf].Stride = active_varyings.buffers[buf].stride / 4; + linked_xfb->Buffers[buf].NumVaryings = active_varyings.buffers[buf].num_varyings; + buffers |= 1 << buf; + } + } + + linked_xfb->ActiveBuffers = buffers; + + free(active_varyings.varyings); +} diff --git a/lib/mesa/src/compiler/glsl/gl_nir_linker.c b/lib/mesa/src/compiler/glsl/gl_nir_linker.c new file mode 100644 index 000000000..547549bc4 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_linker.c @@ -0,0 +1,71 @@ +/* + * Copyright © 2018 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 "nir.h" +#include "gl_nir_linker.h" +#include "linker_util.h" +#include "main/mtypes.h" +#include "ir_uniform.h" /* for gl_uniform_storage */ + +/* This file included general link methods, using NIR, instead of IR as + * the counter-part glsl/linker.cpp + * + * Also note that this is tailored for ARB_gl_spirv needs and particularities + */ + +void +nir_build_program_resource_list(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + /* Rebuild resource list. */ + if (prog->data->ProgramResourceList) { + ralloc_free(prog->data->ProgramResourceList); + prog->data->ProgramResourceList = NULL; + prog->data->NumProgramResourceList = 0; + } + + struct set *resource_set = _mesa_set_create(NULL, + _mesa_hash_pointer, + _mesa_key_pointer_equal); + + /* Add uniforms + * + * Here, it is expected that nir_link_uniforms() has already been + * called, so that UniformStorage table is already available. + */ + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { + struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i]; + + /* Do not add uniforms internally used by Mesa. */ + if (uniform->hidden) + continue; + + if (!link_util_add_program_resource(prog, resource_set, GL_UNIFORM, uniform, + uniform->active_shader_mask)) { + return; + } + } + + + _mesa_set_destroy(resource_set, NULL); +} diff --git a/lib/mesa/src/compiler/glsl/gl_nir_linker.h b/lib/mesa/src/compiler/glsl/gl_nir_linker.h new file mode 100644 index 000000000..29ca27d3d --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_linker.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2017 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. + */ + +#ifndef GL_NIR_LINKER_H +#define GL_NIR_LINKER_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct gl_context; +struct gl_shader_program; + +bool gl_nir_link_uniforms(struct gl_context *ctx, + struct gl_shader_program *prog); + +void gl_nir_set_uniform_initializers(struct gl_context *ctx, + struct gl_shader_program *prog); + +void nir_build_program_resource_list(struct gl_context *ctx, + struct gl_shader_program *prog); + +void gl_nir_link_assign_atomic_counter_resources(struct gl_context *ctx, + struct gl_shader_program *prog); + +void gl_nir_link_assign_xfb_resources(struct gl_context *ctx, + struct gl_shader_program *prog); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* GL_NIR_LINKER_H */ diff --git a/lib/mesa/src/compiler/glsl/gl_nir_lower_atomics.c b/lib/mesa/src/compiler/glsl/gl_nir_lower_atomics.c new file mode 100644 index 000000000..36e273c45 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_lower_atomics.c @@ -0,0 +1,178 @@ +/* + * Copyright © 2014 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. + * + * Authors: + * Connor Abbott (cwabbott0@gmail.com) + * + */ + +#include "compiler/nir/nir.h" +#include "compiler/nir/nir_builder.h" +#include "gl_nir.h" +#include "ir_uniform.h" +#include "main/config.h" +#include "main/mtypes.h" +#include <assert.h> + +/* + * replace atomic counter intrinsics that use a variable with intrinsics + * that directly store the buffer index and byte offset + */ + +static bool +lower_deref_instr(nir_builder *b, nir_intrinsic_instr *instr, + const struct gl_shader_program *shader_program, + nir_shader *shader, bool use_binding_as_idx) +{ + nir_intrinsic_op op; + switch (instr->intrinsic) { + case nir_intrinsic_atomic_counter_read_deref: + op = nir_intrinsic_atomic_counter_read; + break; + + case nir_intrinsic_atomic_counter_inc_deref: + op = nir_intrinsic_atomic_counter_inc; + break; + + case nir_intrinsic_atomic_counter_pre_dec_deref: + op = nir_intrinsic_atomic_counter_pre_dec; + break; + + case nir_intrinsic_atomic_counter_post_dec_deref: + op = nir_intrinsic_atomic_counter_post_dec; + break; + + case nir_intrinsic_atomic_counter_add_deref: + op = nir_intrinsic_atomic_counter_add; + break; + + case nir_intrinsic_atomic_counter_min_deref: + op = nir_intrinsic_atomic_counter_min; + break; + + case nir_intrinsic_atomic_counter_max_deref: + op = nir_intrinsic_atomic_counter_max; + break; + + case nir_intrinsic_atomic_counter_and_deref: + op = nir_intrinsic_atomic_counter_and; + break; + + case nir_intrinsic_atomic_counter_or_deref: + op = nir_intrinsic_atomic_counter_or; + break; + + case nir_intrinsic_atomic_counter_xor_deref: + op = nir_intrinsic_atomic_counter_xor; + break; + + case nir_intrinsic_atomic_counter_exchange_deref: + op = nir_intrinsic_atomic_counter_exchange; + break; + + case nir_intrinsic_atomic_counter_comp_swap_deref: + op = nir_intrinsic_atomic_counter_comp_swap; + break; + + default: + return false; + } + + nir_deref_instr *deref = nir_src_as_deref(instr->src[0]); + nir_variable *var = nir_deref_instr_get_variable(deref); + + if (var->data.mode != nir_var_uniform && + var->data.mode != nir_var_shader_storage && + var->data.mode != nir_var_shared) + return false; /* atomics passed as function arguments can't be lowered */ + + const unsigned uniform_loc = var->data.location; + const unsigned idx = use_binding_as_idx ? var->data.binding : + shader_program->data->UniformStorage[uniform_loc].opaque[shader->info.stage].index; + + b->cursor = nir_before_instr(&instr->instr); + + nir_ssa_def *offset = nir_imm_int(b, var->data.offset); + for (nir_deref_instr *d = deref; d->deref_type != nir_deref_type_var; + d = nir_deref_instr_parent(d)) { + assert(d->deref_type == nir_deref_type_array); + assert(d->arr.index.is_ssa); + + unsigned array_stride = ATOMIC_COUNTER_SIZE; + if (glsl_type_is_array(d->type)) + array_stride *= glsl_get_aoa_size(d->type); + + offset = nir_iadd(b, offset, nir_imul(b, d->arr.index.ssa, + nir_imm_int(b, array_stride))); + } + + /* Since the first source is a deref and the first source in the lowered + * instruction is the offset, we can just swap it out and change the + * opcode. + */ + instr->intrinsic = op; + nir_instr_rewrite_src(&instr->instr, &instr->src[0], + nir_src_for_ssa(offset)); + nir_intrinsic_set_base(instr, idx); + + nir_deref_instr_remove_if_unused(deref); + + return true; +} + +bool +gl_nir_lower_atomics(nir_shader *shader, + const struct gl_shader_program *shader_program, + bool use_binding_as_idx) +{ + bool progress = false; + + nir_foreach_function(function, shader) { + if (!function->impl) + continue; + + bool impl_progress = false; + + nir_builder build; + nir_builder_init(&build, function->impl); + + nir_foreach_block(block, function->impl) { + nir_foreach_instr_safe(instr, block) { + if (instr->type != nir_instr_type_intrinsic) + continue; + + impl_progress |= lower_deref_instr(&build, + nir_instr_as_intrinsic(instr), + shader_program, shader, + use_binding_as_idx); + } + } + + if (impl_progress) { + nir_metadata_preserve(function->impl, nir_metadata_block_index | + nir_metadata_dominance); + progress = true; + } + } + + return progress; +} diff --git a/lib/mesa/src/compiler/glsl/gl_nir_lower_samplers.c b/lib/mesa/src/compiler/glsl/gl_nir_lower_samplers.c new file mode 100644 index 000000000..1ee075cfd --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_lower_samplers.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2005-2007 Brian Paul All Rights Reserved. + * Copyright (C) 2008 VMware, Inc. All Rights Reserved. + * Copyright © 2014 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 "compiler/nir/nir.h" +#include "compiler/nir/nir_builder.h" +#include "gl_nir.h" +#include "ir_uniform.h" + +#include "main/compiler.h" +#include "main/mtypes.h" + +static void +lower_tex_src_to_offset(nir_builder *b, + nir_tex_instr *instr, unsigned src_idx, + const struct gl_shader_program *shader_program) +{ + nir_ssa_def *index = NULL; + unsigned base_index = 0; + unsigned array_elements = 1; + nir_tex_src *src = &instr->src[src_idx]; + bool is_sampler = src->src_type == nir_tex_src_sampler_deref; + unsigned location = 0; + + /* We compute first the offsets */ + nir_deref_instr *deref = nir_instr_as_deref(src->src.ssa->parent_instr); + while (deref->deref_type != nir_deref_type_var) { + assert(deref->parent.is_ssa); + nir_deref_instr *parent = + nir_instr_as_deref(deref->parent.ssa->parent_instr); + + switch (deref->deref_type) { + case nir_deref_type_struct: + location += glsl_get_record_location_offset(parent->type, + deref->strct.index); + break; + + case nir_deref_type_array: { + if (nir_src_is_const(deref->arr.index) && index == NULL) { + /* We're still building a direct index */ + base_index += nir_src_as_uint(deref->arr.index) * array_elements; + } else { + if (index == NULL) { + /* We used to be direct but not anymore */ + index = nir_imm_int(b, base_index); + base_index = 0; + } + + index = nir_iadd(b, index, + nir_imul(b, nir_imm_int(b, array_elements), + nir_ssa_for_src(b, deref->arr.index, 1))); + } + + array_elements *= glsl_get_length(parent->type); + break; + } + + default: + unreachable("Invalid sampler deref type"); + } + + deref = parent; + } + + if (index) + index = nir_umin(b, index, nir_imm_int(b, array_elements - 1)); + + /* We hit the deref_var. This is the end of the line */ + assert(deref->deref_type == nir_deref_type_var); + + location += deref->var->data.location; + + gl_shader_stage stage = b->shader->info.stage; + assert(location < shader_program->data->NumUniformStorage && + shader_program->data->UniformStorage[location].opaque[stage].active); + + base_index += + shader_program->data->UniformStorage[location].opaque[stage].index; + + /* We have the offsets, we apply them, rewriting the source or removing + * instr if needed + */ + if (index) { + nir_instr_rewrite_src(&instr->instr, &src->src, + nir_src_for_ssa(index)); + + src->src_type = is_sampler ? + nir_tex_src_sampler_offset : + nir_tex_src_texture_offset; + + instr->texture_array_size = array_elements; + } else { + nir_tex_instr_remove_src(instr, src_idx); + } + + if (is_sampler) { + instr->sampler_index = base_index; + } else { + instr->texture_index = base_index; + instr->texture_array_size = array_elements; + } +} + +static bool +lower_sampler(nir_builder *b, nir_tex_instr *instr, + const struct gl_shader_program *shader_program) +{ + int texture_idx = + nir_tex_instr_src_index(instr, nir_tex_src_texture_deref); + + if (texture_idx >= 0) { + b->cursor = nir_before_instr(&instr->instr); + + lower_tex_src_to_offset(b, instr, texture_idx, + shader_program); + } + + int sampler_idx = + nir_tex_instr_src_index(instr, nir_tex_src_sampler_deref); + + if (sampler_idx >= 0) { + lower_tex_src_to_offset(b, instr, sampler_idx, + shader_program); + } + + if (texture_idx < 0 && sampler_idx < 0) + return false; + + return true; +} + +static bool +lower_impl(nir_function_impl *impl, + const struct gl_shader_program *shader_program) +{ + nir_builder b; + nir_builder_init(&b, impl); + bool progress = false; + + nir_foreach_block(block, impl) { + nir_foreach_instr(instr, block) { + if (instr->type == nir_instr_type_tex) + progress |= lower_sampler(&b, nir_instr_as_tex(instr), + shader_program); + } + } + + return progress; +} + +bool +gl_nir_lower_samplers(nir_shader *shader, + const struct gl_shader_program *shader_program) +{ + bool progress = false; + + nir_foreach_function(function, shader) { + if (function->impl) + progress |= lower_impl(function->impl, shader_program); + } + + return progress; +} diff --git a/lib/mesa/src/compiler/glsl/gl_nir_lower_samplers_as_deref.c b/lib/mesa/src/compiler/glsl/gl_nir_lower_samplers_as_deref.c new file mode 100644 index 000000000..9ff5708f5 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/gl_nir_lower_samplers_as_deref.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2005-2007 Brian Paul All Rights Reserved. + * Copyright (C) 2008 VMware, Inc. All Rights Reserved. + * Copyright © 2014 Intel Corporation + * Copyright © 2017 Advanced Micro Devices, Inc. + * + * 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. + */ + +/** + * \file + * + * Lower sampler and image references of (non-bindless) uniforms by removing + * struct dereferences, and synthesizing new uniform variables without structs + * if required. + * + * This will allow backends to have a simple, uniform treatment of bindless and + * non-bindless samplers and images. + * + * Example: + * + * struct S { + * sampler2D tex[2]; + * sampler2D other; + * }; + * uniform S s[2]; + * + * tmp = texture(s[n].tex[m], coord); + * + * Becomes: + * + * decl_var uniform INTERP_MODE_NONE sampler2D[2][2] lower@s.tex (...) + * + * vec1 32 ssa_idx = $(2 * n + m) + * vec4 32 ssa_out = tex ssa_coord (coord), lower@s.tex[n][m] (texture), lower@s.tex[n][m] (sampler) + * + * and lower@s.tex has var->data.binding set to the base index as defined by + * the opaque uniform mapping. + */ + +#include "compiler/nir/nir.h" +#include "compiler/nir/nir_builder.h" +#include "compiler/nir/nir_deref.h" +#include "gl_nir.h" +#include "ir_uniform.h" + +#include "main/compiler.h" +#include "main/mtypes.h" + +struct lower_samplers_as_deref_state { + nir_shader *shader; + const struct gl_shader_program *shader_program; + struct hash_table *remap_table; +}; + +/* Prepare for removing struct derefs. This pre-pass generates the name + * of the lowered deref, and calculates the lowered type and location. + * After that, once looking up (or creating if needed) the lowered var, + * constructing the new chain of deref instructions is a simple loop + * that skips the struct deref's + * + * path: appended to as we descend down the chain of deref instrs + * and remove struct derefs + * location: increased as we descend down and remove struct derefs + * type: updated as we recurse back up the chain of deref instrs + * with the resulting type after removing struct derefs + */ +static void +remove_struct_derefs_prep(nir_deref_instr **p, char **name, + unsigned *location, const struct glsl_type **type) +{ + nir_deref_instr *cur = p[0], *next = p[1]; + + if (!next) { + *type = cur->type; + return; + } + + switch (next->deref_type) { + case nir_deref_type_array: { + unsigned length = glsl_get_length(cur->type); + + remove_struct_derefs_prep(&p[1], name, location, type); + + *type = glsl_get_array_instance(*type, length); + break; + } + + case nir_deref_type_struct: { + *location += glsl_get_record_location_offset(cur->type, next->strct.index); + ralloc_asprintf_append(name, ".%s", + glsl_get_struct_elem_name(cur->type, next->strct.index)); + + remove_struct_derefs_prep(&p[1], name, location, type); + + /* skip over the struct type: */ + *type = next->type; + break; + } + + default: + unreachable("Invalid deref type"); + break; + } +} + +static nir_deref_instr * +lower_deref(nir_builder *b, struct lower_samplers_as_deref_state *state, + nir_deref_instr *deref) +{ + nir_variable *var = nir_deref_instr_get_variable(deref); + gl_shader_stage stage = state->shader->info.stage; + + if (var->data.bindless || var->data.mode != nir_var_uniform) + return NULL; + + nir_deref_path path; + nir_deref_path_init(&path, deref, state->remap_table); + assert(path.path[0]->deref_type == nir_deref_type_var); + + char *name = ralloc_asprintf(state->remap_table, "lower@%s", var->name); + unsigned location = var->data.location; + const struct glsl_type *type = NULL; + unsigned binding; + + /* + * We end up needing to do this in two passes, in order to generate + * the name of the lowered var (and detecting whether there even are + * any struct deref's), and then the second pass to construct the + * actual deref instructions after looking up / generating a new + * nir_variable (since we need to construct the deref_var first) + */ + + remove_struct_derefs_prep(path.path, &name, &location, &type); + + assert(location < state->shader_program->data->NumUniformStorage && + state->shader_program->data->UniformStorage[location].opaque[stage].active); + + binding = state->shader_program->data->UniformStorage[location].opaque[stage].index; + + if (var->type == type) { + /* Fast path: We did not encounter any struct derefs. */ + var->data.binding = binding; + return deref; + } + + uint32_t hash = _mesa_key_hash_string(name); + struct hash_entry *h = + _mesa_hash_table_search_pre_hashed(state->remap_table, hash, name); + + if (h) { + var = (nir_variable *)h->data; + } else { + var = nir_variable_create(state->shader, nir_var_uniform, type, name); + var->data.binding = binding; + _mesa_hash_table_insert_pre_hashed(state->remap_table, hash, name, var); + } + + /* construct a new deref based on lowered var (skipping the struct deref's + * from the original deref: + */ + nir_deref_instr *new_deref = nir_build_deref_var(b, var); + for (nir_deref_instr **p = &path.path[1]; *p; p++) { + if ((*p)->deref_type == nir_deref_type_struct) + continue; + + assert((*p)->deref_type == nir_deref_type_array); + + new_deref = nir_build_deref_array(b, new_deref, + nir_ssa_for_src(b, (*p)->arr.index, 1)); + } + + return new_deref; +} + +static bool +lower_sampler(nir_tex_instr *instr, struct lower_samplers_as_deref_state *state, + nir_builder *b) +{ + int texture_idx = + nir_tex_instr_src_index(instr, nir_tex_src_texture_deref); + int sampler_idx = + nir_tex_instr_src_index(instr, nir_tex_src_sampler_deref); + + if (texture_idx < 0) + return false; + + assert(texture_idx >= 0 && sampler_idx >= 0); + assert(instr->src[texture_idx].src.is_ssa); + assert(instr->src[sampler_idx].src.is_ssa); + assert(instr->src[texture_idx].src.ssa == instr->src[sampler_idx].src.ssa); + + b->cursor = nir_before_instr(&instr->instr); + + nir_deref_instr *texture_deref = + lower_deref(b, state, nir_src_as_deref(instr->src[texture_idx].src)); + /* don't lower bindless: */ + if (!texture_deref) + return false; + nir_instr_rewrite_src(&instr->instr, &instr->src[texture_idx].src, + nir_src_for_ssa(&texture_deref->dest.ssa)); + + nir_deref_instr *sampler_deref = + lower_deref(b, state, nir_src_as_deref(instr->src[sampler_idx].src)); + nir_instr_rewrite_src(&instr->instr, &instr->src[sampler_idx].src, + nir_src_for_ssa(&sampler_deref->dest.ssa)); + + return true; +} + +static bool +lower_intrinsic(nir_intrinsic_instr *instr, + struct lower_samplers_as_deref_state *state, + nir_builder *b) +{ + if (instr->intrinsic == nir_intrinsic_image_deref_load || + instr->intrinsic == nir_intrinsic_image_deref_store || + instr->intrinsic == nir_intrinsic_image_deref_atomic_add || + instr->intrinsic == nir_intrinsic_image_deref_atomic_min || + instr->intrinsic == nir_intrinsic_image_deref_atomic_max || + instr->intrinsic == nir_intrinsic_image_deref_atomic_and || + instr->intrinsic == nir_intrinsic_image_deref_atomic_or || + instr->intrinsic == nir_intrinsic_image_deref_atomic_xor || + instr->intrinsic == nir_intrinsic_image_deref_atomic_exchange || + instr->intrinsic == nir_intrinsic_image_deref_atomic_comp_swap || + instr->intrinsic == nir_intrinsic_image_deref_atomic_fadd || + instr->intrinsic == nir_intrinsic_image_deref_size) { + + b->cursor = nir_before_instr(&instr->instr); + nir_deref_instr *deref = + lower_deref(b, state, nir_src_as_deref(instr->src[0])); + /* don't lower bindless: */ + if (!deref) + return false; + nir_instr_rewrite_src(&instr->instr, &instr->src[0], + nir_src_for_ssa(&deref->dest.ssa)); + return true; + } + + return false; +} + +static bool +lower_impl(nir_function_impl *impl, struct lower_samplers_as_deref_state *state) +{ + nir_builder b; + nir_builder_init(&b, impl); + bool progress = false; + + nir_foreach_block(block, impl) { + nir_foreach_instr(instr, block) { + if (instr->type == nir_instr_type_tex) + progress |= lower_sampler(nir_instr_as_tex(instr), state, &b); + else if (instr->type == nir_instr_type_intrinsic) + progress |= lower_intrinsic(nir_instr_as_intrinsic(instr), state, &b); + } + } + + return progress; +} + +bool +gl_nir_lower_samplers_as_deref(nir_shader *shader, + const struct gl_shader_program *shader_program) +{ + bool progress = false; + struct lower_samplers_as_deref_state state; + + state.shader = shader; + state.shader_program = shader_program; + state.remap_table = _mesa_hash_table_create(NULL, _mesa_key_hash_string, + _mesa_key_string_equal); + + nir_foreach_function(function, shader) { + if (function->impl) + progress |= lower_impl(function->impl, &state); + } + + /* keys are freed automatically by ralloc */ + _mesa_hash_table_destroy(state.remap_table, NULL); + + if (progress) + nir_remove_dead_derefs(shader); + + return progress; +} diff --git a/lib/mesa/src/compiler/glsl/glcpp/glcpp-parse.h b/lib/mesa/src/compiler/glsl/glcpp/glcpp-parse.h index cff4146c5..38fc43d7c 100644 --- a/lib/mesa/src/compiler/glsl/glcpp/glcpp-parse.h +++ b/lib/mesa/src/compiler/glsl/glcpp/glcpp-parse.h @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 3.0.4. */ +/* A Bison parser, made by GNU Bison 3.1. */ /* Bison interface for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/lib/mesa/src/compiler/glsl/glcpp/meson.build b/lib/mesa/src/compiler/glsl/glcpp/meson.build new file mode 100644 index 000000000..a03d589b3 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/glcpp/meson.build @@ -0,0 +1,75 @@ +# Copyright © 2017 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 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. + +glcpp_parse = custom_target( + 'glcpp-parse.[ch]', + input : 'glcpp-parse.y', + output : ['glcpp-parse.c', 'glcpp-parse.h'], + command : [ + prog_bison, '-o', '@OUTPUT0@', '-p', 'glcpp_parser_', + '--defines=@OUTPUT1@', '@INPUT@', + ], +) + +glcpp_lex = custom_target( + 'glcpp-lex.c', + input : 'glcpp-lex.l', + output : 'glcpp-lex.c', + command : [prog_flex, '-o', '@OUTPUT@', '@INPUT@'], +) + +libglcpp = static_library( + 'glcpp', + [glcpp_lex, glcpp_parse, files('glcpp.h', 'pp.c')], + link_with : libmesa_util, + include_directories : [inc_common], + c_args : [c_vis_args, no_override_init_args, c_msvc_compat_args], + cpp_args : [cpp_vis_args, cpp_msvc_compat_args], + build_by_default : false, +) + +glcpp = executable( + 'glcpp', + 'glcpp.c', + dependencies : [dep_m], + include_directories : [inc_common], + link_with : [libglcpp, libglsl_util], + c_args : [c_vis_args, no_override_init_args, c_msvc_compat_args], + build_by_default : false, +) + +if with_any_opengl and with_tests + modes = ['unix', 'windows', 'oldmac', 'bizarro'] + if dep_valgrind.found() + modes += ['valgrind'] + endif + + foreach m : modes + test( + 'glcpp test (@0@)'.format(m), + prog_python, + args : [ + join_paths(meson.current_source_dir(), 'tests/glcpp_test.py'), + glcpp, join_paths(meson.current_source_dir(), 'tests'), + '--@0@'.format(m), + ], + ) + endforeach +endif diff --git a/lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c b/lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c index ae7ea09f6..2b084e096 100644 --- a/lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c +++ b/lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c @@ -2,6 +2,7 @@ #define TWO ( 1+1 ) #define FOUR (2 + 2) #define SIX (3 + 3) +#define EIGHT (8 + 8) /* Redefinitions with whitespace in same places, but different amounts, (so no * error). */ @@ -9,6 +10,9 @@ #define FOUR (2 + 2) #define SIX (3/*comment is whitespace*/+ /* collapsed */ /* to */ /* one */ /* space */ 3) +/* Trailing whitespace (no error) */ +#define EIGHT (8 + 8) + /* Redefinitions with whitespace in different places. Each of these should * trigger an error. */ #define TWO (1 + 1) diff --git a/lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c.expected b/lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c.expected index 602bdef94..766849e34 100644 --- a/lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c.expected +++ b/lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c.expected @@ -1,14 +1,15 @@ -0:14(9): preprocessor error: Redefinition of macro TWO +0:18(9): preprocessor error: Redefinition of macro TWO -0:15(9): preprocessor error: Redefinition of macro FOUR +0:19(9): preprocessor error: Redefinition of macro FOUR -0:16(9): preprocessor error: Redefinition of macro SIX +0:20(9): preprocessor error: Redefinition of macro SIX + @@ -18,5 +19,8 @@ + + + diff --git a/lib/mesa/src/compiler/glsl/glcpp/tests/149-hex-const-uppercase-prefix.c b/lib/mesa/src/compiler/glsl/glcpp/tests/149-hex-const-uppercase-prefix.c new file mode 100644 index 000000000..1be9b28eb --- /dev/null +++ b/lib/mesa/src/compiler/glsl/glcpp/tests/149-hex-const-uppercase-prefix.c @@ -0,0 +1,5 @@ +#if 0x1234abcd == 0X1234abcd +success +#else +failure +#endif diff --git a/lib/mesa/src/compiler/glsl/glcpp/tests/149-hex-const-uppercase-prefix.c.expected b/lib/mesa/src/compiler/glsl/glcpp/tests/149-hex-const-uppercase-prefix.c.expected new file mode 100644 index 000000000..4cf250f6b --- /dev/null +++ b/lib/mesa/src/compiler/glsl/glcpp/tests/149-hex-const-uppercase-prefix.c.expected @@ -0,0 +1,5 @@ + +success + + + diff --git a/lib/mesa/src/compiler/glsl/glcpp/tests/glcpp_test.py b/lib/mesa/src/compiler/glsl/glcpp/tests/glcpp_test.py new file mode 100644 index 000000000..b02430052 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/glcpp/tests/glcpp_test.py @@ -0,0 +1,235 @@ +# encoding=utf-8 +# Copyright © 2018 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 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. + +"""Run glcpp tests with various line endings.""" + +from __future__ import print_function +import argparse +import difflib +import io +import os +import subprocess +import sys +import tempfile + + +def arg_parser(): + parser = argparse.ArgumentParser() + parser.add_argument('glcpp', help='Path to the he glcpp binary.') + parser.add_argument('testdir', help='Path to tests and expected output.') + parser.add_argument('--unix', action='store_true', help='Run tests for Unix style newlines') + parser.add_argument('--windows', action='store_true', help='Run tests for Windows/Dos style newlines') + parser.add_argument('--oldmac', action='store_true', help='Run tests for Old Mac (pre-OSX) style newlines') + parser.add_argument('--bizarro', action='store_true', help='Run tests for Bizarro world style newlines') + parser.add_argument('--valgrind', action='store_true', help='Run with valgrind for errors') + return parser.parse_args() + + +def parse_test_file(filename, nl_format): + """Check for any special arguments and return them as a list.""" + # Disable "universal newlines" mode; we can't directly use `nl_format` as + # the `newline` argument, because the "bizarro" test uses something Python + # considers invalid. + with io.open(filename, newline='') as f: + for l in f.read().split(nl_format): + if 'glcpp-args:' in l: + return l.split('glcpp-args:')[1].strip().split() + return [] + + +def test_output(glcpp, filename, expfile, nl_format='\n'): + """Test that the output of glcpp is what we expect.""" + extra_args = parse_test_file(filename, nl_format) + + with open(filename, 'rb') as f: + proc = subprocess.Popen( + [glcpp] + extra_args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE) + actual, _ = proc.communicate(f.read()) + actual = actual.decode('utf-8') + + with open(expfile, 'r') as f: + expected = f.read() + + if actual == expected: + return (True, []) + return (False, difflib.unified_diff(actual.splitlines(), expected.splitlines())) + + +def _valgrind(glcpp, filename): + """Run valgrind and report any warnings.""" + extra_args = parse_test_file(filename, nl_format='\n') + + try: + fd, tmpfile = tempfile.mkstemp() + os.close(fd) + with open(filename, 'rb') as f: + proc = subprocess.Popen( + ['valgrind', '--error-exitcode=31', '--log-file', tmpfile, glcpp] + extra_args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE) + proc.communicate(f.read()) + if proc.returncode != 31: + return (True, []) + with open(tmpfile, 'rb') as f: + contents = f.read() + return (False, contents) + finally: + os.unlink(tmpfile) + + +def test_unix(args): + """Test files with unix style (\n) new lines.""" + total = 0 + passed = 0 + + print('============= Testing for Correctness (Unix) =============') + for filename in os.listdir(args.testdir): + if not filename.endswith('.c'): + continue + + print( '{}:'.format(os.path.splitext(filename)[0]), end=' ') + total += 1 + + testfile = os.path.join(args.testdir, filename) + valid, diff = test_output(args.glcpp, testfile, testfile + '.expected') + if valid: + passed += 1 + print('PASS') + else: + print('FAIL') + for l in diff: + print(l, file=sys.stderr) + + if not total: + raise Exception('Could not find any tests.') + + print('{}/{}'.format(passed, total), 'tests returned correct results') + return total == passed + + +def _replace_test(args, replace): + """Test files with non-unix style line endings. Print your own header.""" + total = 0 + passed = 0 + + for filename in os.listdir(args.testdir): + if not filename.endswith('.c'): + continue + + print( '{}:'.format(os.path.splitext(filename)[0]), end=' ') + total += 1 + testfile = os.path.join(args.testdir, filename) + try: + fd, tmpfile = tempfile.mkstemp() + os.close(fd) + with io.open(testfile, 'rt') as f: + contents = f.read() + with io.open(tmpfile, 'wt') as f: + f.write(contents.replace('\n', replace)) + valid, diff = test_output( + args.glcpp, tmpfile, testfile + '.expected', nl_format=replace) + finally: + os.unlink(tmpfile) + + if valid: + passed += 1 + print('PASS') + else: + print('FAIL') + for l in diff: + print(l, file=sys.stderr) + + if not total: + raise Exception('Could not find any tests.') + + print('{}/{}'.format(passed, total), 'tests returned correct results') + return total == passed + + +def test_windows(args): + """Test files with windows/dos style (\r\n) new lines.""" + print('============= Testing for Correctness (Windows) =============') + return _replace_test(args, '\r\n') + + +def test_oldmac(args): + """Test files with Old Mac style (\r) new lines.""" + print('============= Testing for Correctness (Old Mac) =============') + return _replace_test(args, '\r') + + +def test_bizarro(args): + """Test files with Bizarro world style (\n\r) new lines.""" + # This is allowed by the spec, but why? + print('============= Testing for Correctness (Bizarro) =============') + return _replace_test(args, '\n\r') + + +def test_valgrind(args): + total = 0 + passed = 0 + + print('============= Testing for Valgrind Warnings =============') + for filename in os.listdir(args.testdir): + if not filename.endswith('.c'): + continue + + print( '{}:'.format(os.path.splitext(filename)[0]), end=' ') + total += 1 + valid, log = _valgrind(args.glcpp, os.path.join(args.testdir, filename)) + if valid: + passed += 1 + print('PASS') + else: + print('FAIL') + print(log, file=sys.stderr) + + if not total: + raise Exception('Could not find any tests.') + + print('{}/{}'.format(passed, total), 'tests returned correct results') + return total == passed + + +def main(): + args = arg_parser() + + success = True + if args.unix: + success = success and test_unix(args) + if args.windows: + success = success and test_windows(args) + if args.oldmac: + success = success and test_oldmac(args) + if args.bizarro: + success = success and test_bizarro(args) + if args.valgrind: + success = success and test_valgrind(args) + + exit(0 if success else 1) + + +if __name__ == '__main__': + main() diff --git a/lib/mesa/src/compiler/glsl/ir_function_detect_recursion.cpp b/lib/mesa/src/compiler/glsl/ir_function_detect_recursion.cpp index 38e4357ef..5b05274d0 100644 --- a/lib/mesa/src/compiler/glsl/ir_function_detect_recursion.cpp +++ b/lib/mesa/src/compiler/glsl/ir_function_detect_recursion.cpp @@ -120,7 +120,6 @@ * * \author Ian Romanick <ian.d.romanick@intel.com> */ -#include "main/core.h" #include "ir.h" #include "glsl_parser_extras.h" #include "linker.h" diff --git a/lib/mesa/src/compiler/glsl/link_functions.cpp b/lib/mesa/src/compiler/glsl/link_functions.cpp index 56d2933ff..e73a72c86 100644 --- a/lib/mesa/src/compiler/glsl/link_functions.cpp +++ b/lib/mesa/src/compiler/glsl/link_functions.cpp @@ -21,7 +21,6 @@ * DEALINGS IN THE SOFTWARE. */ -#include "main/core.h" #include "glsl_symbol_table.h" #include "glsl_parser_extras.h" #include "ir.h" @@ -29,6 +28,7 @@ #include "util/set.h" #include "util/hash_table.h" #include "linker.h" +#include "main/mtypes.h" static ir_function_signature * find_matching_signature(const char *name, const exec_list *actual_parameters, diff --git a/lib/mesa/src/compiler/glsl/linker_util.cpp b/lib/mesa/src/compiler/glsl/linker_util.cpp new file mode 100644 index 000000000..d2724c239 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/linker_util.cpp @@ -0,0 +1,119 @@ +/* + * Copyright © 2018 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 "main/mtypes.h" +#include "linker_util.h" +#include "util/set.h" +#include "ir_uniform.h" /* for gl_uniform_storage */ + +/* Utility methods shared between the GLSL IR and the NIR */ + +bool +link_util_add_program_resource(struct gl_shader_program *prog, + struct set *resource_set, + GLenum type, const void *data, uint8_t stages) +{ + assert(data); + + /* If resource already exists, do not add it again. */ + if (_mesa_set_search(resource_set, data)) + return true; + + prog->data->ProgramResourceList = + reralloc(prog->data, + prog->data->ProgramResourceList, + gl_program_resource, + prog->data->NumProgramResourceList + 1); + + if (!prog->data->ProgramResourceList) { + linker_error(prog, "Out of memory during linking.\n"); + return false; + } + + struct gl_program_resource *res = + &prog->data->ProgramResourceList[prog->data->NumProgramResourceList]; + + res->Type = type; + res->Data = data; + res->StageReferences = stages; + + prog->data->NumProgramResourceList++; + + _mesa_set_add(resource_set, data); + + return true; +} + +/** + * Search through the list of empty blocks to find one that fits the current + * uniform. + */ +int +link_util_find_empty_block(struct gl_shader_program *prog, + struct gl_uniform_storage *uniform) +{ + const unsigned entries = MAX2(1, uniform->array_elements); + + foreach_list_typed(struct empty_uniform_block, block, link, + &prog->EmptyUniformLocations) { + /* Found a block with enough slots to fit the uniform */ + if (block->slots == entries) { + unsigned start = block->start; + exec_node_remove(&block->link); + ralloc_free(block); + + return start; + /* Found a block with more slots than needed. It can still be used. */ + } else if (block->slots > entries) { + unsigned start = block->start; + block->start += entries; + block->slots -= entries; + + return start; + } + } + + return -1; +} + +void +link_util_update_empty_uniform_locations(struct gl_shader_program *prog) +{ + struct empty_uniform_block *current_block = NULL; + + for (unsigned i = 0; i < prog->NumUniformRemapTable; i++) { + /* We found empty space in UniformRemapTable. */ + if (prog->UniformRemapTable[i] == NULL) { + /* We've found the beginning of a new continous block of empty slots */ + if (!current_block || current_block->start + current_block->slots != i) { + current_block = rzalloc(prog, struct empty_uniform_block); + current_block->start = i; + exec_list_push_tail(&prog->EmptyUniformLocations, + ¤t_block->link); + } + + /* The current block continues, so we simply increment its slots */ + current_block->slots++; + } + } +} diff --git a/lib/mesa/src/compiler/glsl/linker_util.h b/lib/mesa/src/compiler/glsl/linker_util.h new file mode 100644 index 000000000..1c3674f35 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/linker_util.h @@ -0,0 +1,69 @@ +/* + * Copyright © 2018 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. + */ + +#ifndef GLSL_LINKER_UTIL_H +#define GLSL_LINKER_UTIL_H + +struct gl_shader_program; +struct gl_uniform_storage; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Sometimes there are empty slots left over in UniformRemapTable after we + * allocate slots to explicit locations. This struct represents a single + * continouous block of empty slots in UniformRemapTable. + */ +struct empty_uniform_block { + struct exec_node link; + /* The start location of the block */ + unsigned start; + /* The number of slots in the block */ + unsigned slots; +}; + +void +linker_error(struct gl_shader_program *prog, const char *fmt, ...); + +void +linker_warning(struct gl_shader_program *prog, const char *fmt, ...); + +bool +link_util_add_program_resource(struct gl_shader_program *prog, + struct set *resource_set, + GLenum type, const void *data, uint8_t stages); + +int +link_util_find_empty_block(struct gl_shader_program *prog, + struct gl_uniform_storage *uniform); + +void +link_util_update_empty_uniform_locations(struct gl_shader_program *prog); + +#ifdef __cplusplus +} +#endif + +#endif /* GLSL_LINKER_UTIL_H */ diff --git a/lib/mesa/src/compiler/glsl/loop_analysis.cpp b/lib/mesa/src/compiler/glsl/loop_analysis.cpp index 2979e0943..0fb6e9feb 100644 --- a/lib/mesa/src/compiler/glsl/loop_analysis.cpp +++ b/lib/mesa/src/compiler/glsl/loop_analysis.cpp @@ -87,7 +87,8 @@ find_initial_value(ir_loop *loop, ir_variable *var) static int calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment, - enum ir_expression_operation op, bool continue_from_then) + enum ir_expression_operation op, bool continue_from_then, + bool swap_compare_operands) { if (from == NULL || to == NULL || increment == NULL) return -1; @@ -154,8 +155,9 @@ calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment, ir_expression *const add = new(mem_ctx) ir_expression(ir_binop_add, mul->type, mul, from); - ir_expression *cmp = - new(mem_ctx) ir_expression(op, glsl_type::bool_type, add, to); + ir_expression *cmp = swap_compare_operands + ? new(mem_ctx) ir_expression(op, glsl_type::bool_type, to, add) + : new(mem_ctx) ir_expression(op, glsl_type::bool_type, add, to); if (continue_from_then) cmp = new(mem_ctx) ir_expression(ir_unop_logic_not, cmp); @@ -582,8 +584,6 @@ loop_analysis::visit_leave(ir_loop *ir) switch (cond->operation) { case ir_binop_less: - case ir_binop_greater: - case ir_binop_lequal: case ir_binop_gequal: { /* The expressions that we care about will either be of the form * 'counter < limit' or 'limit < counter'. Figure out which is @@ -592,18 +592,12 @@ loop_analysis::visit_leave(ir_loop *ir) ir_rvalue *counter = cond->operands[0]->as_dereference_variable(); ir_constant *limit = cond->operands[1]->as_constant(); enum ir_expression_operation cmp = cond->operation; + bool swap_compare_operands = false; if (limit == NULL) { counter = cond->operands[1]->as_dereference_variable(); limit = cond->operands[0]->as_constant(); - - switch (cmp) { - case ir_binop_less: cmp = ir_binop_greater; break; - case ir_binop_greater: cmp = ir_binop_less; break; - case ir_binop_lequal: cmp = ir_binop_gequal; break; - case ir_binop_gequal: cmp = ir_binop_lequal; break; - default: assert(!"Should not get here."); - } + swap_compare_operands = true; } if ((counter == NULL) || (limit == NULL)) @@ -616,7 +610,8 @@ loop_analysis::visit_leave(ir_loop *ir) loop_variable *lv = ls->get(var); if (lv != NULL && lv->is_induction_var()) { t->iterations = calculate_iterations(init, limit, lv->increment, - cmp, t->continue_from_then); + cmp, t->continue_from_then, + swap_compare_operands); if (incremented_before_terminator(ir, var, t->ir)) { t->iterations--; diff --git a/lib/mesa/src/compiler/glsl/lower_shared_reference.cpp b/lib/mesa/src/compiler/glsl/lower_shared_reference.cpp index a1b3f7df4..5954ccce4 100644 --- a/lib/mesa/src/compiler/glsl/lower_shared_reference.cpp +++ b/lib/mesa/src/compiler/glsl/lower_shared_reference.cpp @@ -37,6 +37,7 @@ #include "main/macros.h" #include "util/list.h" #include "glsl_parser_extras.h" +#include "main/mtypes.h" using namespace ir_builder; @@ -137,13 +138,13 @@ lower_shared_reference_visitor::handle_rvalue(ir_rvalue **rvalue) ir_rvalue *offset = NULL; unsigned const_offset = get_shared_offset(var); bool row_major; - int matrix_columns; + const glsl_type *matrix_type; assert(var->get_interface_type() == NULL); const enum glsl_interface_packing packing = GLSL_INTERFACE_PACKING_STD430; setup_buffer_access(mem_ctx, deref, &offset, &const_offset, - &row_major, &matrix_columns, NULL, packing); + &row_major, &matrix_type, NULL, packing); /* Now that we've calculated the offset to the start of the * dereference, walk over the type and emit loads into a temporary. @@ -163,7 +164,7 @@ lower_shared_reference_visitor::handle_rvalue(ir_rvalue **rvalue) deref = new(mem_ctx) ir_dereference_variable(load_var); emit_access(mem_ctx, false, deref, load_offset, const_offset, row_major, - matrix_columns, packing, 0); + matrix_type, packing, 0); *rvalue = deref; @@ -205,13 +206,13 @@ lower_shared_reference_visitor::handle_assignment(ir_assignment *ir) ir_rvalue *offset = NULL; unsigned const_offset = get_shared_offset(var); bool row_major; - int matrix_columns; + const glsl_type *matrix_type; assert(var->get_interface_type() == NULL); const enum glsl_interface_packing packing = GLSL_INTERFACE_PACKING_STD430; setup_buffer_access(mem_ctx, deref, &offset, &const_offset, - &row_major, &matrix_columns, NULL, packing); + &row_major, &matrix_type, NULL, packing); deref = new(mem_ctx) ir_dereference_variable(store_var); @@ -223,7 +224,7 @@ lower_shared_reference_visitor::handle_assignment(ir_assignment *ir) /* Now we have to write the value assigned to the temporary back to memory */ emit_access(mem_ctx, true, deref, store_offset, const_offset, row_major, - matrix_columns, packing, ir->write_mask); + matrix_type, packing, ir->write_mask); progress = true; } @@ -241,7 +242,7 @@ lower_shared_reference_visitor::insert_buffer_access(void *mem_ctx, const glsl_type *type, ir_rvalue *offset, unsigned mask, - int channel) + int /* channel */) { if (buffer_access_type == shared_store_access) { ir_call *store = shared_store(mem_ctx, deref, offset, mask); @@ -352,7 +353,8 @@ lower_shared_reference_visitor::lower_shared_atomic_intrinsic(ir_call *ir) inst->ir_type == ir_type_swizzle); ir_rvalue *deref = (ir_rvalue *) inst; - assert(deref->type->is_scalar() && deref->type->is_integer()); + assert(deref->type->is_scalar() && + (deref->type->is_integer() || deref->type->is_float())); ir_variable *var = deref->variable_referenced(); assert(var); @@ -364,18 +366,18 @@ lower_shared_reference_visitor::lower_shared_atomic_intrinsic(ir_call *ir) ir_rvalue *offset = NULL; unsigned const_offset = get_shared_offset(var); bool row_major; - int matrix_columns; + const glsl_type *matrix_type; assert(var->get_interface_type() == NULL); const enum glsl_interface_packing packing = GLSL_INTERFACE_PACKING_STD430; buffer_access_type = shared_atomic_access; setup_buffer_access(mem_ctx, deref, &offset, &const_offset, - &row_major, &matrix_columns, NULL, packing); + &row_major, &matrix_type, NULL, packing); assert(offset); assert(!row_major); - assert(matrix_columns == 1); + assert(matrix_type == NULL); ir_rvalue *deref_offset = add(offset, new(mem_ctx) ir_constant(const_offset)); @@ -388,8 +390,7 @@ lower_shared_reference_visitor::lower_shared_atomic_intrinsic(ir_call *ir) ir_variable(glsl_type::uint_type, "offset" , ir_var_function_in); sig_params.push_tail(sig_param); - const glsl_type *type = deref->type->base_type == GLSL_TYPE_INT ? - glsl_type::int_type : glsl_type::uint_type; + const glsl_type *type = deref->type->get_scalar_type(); sig_param = new(mem_ctx) ir_variable(type, "data1", ir_var_function_in); sig_params.push_tail(sig_param); diff --git a/lib/mesa/src/compiler/glsl/lower_tess_level.cpp b/lib/mesa/src/compiler/glsl/lower_tess_level.cpp index b0965eb56..3e4c7f026 100644 --- a/lib/mesa/src/compiler/glsl/lower_tess_level.cpp +++ b/lib/mesa/src/compiler/glsl/lower_tess_level.cpp @@ -49,6 +49,7 @@ #include "ir_rvalue_visitor.h" #include "ir.h" #include "program/prog_instruction.h" /* For WRITEMASK_* */ +#include "main/mtypes.h" namespace { diff --git a/lib/mesa/src/compiler/glsl/lower_vector_derefs.cpp b/lib/mesa/src/compiler/glsl/lower_vector_derefs.cpp index a83658d20..6cd9a2d81 100644 --- a/lib/mesa/src/compiler/glsl/lower_vector_derefs.cpp +++ b/lib/mesa/src/compiler/glsl/lower_vector_derefs.cpp @@ -24,6 +24,7 @@ #include "ir_builder.h" #include "ir_rvalue_visitor.h" #include "ir_optimization.h" +#include "main/mtypes.h" using namespace ir_builder; @@ -58,8 +59,7 @@ vector_deref_visitor::visit_enter(ir_assignment *ir) if (!deref->array->type->is_vector()) return ir_rvalue_enter_visitor::visit_enter(ir); - ir_dereference *const new_lhs = (ir_dereference *) deref->array; - ir->set_lhs(new_lhs); + ir_rvalue *const new_lhs = deref->array; void *mem_ctx = ralloc_parent(ir); ir_constant *old_index_constant = @@ -71,8 +71,16 @@ vector_deref_visitor::visit_enter(ir_assignment *ir) ir->rhs, deref->array_index); ir->write_mask = (1 << new_lhs->type->vector_elements) - 1; + ir->set_lhs(new_lhs); + } else if (new_lhs->ir_type != ir_type_swizzle) { + ir->set_lhs(new_lhs); + ir->write_mask = 1 << old_index_constant->get_uint_component(0); } else { - ir->write_mask = 1 << old_index_constant->get_int_component(0); + /* If the "new" LHS is a swizzle, use the set_lhs helper to instead + * swizzle the RHS. + */ + unsigned component[1] = { old_index_constant->get_uint_component(0) }; + ir->set_lhs(new(mem_ctx) ir_swizzle(new_lhs, component, 1)); } return ir_rvalue_enter_visitor::visit_enter(ir); diff --git a/lib/mesa/src/compiler/glsl/meson.build b/lib/mesa/src/compiler/glsl/meson.build new file mode 100644 index 000000000..71b4c42e4 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/meson.build @@ -0,0 +1,263 @@ +# Copyright © 2017 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 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. + +subdir('glcpp') + +glsl_parser = custom_target( + 'glsl_parser', + input : 'glsl_parser.yy', + output : ['glsl_parser.cpp', 'glsl_parser.h'], + command : [ + prog_bison, '-o', '@OUTPUT0@', '-p', '_mesa_glsl_', '--defines=@OUTPUT1@', + '@INPUT@', + ], +) + +glsl_lexer_cpp = custom_target( + 'glsl_lexer_cpp', + input : 'glsl_lexer.ll', + output : 'glsl_lexer.cpp', + command : [prog_flex, '-o', '@OUTPUT@', '@INPUT@'], +) + +ir_expression_operation_constant_h = custom_target( + 'ir_expression_operation_constant.h', + input : 'ir_expression_operation.py', + output : 'ir_expression_operation_constant.h', + command : [prog_python, '@INPUT@', 'constant'], + capture : true, +) + +ir_expression_operation_strings_h = custom_target( + 'ir_expression_operation_strings.h', + input : 'ir_expression_operation.py', + output : 'ir_expression_operation_strings.h', + command : [prog_python, '@INPUT@', 'strings'], + capture : true, +) + +files_libglsl = files( + 'ast.h', + 'ast_array_index.cpp', + 'ast_expr.cpp', + 'ast_function.cpp', + 'ast_to_hir.cpp', + 'ast_type.cpp', + 'builtin_functions.cpp', + 'builtin_functions.h', + 'builtin_int64.h', + 'builtin_types.cpp', + 'builtin_variables.cpp', + 'generate_ir.cpp', + 'gl_nir_lower_atomics.c', + 'gl_nir_lower_samplers.c', + 'gl_nir_lower_samplers_as_deref.c', + 'gl_nir_link_atomics.c', + 'gl_nir_link_uniform_initializers.c', + 'gl_nir_link_uniforms.c', + 'gl_nir_link_xfb.c', + 'gl_nir_linker.c', + 'gl_nir_linker.h', + 'gl_nir.h', + 'glsl_parser_extras.cpp', + 'glsl_parser_extras.h', + 'glsl_symbol_table.cpp', + 'glsl_symbol_table.h', + 'glsl_to_nir.cpp', + 'glsl_to_nir.h', + 'hir_field_selection.cpp', + 'ir_array_refcount.cpp', + 'ir_array_refcount.h', + 'ir_basic_block.cpp', + 'ir_basic_block.h', + 'ir_builder.cpp', + 'ir_builder.h', + 'ir_clone.cpp', + 'ir_constant_expression.cpp', + 'ir.cpp', + 'ir.h', + 'ir_equals.cpp', + 'ir_expression_flattening.cpp', + 'ir_expression_flattening.h', + 'ir_function_can_inline.cpp', + 'ir_function_detect_recursion.cpp', + 'ir_function_inlining.h', + 'ir_function.cpp', + 'ir_hierarchical_visitor.cpp', + 'ir_hierarchical_visitor.h', + 'ir_hv_accept.cpp', + 'ir_optimization.h', + 'ir_print_visitor.cpp', + 'ir_print_visitor.h', + 'ir_reader.cpp', + 'ir_reader.h', + 'ir_rvalue_visitor.cpp', + 'ir_rvalue_visitor.h', + 'ir_set_program_inouts.cpp', + 'ir_uniform.h', + 'ir_validate.cpp', + 'ir_variable_refcount.cpp', + 'ir_variable_refcount.h', + 'ir_visitor.h', + 'linker.cpp', + 'linker.h', + 'linker_util.h', + 'linker_util.cpp', + 'link_atomics.cpp', + 'link_functions.cpp', + 'link_interface_blocks.cpp', + 'link_uniforms.cpp', + 'link_uniform_initializers.cpp', + 'link_uniform_block_active_visitor.cpp', + 'link_uniform_block_active_visitor.h', + 'link_uniform_blocks.cpp', + 'link_varyings.cpp', + 'link_varyings.h', + 'list.h', + 'loop_analysis.cpp', + 'loop_analysis.h', + 'loop_unroll.cpp', + 'lower_blend_equation_advanced.cpp', + 'lower_buffer_access.cpp', + 'lower_buffer_access.h', + 'lower_const_arrays_to_uniforms.cpp', + 'lower_cs_derived.cpp', + 'lower_discard.cpp', + 'lower_discard_flow.cpp', + 'lower_distance.cpp', + 'lower_if_to_cond_assign.cpp', + 'lower_instructions.cpp', + 'lower_int64.cpp', + 'lower_jumps.cpp', + 'lower_mat_op_to_vec.cpp', + 'lower_noise.cpp', + 'lower_offset_array.cpp', + 'lower_packed_varyings.cpp', + 'lower_named_interface_blocks.cpp', + 'lower_packing_builtins.cpp', + 'lower_subroutine.cpp', + 'lower_tess_level.cpp', + 'lower_texture_projection.cpp', + 'lower_variable_index_to_cond_assign.cpp', + 'lower_vec_index_to_cond_assign.cpp', + 'lower_vec_index_to_swizzle.cpp', + 'lower_vector.cpp', + 'lower_vector_derefs.cpp', + 'lower_vector_insert.cpp', + 'lower_vertex_id.cpp', + 'lower_output_reads.cpp', + 'lower_shared_reference.cpp', + 'lower_ubo_reference.cpp', + 'opt_algebraic.cpp', + 'opt_array_splitting.cpp', + 'opt_conditional_discard.cpp', + 'opt_constant_folding.cpp', + 'opt_constant_propagation.cpp', + 'opt_constant_variable.cpp', + 'opt_copy_propagation_elements.cpp', + 'opt_dead_builtin_variables.cpp', + 'opt_dead_builtin_varyings.cpp', + 'opt_dead_code.cpp', + 'opt_dead_code_local.cpp', + 'opt_dead_functions.cpp', + 'opt_flatten_nested_if_blocks.cpp', + 'opt_flip_matrices.cpp', + 'opt_function_inlining.cpp', + 'opt_if_simplification.cpp', + 'opt_minmax.cpp', + 'opt_rebalance_tree.cpp', + 'opt_redundant_jumps.cpp', + 'opt_structure_splitting.cpp', + 'opt_swizzle.cpp', + 'opt_tree_grafting.cpp', + 'opt_vectorize.cpp', + 'program.h', + 'propagate_invariance.cpp', + 's_expression.cpp', + 's_expression.h', + 'string_to_uint_map.cpp', + 'string_to_uint_map.h', + 'serialize.cpp', + 'serialize.h', + 'shader_cache.cpp', + 'shader_cache.h', +) + +files_libglsl_standalone = files( + 'ir_builder_print_visitor.cpp', + 'ir_builder_print_visitor.h', + 'opt_add_neg_to_sub.h', + 'standalone_scaffolding.cpp', + 'standalone_scaffolding.h', + 'standalone.cpp', + 'standalone.h', +) + +libglsl = static_library( + 'glsl', + [files_libglsl, glsl_parser, glsl_lexer_cpp, ir_expression_operation_h, + ir_expression_operation_strings_h, ir_expression_operation_constant_h], + c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args], + cpp_args : [cpp_vis_args, cpp_msvc_compat_args], + link_with : libglcpp, + include_directories : [inc_common, inc_compiler, inc_nir], + dependencies : idep_nir, + build_by_default : false, +) + +libglsl_standalone = static_library( + 'glsl_standalone', + [files_libglsl_standalone, ir_expression_operation_h], + c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args], + cpp_args : [cpp_vis_args, cpp_msvc_compat_args], + include_directories : [inc_common], + link_with : [libglsl, libglsl_util, libmesa_util], + dependencies : [dep_thread], + build_by_default : false, +) + +glsl_compiler = executable( + 'glsl_compiler', + 'main.cpp', + c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args], + cpp_args : [cpp_vis_args, cpp_msvc_compat_args], + dependencies : [dep_clock, dep_thread], + include_directories : [inc_common], + link_with : [libglsl_standalone], + build_by_default : with_tools.contains('glsl'), + install : with_tools.contains('glsl'), +) + +glsl_test = executable( + 'glsl_test', + ['test.cpp', 'test_optpass.cpp', 'test_optpass.h', + ir_expression_operation_h], + c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args], + cpp_args : [cpp_vis_args, cpp_msvc_compat_args], + include_directories : [inc_common], + dependencies : [dep_clock, dep_thread], + link_with : [libglsl, libglsl_standalone, libglsl_util], + build_by_default : with_tools.contains('glsl'), + install : with_tools.contains('glsl'), +) + +if with_any_opengl and with_tests + subdir('tests') +endif diff --git a/lib/mesa/src/compiler/glsl/opt_constant_variable.cpp b/lib/mesa/src/compiler/glsl/opt_constant_variable.cpp index 914b46004..15d8cf7cd 100644 --- a/lib/mesa/src/compiler/glsl/opt_constant_variable.cpp +++ b/lib/mesa/src/compiler/glsl/opt_constant_variable.cpp @@ -190,7 +190,6 @@ do_constant_variable(exec_list *instructions) _mesa_key_pointer_equal); v.run(instructions); - struct hash_entry *hte; hash_table_foreach(v.ht, hte) { struct assignment_entry *entry = (struct assignment_entry *) hte->data; diff --git a/lib/mesa/src/compiler/glsl/opt_dead_builtin_varyings.cpp b/lib/mesa/src/compiler/glsl/opt_dead_builtin_varyings.cpp index 4526f2bca..6ed00128e 100644 --- a/lib/mesa/src/compiler/glsl/opt_dead_builtin_varyings.cpp +++ b/lib/mesa/src/compiler/glsl/opt_dead_builtin_varyings.cpp @@ -46,13 +46,14 @@ * The same is done for the gl_FragData fragment shader output. */ -#include "main/core.h" /* for snprintf and ARRAY_SIZE */ #include "ir.h" #include "ir_rvalue_visitor.h" #include "ir_optimization.h" #include "ir_print_visitor.h" #include "compiler/glsl_types.h" #include "link_varyings.h" +#include "main/mtypes.h" +#include "util/u_string.h" namespace { @@ -322,14 +323,14 @@ public: if (!(external_color_usage & (1 << i))) { if (info->color[i]) { - snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i); + util_snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i); this->new_color[i] = new (ctx) ir_variable(glsl_type::vec4_type, name, ir_var_temporary); } if (info->backcolor[i]) { - snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i); + util_snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i); this->new_backcolor[i] = new (ctx) ir_variable(glsl_type::vec4_type, name, ir_var_temporary); @@ -341,7 +342,7 @@ public: info->fog) { char name[32]; - snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str); + util_snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str); this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name, ir_var_temporary); } @@ -365,13 +366,13 @@ public: if (!(external_usage & (1 << i))) { /* This varying is unused in the next stage. Declare * a temporary instead of an output. */ - snprintf(name, 32, "gl_%s_%s%i_dummy", mode_str, var_name, i); + util_snprintf(name, 32, "gl_%s_%s%i_dummy", mode_str, var_name, i); new_var[i] = new (ctx) ir_variable(glsl_type::vec4_type, name, ir_var_temporary); } else { - snprintf(name, 32, "gl_%s_%s%i", mode_str, var_name, i); + util_snprintf(name, 32, "gl_%s_%s%i", mode_str, var_name, i); new_var[i] = new(ctx) ir_variable(glsl_type::vec4_type, name, this->info->mode); @@ -557,6 +558,9 @@ do_dead_builtin_varyings(struct gl_context *ctx, if (producer) { producer_info.get(producer->ir, num_tfeedback_decls, tfeedback_decls); + if (producer->Stage == MESA_SHADER_TESS_CTRL) + producer_info.lower_texcoord_array = false; + if (!consumer) { /* At least eliminate unused gl_TexCoord elements. */ if (producer_info.lower_texcoord_array) { @@ -569,6 +573,9 @@ do_dead_builtin_varyings(struct gl_context *ctx, if (consumer) { consumer_info.get(consumer->ir, 0, NULL); + if (consumer->Stage != MESA_SHADER_FRAGMENT) + consumer_info.lower_texcoord_array = false; + if (!producer) { /* At least eliminate unused gl_TexCoord elements. */ if (consumer_info.lower_texcoord_array) { diff --git a/lib/mesa/src/compiler/glsl/opt_dead_code.cpp b/lib/mesa/src/compiler/glsl/opt_dead_code.cpp index 75e668ae4..1eff3f2fd 100644 --- a/lib/mesa/src/compiler/glsl/opt_dead_code.cpp +++ b/lib/mesa/src/compiler/glsl/opt_dead_code.cpp @@ -50,7 +50,6 @@ do_dead_code(exec_list *instructions, bool uniform_locations_assigned) v.run(instructions); - struct hash_entry *e; hash_table_foreach(v.ht, e) { ir_variable_refcount_entry *entry = (ir_variable_refcount_entry *)e->data; diff --git a/lib/mesa/src/compiler/glsl/opt_swizzle.cpp b/lib/mesa/src/compiler/glsl/opt_swizzle.cpp new file mode 100644 index 000000000..2fbe36218 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/opt_swizzle.cpp @@ -0,0 +1,119 @@ +/* + * Copyright © 2010 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. + */ + +/** + * \file opt_swizzle.cpp + * Optimize swizzle operations. + * + * First, compact a sequence of swizzled swizzles into a single swizzle. + * + * If the final resulting swizzle doesn't change the order or count of + * components, then remove the swizzle so that other optimization passes see + * the value behind it. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_rvalue_visitor.h" +#include "compiler/glsl_types.h" + +namespace { + +class ir_opt_swizzle_visitor : public ir_rvalue_visitor { +public: + ir_opt_swizzle_visitor() + { + this->progress = false; + } + + void handle_rvalue(ir_rvalue **rvalue); + bool progress; +}; + +} /* unnamed namespace */ + +void +ir_opt_swizzle_visitor::handle_rvalue(ir_rvalue **rvalue) +{ + if (!*rvalue) + return; + + ir_swizzle *swiz = (*rvalue)->as_swizzle(); + + if (!swiz) + return; + + ir_swizzle *swiz2; + + while ((swiz2 = swiz->val->as_swizzle()) != NULL) { + int mask2[4]; + + memset(&mask2, 0, sizeof(mask2)); + if (swiz2->mask.num_components >= 1) + mask2[0] = swiz2->mask.x; + if (swiz2->mask.num_components >= 2) + mask2[1] = swiz2->mask.y; + if (swiz2->mask.num_components >= 3) + mask2[2] = swiz2->mask.z; + if (swiz2->mask.num_components >= 4) + mask2[3] = swiz2->mask.w; + + if (swiz->mask.num_components >= 1) + swiz->mask.x = mask2[swiz->mask.x]; + if (swiz->mask.num_components >= 2) + swiz->mask.y = mask2[swiz->mask.y]; + if (swiz->mask.num_components >= 3) + swiz->mask.z = mask2[swiz->mask.z]; + if (swiz->mask.num_components >= 4) + swiz->mask.w = mask2[swiz->mask.w]; + + swiz->val = swiz2->val; + + this->progress = true; + } + + if (swiz->type != swiz->val->type) + return; + + int elems = swiz->val->type->vector_elements; + if (swiz->mask.x != 0) + return; + if (elems >= 2 && swiz->mask.y != 1) + return; + if (elems >= 3 && swiz->mask.z != 2) + return; + if (elems >= 4 && swiz->mask.w != 3) + return; + + this->progress = true; + *rvalue = swiz->val; +} + +bool +optimize_swizzles(exec_list *instructions) +{ + ir_opt_swizzle_visitor v; + visit_list_elements(&v, instructions); + + return v.progress; +} diff --git a/lib/mesa/src/compiler/glsl/s_expression.cpp b/lib/mesa/src/compiler/glsl/s_expression.cpp index f82e155a6..12baf1d3e 100644 --- a/lib/mesa/src/compiler/glsl/s_expression.cpp +++ b/lib/mesa/src/compiler/glsl/s_expression.cpp @@ -25,6 +25,8 @@ #include <assert.h> #include <stdio.h> #include <math.h> +#include <string.h> +#include <stdlib.h> #include "s_expression.h" s_symbol::s_symbol(const char *str, size_t n) diff --git a/lib/mesa/src/compiler/glsl/serialize.cpp b/lib/mesa/src/compiler/glsl/serialize.cpp new file mode 100644 index 000000000..26d8ec4b7 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/serialize.cpp @@ -0,0 +1,1292 @@ +/* + * Copyright © 2014 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. + */ + +/** + * \file serialize.cpp + * + * GLSL serialization + * + * Supports serializing and deserializing glsl programs using a blob. + */ + +#include "compiler/glsl_types.h" +#include "compiler/shader_info.h" +#include "ir_uniform.h" +#include "main/mtypes.h" +#include "main/shaderobj.h" +#include "program/program.h" +#include "string_to_uint_map.h" +#include "util/bitscan.h" + + +static void +write_subroutines(struct blob *metadata, struct gl_shader_program *prog) +{ + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (!sh) + continue; + + struct gl_program *glprog = sh->Program; + + blob_write_uint32(metadata, glprog->sh.NumSubroutineUniforms); + blob_write_uint32(metadata, glprog->sh.MaxSubroutineFunctionIndex); + blob_write_uint32(metadata, glprog->sh.NumSubroutineFunctions); + for (unsigned j = 0; j < glprog->sh.NumSubroutineFunctions; j++) { + int num_types = glprog->sh.SubroutineFunctions[j].num_compat_types; + + blob_write_string(metadata, glprog->sh.SubroutineFunctions[j].name); + blob_write_uint32(metadata, glprog->sh.SubroutineFunctions[j].index); + blob_write_uint32(metadata, num_types); + + for (int k = 0; k < num_types; k++) { + encode_type_to_blob(metadata, + glprog->sh.SubroutineFunctions[j].types[k]); + } + } + } +} + +static void +read_subroutines(struct blob_reader *metadata, struct gl_shader_program *prog) +{ + struct gl_subroutine_function *subs; + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (!sh) + continue; + + struct gl_program *glprog = sh->Program; + + glprog->sh.NumSubroutineUniforms = blob_read_uint32(metadata); + glprog->sh.MaxSubroutineFunctionIndex = blob_read_uint32(metadata); + glprog->sh.NumSubroutineFunctions = blob_read_uint32(metadata); + + subs = rzalloc_array(prog, struct gl_subroutine_function, + glprog->sh.NumSubroutineFunctions); + glprog->sh.SubroutineFunctions = subs; + + for (unsigned j = 0; j < glprog->sh.NumSubroutineFunctions; j++) { + subs[j].name = ralloc_strdup(prog, blob_read_string (metadata)); + subs[j].index = (int) blob_read_uint32(metadata); + subs[j].num_compat_types = (int) blob_read_uint32(metadata); + + subs[j].types = rzalloc_array(prog, const struct glsl_type *, + subs[j].num_compat_types); + for (int k = 0; k < subs[j].num_compat_types; k++) { + subs[j].types[k] = decode_type_from_blob(metadata); + } + } + } +} + +static void +write_buffer_block(struct blob *metadata, struct gl_uniform_block *b) +{ + blob_write_string(metadata, b->Name); + blob_write_uint32(metadata, b->NumUniforms); + blob_write_uint32(metadata, b->Binding); + blob_write_uint32(metadata, b->UniformBufferSize); + blob_write_uint32(metadata, b->stageref); + + for (unsigned j = 0; j < b->NumUniforms; j++) { + blob_write_string(metadata, b->Uniforms[j].Name); + blob_write_string(metadata, b->Uniforms[j].IndexName); + encode_type_to_blob(metadata, b->Uniforms[j].Type); + blob_write_uint32(metadata, b->Uniforms[j].Offset); + } +} + +static void +write_buffer_blocks(struct blob *metadata, struct gl_shader_program *prog) +{ + blob_write_uint32(metadata, prog->data->NumUniformBlocks); + blob_write_uint32(metadata, prog->data->NumShaderStorageBlocks); + + for (unsigned i = 0; i < prog->data->NumUniformBlocks; i++) { + write_buffer_block(metadata, &prog->data->UniformBlocks[i]); + } + + for (unsigned i = 0; i < prog->data->NumShaderStorageBlocks; i++) { + write_buffer_block(metadata, &prog->data->ShaderStorageBlocks[i]); + } + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (!sh) + continue; + + struct gl_program *glprog = sh->Program; + + blob_write_uint32(metadata, glprog->info.num_ubos); + blob_write_uint32(metadata, glprog->info.num_ssbos); + + for (unsigned j = 0; j < glprog->info.num_ubos; j++) { + uint32_t offset = + glprog->sh.UniformBlocks[j] - prog->data->UniformBlocks; + blob_write_uint32(metadata, offset); + } + + for (unsigned j = 0; j < glprog->info.num_ssbos; j++) { + uint32_t offset = glprog->sh.ShaderStorageBlocks[j] - + prog->data->ShaderStorageBlocks; + blob_write_uint32(metadata, offset); + } + } +} + +static void +read_buffer_block(struct blob_reader *metadata, struct gl_uniform_block *b, + struct gl_shader_program *prog) +{ + b->Name = ralloc_strdup(prog->data, blob_read_string (metadata)); + b->NumUniforms = blob_read_uint32(metadata); + b->Binding = blob_read_uint32(metadata); + b->UniformBufferSize = blob_read_uint32(metadata); + b->stageref = blob_read_uint32(metadata); + + b->Uniforms = + rzalloc_array(prog->data, struct gl_uniform_buffer_variable, + b->NumUniforms); + for (unsigned j = 0; j < b->NumUniforms; j++) { + b->Uniforms[j].Name = ralloc_strdup(prog->data, + blob_read_string (metadata)); + + char *index_name = blob_read_string(metadata); + if (strcmp(b->Uniforms[j].Name, index_name) == 0) { + b->Uniforms[j].IndexName = b->Uniforms[j].Name; + } else { + b->Uniforms[j].IndexName = ralloc_strdup(prog->data, index_name); + } + + b->Uniforms[j].Type = decode_type_from_blob(metadata); + b->Uniforms[j].Offset = blob_read_uint32(metadata); + } +} + +static void +read_buffer_blocks(struct blob_reader *metadata, + struct gl_shader_program *prog) +{ + prog->data->NumUniformBlocks = blob_read_uint32(metadata); + prog->data->NumShaderStorageBlocks = blob_read_uint32(metadata); + + prog->data->UniformBlocks = + rzalloc_array(prog->data, struct gl_uniform_block, + prog->data->NumUniformBlocks); + + prog->data->ShaderStorageBlocks = + rzalloc_array(prog->data, struct gl_uniform_block, + prog->data->NumShaderStorageBlocks); + + for (unsigned i = 0; i < prog->data->NumUniformBlocks; i++) { + read_buffer_block(metadata, &prog->data->UniformBlocks[i], prog); + } + + for (unsigned i = 0; i < prog->data->NumShaderStorageBlocks; i++) { + read_buffer_block(metadata, &prog->data->ShaderStorageBlocks[i], prog); + } + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (!sh) + continue; + + struct gl_program *glprog = sh->Program; + + glprog->info.num_ubos = blob_read_uint32(metadata); + glprog->info.num_ssbos = blob_read_uint32(metadata); + + glprog->sh.UniformBlocks = + rzalloc_array(glprog, gl_uniform_block *, glprog->info.num_ubos); + glprog->sh.ShaderStorageBlocks = + rzalloc_array(glprog, gl_uniform_block *, glprog->info.num_ssbos); + + for (unsigned j = 0; j < glprog->info.num_ubos; j++) { + uint32_t offset = blob_read_uint32(metadata); + glprog->sh.UniformBlocks[j] = prog->data->UniformBlocks + offset; + } + + for (unsigned j = 0; j < glprog->info.num_ssbos; j++) { + uint32_t offset = blob_read_uint32(metadata); + glprog->sh.ShaderStorageBlocks[j] = + prog->data->ShaderStorageBlocks + offset; + } + } +} + +static void +write_atomic_buffers(struct blob *metadata, struct gl_shader_program *prog) +{ + blob_write_uint32(metadata, prog->data->NumAtomicBuffers); + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + if (prog->_LinkedShaders[i]) { + struct gl_program *glprog = prog->_LinkedShaders[i]->Program; + blob_write_uint32(metadata, glprog->info.num_abos); + } + } + + for (unsigned i = 0; i < prog->data->NumAtomicBuffers; i++) { + blob_write_uint32(metadata, prog->data->AtomicBuffers[i].Binding); + blob_write_uint32(metadata, prog->data->AtomicBuffers[i].MinimumSize); + blob_write_uint32(metadata, prog->data->AtomicBuffers[i].NumUniforms); + + blob_write_bytes(metadata, prog->data->AtomicBuffers[i].StageReferences, + sizeof(prog->data->AtomicBuffers[i].StageReferences)); + + for (unsigned j = 0; j < prog->data->AtomicBuffers[i].NumUniforms; j++) { + blob_write_uint32(metadata, prog->data->AtomicBuffers[i].Uniforms[j]); + } + } +} + +static void +read_atomic_buffers(struct blob_reader *metadata, + struct gl_shader_program *prog) +{ + prog->data->NumAtomicBuffers = blob_read_uint32(metadata); + prog->data->AtomicBuffers = + rzalloc_array(prog, gl_active_atomic_buffer, + prog->data->NumAtomicBuffers); + + struct gl_active_atomic_buffer **stage_buff_list[MESA_SHADER_STAGES]; + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + if (prog->_LinkedShaders[i]) { + struct gl_program *glprog = prog->_LinkedShaders[i]->Program; + + glprog->info.num_abos = blob_read_uint32(metadata); + glprog->sh.AtomicBuffers = + rzalloc_array(glprog, gl_active_atomic_buffer *, + glprog->info.num_abos); + stage_buff_list[i] = glprog->sh.AtomicBuffers; + } + } + + for (unsigned i = 0; i < prog->data->NumAtomicBuffers; i++) { + prog->data->AtomicBuffers[i].Binding = blob_read_uint32(metadata); + prog->data->AtomicBuffers[i].MinimumSize = blob_read_uint32(metadata); + prog->data->AtomicBuffers[i].NumUniforms = blob_read_uint32(metadata); + + blob_copy_bytes(metadata, + (uint8_t *) &prog->data->AtomicBuffers[i].StageReferences, + sizeof(prog->data->AtomicBuffers[i].StageReferences)); + + prog->data->AtomicBuffers[i].Uniforms = rzalloc_array(prog, unsigned, + prog->data->AtomicBuffers[i].NumUniforms); + + for (unsigned j = 0; j < prog->data->AtomicBuffers[i].NumUniforms; j++) { + prog->data->AtomicBuffers[i].Uniforms[j] = blob_read_uint32(metadata); + } + + for (unsigned j = 0; j < MESA_SHADER_STAGES; j++) { + if (prog->data->AtomicBuffers[i].StageReferences[j]) { + *stage_buff_list[j] = &prog->data->AtomicBuffers[i]; + stage_buff_list[j]++; + } + } + } +} + +static void +write_xfb(struct blob *metadata, struct gl_shader_program *shProg) +{ + struct gl_program *prog = shProg->last_vert_prog; + + if (!prog) { + blob_write_uint32(metadata, ~0u); + return; + } + + struct gl_transform_feedback_info *ltf = prog->sh.LinkedTransformFeedback; + + blob_write_uint32(metadata, prog->info.stage); + + /* Data set by glTransformFeedbackVaryings. */ + blob_write_uint32(metadata, shProg->TransformFeedback.BufferMode); + blob_write_bytes(metadata, shProg->TransformFeedback.BufferStride, + sizeof(shProg->TransformFeedback.BufferStride)); + blob_write_uint32(metadata, shProg->TransformFeedback.NumVarying); + for (unsigned i = 0; i < shProg->TransformFeedback.NumVarying; i++) + blob_write_string(metadata, shProg->TransformFeedback.VaryingNames[i]); + + blob_write_uint32(metadata, ltf->NumOutputs); + blob_write_uint32(metadata, ltf->ActiveBuffers); + blob_write_uint32(metadata, ltf->NumVarying); + + blob_write_bytes(metadata, ltf->Outputs, + sizeof(struct gl_transform_feedback_output) * + ltf->NumOutputs); + + for (int i = 0; i < ltf->NumVarying; i++) { + blob_write_string(metadata, ltf->Varyings[i].Name); + blob_write_uint32(metadata, ltf->Varyings[i].Type); + blob_write_uint32(metadata, ltf->Varyings[i].BufferIndex); + blob_write_uint32(metadata, ltf->Varyings[i].Size); + blob_write_uint32(metadata, ltf->Varyings[i].Offset); + } + + blob_write_bytes(metadata, ltf->Buffers, + sizeof(struct gl_transform_feedback_buffer) * + MAX_FEEDBACK_BUFFERS); +} + +static void +read_xfb(struct blob_reader *metadata, struct gl_shader_program *shProg) +{ + unsigned xfb_stage = blob_read_uint32(metadata); + + if (xfb_stage == ~0u) + return; + + if (shProg->TransformFeedback.VaryingNames) { + for (unsigned i = 0; i < shProg->TransformFeedback.NumVarying; ++i) + free(shProg->TransformFeedback.VaryingNames[i]); + } + + /* Data set by glTransformFeedbackVaryings. */ + shProg->TransformFeedback.BufferMode = blob_read_uint32(metadata); + blob_copy_bytes(metadata, &shProg->TransformFeedback.BufferStride, + sizeof(shProg->TransformFeedback.BufferStride)); + shProg->TransformFeedback.NumVarying = blob_read_uint32(metadata); + + shProg->TransformFeedback.VaryingNames = (char **) + realloc(shProg->TransformFeedback.VaryingNames, + shProg->TransformFeedback.NumVarying * sizeof(GLchar *)); + /* Note, malloc used with VaryingNames. */ + for (unsigned i = 0; i < shProg->TransformFeedback.NumVarying; i++) + shProg->TransformFeedback.VaryingNames[i] = + strdup(blob_read_string(metadata)); + + struct gl_program *prog = shProg->_LinkedShaders[xfb_stage]->Program; + struct gl_transform_feedback_info *ltf = + rzalloc(prog, struct gl_transform_feedback_info); + + prog->sh.LinkedTransformFeedback = ltf; + shProg->last_vert_prog = prog; + + ltf->NumOutputs = blob_read_uint32(metadata); + ltf->ActiveBuffers = blob_read_uint32(metadata); + ltf->NumVarying = blob_read_uint32(metadata); + + ltf->Outputs = rzalloc_array(prog, struct gl_transform_feedback_output, + ltf->NumOutputs); + + blob_copy_bytes(metadata, (uint8_t *) ltf->Outputs, + sizeof(struct gl_transform_feedback_output) * + ltf->NumOutputs); + + ltf->Varyings = rzalloc_array(prog, + struct gl_transform_feedback_varying_info, + ltf->NumVarying); + + for (int i = 0; i < ltf->NumVarying; i++) { + ltf->Varyings[i].Name = ralloc_strdup(prog, blob_read_string(metadata)); + ltf->Varyings[i].Type = blob_read_uint32(metadata); + ltf->Varyings[i].BufferIndex = blob_read_uint32(metadata); + ltf->Varyings[i].Size = blob_read_uint32(metadata); + ltf->Varyings[i].Offset = blob_read_uint32(metadata); + } + + blob_copy_bytes(metadata, (uint8_t *) ltf->Buffers, + sizeof(struct gl_transform_feedback_buffer) * + MAX_FEEDBACK_BUFFERS); +} + +static bool +has_uniform_storage(struct gl_shader_program *prog, unsigned idx) +{ + if (!prog->data->UniformStorage[idx].builtin && + !prog->data->UniformStorage[idx].is_shader_storage && + prog->data->UniformStorage[idx].block_index == -1) + return true; + + return false; +} + +static void +write_uniforms(struct blob *metadata, struct gl_shader_program *prog) +{ + blob_write_uint32(metadata, prog->SamplersValidated); + blob_write_uint32(metadata, prog->data->NumUniformStorage); + blob_write_uint32(metadata, prog->data->NumUniformDataSlots); + + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { + encode_type_to_blob(metadata, prog->data->UniformStorage[i].type); + blob_write_uint32(metadata, prog->data->UniformStorage[i].array_elements); + blob_write_string(metadata, prog->data->UniformStorage[i].name); + blob_write_uint32(metadata, prog->data->UniformStorage[i].builtin); + blob_write_uint32(metadata, prog->data->UniformStorage[i].remap_location); + blob_write_uint32(metadata, prog->data->UniformStorage[i].block_index); + blob_write_uint32(metadata, prog->data->UniformStorage[i].atomic_buffer_index); + blob_write_uint32(metadata, prog->data->UniformStorage[i].offset); + blob_write_uint32(metadata, prog->data->UniformStorage[i].array_stride); + blob_write_uint32(metadata, prog->data->UniformStorage[i].hidden); + blob_write_uint32(metadata, prog->data->UniformStorage[i].is_shader_storage); + blob_write_uint32(metadata, prog->data->UniformStorage[i].active_shader_mask); + blob_write_uint32(metadata, prog->data->UniformStorage[i].matrix_stride); + blob_write_uint32(metadata, prog->data->UniformStorage[i].row_major); + blob_write_uint32(metadata, prog->data->UniformStorage[i].is_bindless); + blob_write_uint32(metadata, + prog->data->UniformStorage[i].num_compatible_subroutines); + blob_write_uint32(metadata, + prog->data->UniformStorage[i].top_level_array_size); + blob_write_uint32(metadata, + prog->data->UniformStorage[i].top_level_array_stride); + + if (has_uniform_storage(prog, i)) { + blob_write_uint32(metadata, prog->data->UniformStorage[i].storage - + prog->data->UniformDataSlots); + } + + blob_write_bytes(metadata, prog->data->UniformStorage[i].opaque, + sizeof(prog->data->UniformStorage[i].opaque)); + } + + /* Here we cache all uniform values. We do this to retain values for + * uniforms with initialisers and also hidden uniforms that may be lowered + * constant arrays. We could possibly just store the values we need but for + * now we just store everything. + */ + blob_write_uint32(metadata, prog->data->NumHiddenUniforms); + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { + if (has_uniform_storage(prog, i)) { + unsigned vec_size = + prog->data->UniformStorage[i].type->component_slots() * + MAX2(prog->data->UniformStorage[i].array_elements, 1); + unsigned slot = + prog->data->UniformStorage[i].storage - + prog->data->UniformDataSlots; + blob_write_bytes(metadata, &prog->data->UniformDataDefaults[slot], + sizeof(union gl_constant_value) * vec_size); + } + } +} + +static void +read_uniforms(struct blob_reader *metadata, struct gl_shader_program *prog) +{ + struct gl_uniform_storage *uniforms; + union gl_constant_value *data; + + prog->SamplersValidated = blob_read_uint32(metadata); + prog->data->NumUniformStorage = blob_read_uint32(metadata); + prog->data->NumUniformDataSlots = blob_read_uint32(metadata); + + uniforms = rzalloc_array(prog->data, struct gl_uniform_storage, + prog->data->NumUniformStorage); + prog->data->UniformStorage = uniforms; + + data = rzalloc_array(uniforms, union gl_constant_value, + prog->data->NumUniformDataSlots); + prog->data->UniformDataSlots = data; + prog->data->UniformDataDefaults = + rzalloc_array(uniforms, union gl_constant_value, + prog->data->NumUniformDataSlots); + + prog->UniformHash = new string_to_uint_map; + + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { + uniforms[i].type = decode_type_from_blob(metadata); + uniforms[i].array_elements = blob_read_uint32(metadata); + uniforms[i].name = ralloc_strdup(prog, blob_read_string (metadata)); + uniforms[i].builtin = blob_read_uint32(metadata); + uniforms[i].remap_location = blob_read_uint32(metadata); + uniforms[i].block_index = blob_read_uint32(metadata); + uniforms[i].atomic_buffer_index = blob_read_uint32(metadata); + uniforms[i].offset = blob_read_uint32(metadata); + uniforms[i].array_stride = blob_read_uint32(metadata); + uniforms[i].hidden = blob_read_uint32(metadata); + uniforms[i].is_shader_storage = blob_read_uint32(metadata); + uniforms[i].active_shader_mask = blob_read_uint32(metadata); + uniforms[i].matrix_stride = blob_read_uint32(metadata); + uniforms[i].row_major = blob_read_uint32(metadata); + uniforms[i].is_bindless = blob_read_uint32(metadata); + uniforms[i].num_compatible_subroutines = blob_read_uint32(metadata); + uniforms[i].top_level_array_size = blob_read_uint32(metadata); + uniforms[i].top_level_array_stride = blob_read_uint32(metadata); + prog->UniformHash->put(i, uniforms[i].name); + + if (has_uniform_storage(prog, i)) { + uniforms[i].storage = data + blob_read_uint32(metadata); + } + + memcpy(uniforms[i].opaque, + blob_read_bytes(metadata, sizeof(uniforms[i].opaque)), + sizeof(uniforms[i].opaque)); + } + + /* Restore uniform values. */ + prog->data->NumHiddenUniforms = blob_read_uint32(metadata); + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { + if (has_uniform_storage(prog, i)) { + unsigned vec_size = + prog->data->UniformStorage[i].type->component_slots() * + MAX2(prog->data->UniformStorage[i].array_elements, 1); + unsigned slot = + prog->data->UniformStorage[i].storage - + prog->data->UniformDataSlots; + blob_copy_bytes(metadata, + (uint8_t *) &prog->data->UniformDataSlots[slot], + sizeof(union gl_constant_value) * vec_size); + + assert(vec_size + prog->data->UniformStorage[i].storage <= + data + prog->data->NumUniformDataSlots); + } + } + + memcpy(prog->data->UniformDataDefaults, prog->data->UniformDataSlots, + sizeof(union gl_constant_value) * prog->data->NumUniformDataSlots); +} + +enum uniform_remap_type +{ + remap_type_inactive_explicit_location, + remap_type_null_ptr, + remap_type_uniform_offset +}; + +static void +write_uniform_remap_table_entry(struct blob *metadata, + gl_uniform_storage *uniform_storage, + gl_uniform_storage *entry) +{ + if (entry == INACTIVE_UNIFORM_EXPLICIT_LOCATION) { + blob_write_uint32(metadata, remap_type_inactive_explicit_location); + } else if (entry == NULL) { + blob_write_uint32(metadata, remap_type_null_ptr); + } else { + blob_write_uint32(metadata, remap_type_uniform_offset); + + uint32_t offset = entry - uniform_storage; + blob_write_uint32(metadata, offset); + } +} + +static void +write_uniform_remap_tables(struct blob *metadata, + struct gl_shader_program *prog) +{ + blob_write_uint32(metadata, prog->NumUniformRemapTable); + + for (unsigned i = 0; i < prog->NumUniformRemapTable; i++) { + write_uniform_remap_table_entry(metadata, prog->data->UniformStorage, + prog->UniformRemapTable[i]); + } + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (sh) { + struct gl_program *glprog = sh->Program; + blob_write_uint32(metadata, glprog->sh.NumSubroutineUniformRemapTable); + + for (unsigned j = 0; j < glprog->sh.NumSubroutineUniformRemapTable; j++) { + write_uniform_remap_table_entry(metadata, + prog->data->UniformStorage, + glprog->sh.SubroutineUniformRemapTable[j]); + } + } + } +} + +static void +read_uniform_remap_table_entry(struct blob_reader *metadata, + gl_uniform_storage *uniform_storage, + gl_uniform_storage **entry, + enum uniform_remap_type type) +{ + if (type == remap_type_inactive_explicit_location) { + *entry = INACTIVE_UNIFORM_EXPLICIT_LOCATION; + } else if (type == remap_type_null_ptr) { + *entry = NULL; + } else { + uint32_t uni_offset = blob_read_uint32(metadata); + *entry = uniform_storage + uni_offset; + } +} + +static void +read_uniform_remap_tables(struct blob_reader *metadata, + struct gl_shader_program *prog) +{ + prog->NumUniformRemapTable = blob_read_uint32(metadata); + + prog->UniformRemapTable = rzalloc_array(prog, struct gl_uniform_storage *, + prog->NumUniformRemapTable); + + for (unsigned i = 0; i < prog->NumUniformRemapTable; i++) { + enum uniform_remap_type type = + (enum uniform_remap_type) blob_read_uint32(metadata); + + read_uniform_remap_table_entry(metadata, prog->data->UniformStorage, + &prog->UniformRemapTable[i], type); + } + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (sh) { + struct gl_program *glprog = sh->Program; + glprog->sh.NumSubroutineUniformRemapTable = blob_read_uint32(metadata); + + glprog->sh.SubroutineUniformRemapTable = + rzalloc_array(glprog, struct gl_uniform_storage *, + glprog->sh.NumSubroutineUniformRemapTable); + + for (unsigned j = 0; j < glprog->sh.NumSubroutineUniformRemapTable; j++) { + enum uniform_remap_type type = + (enum uniform_remap_type) blob_read_uint32(metadata); + + read_uniform_remap_table_entry(metadata, + prog->data->UniformStorage, + &glprog->sh.SubroutineUniformRemapTable[j], + type); + } + } + } +} + +struct whte_closure +{ + struct blob *blob; + size_t num_entries; +}; + +static void +write_hash_table_entry(const char *key, unsigned value, void *closure) +{ + struct whte_closure *whte = (struct whte_closure *) closure; + + blob_write_string(whte->blob, key); + blob_write_uint32(whte->blob, value); + + whte->num_entries++; +} + +static void +write_hash_table(struct blob *metadata, struct string_to_uint_map *hash) +{ + size_t offset; + struct whte_closure whte; + + whte.blob = metadata; + whte.num_entries = 0; + + offset = metadata->size; + + /* Write a placeholder for the hashtable size. */ + blob_write_uint32 (metadata, 0); + + hash->iterate(write_hash_table_entry, &whte); + + /* Overwrite with the computed number of entries written. */ + blob_overwrite_uint32 (metadata, offset, whte.num_entries); +} + +static void +read_hash_table(struct blob_reader *metadata, struct string_to_uint_map *hash) +{ + size_t i, num_entries; + const char *key; + uint32_t value; + + num_entries = blob_read_uint32 (metadata); + + for (i = 0; i < num_entries; i++) { + key = blob_read_string(metadata); + value = blob_read_uint32(metadata); + + hash->put(value, key); + } +} + +static void +write_hash_tables(struct blob *metadata, struct gl_shader_program *prog) +{ + write_hash_table(metadata, prog->AttributeBindings); + write_hash_table(metadata, prog->FragDataBindings); + write_hash_table(metadata, prog->FragDataIndexBindings); +} + +static void +read_hash_tables(struct blob_reader *metadata, struct gl_shader_program *prog) +{ + read_hash_table(metadata, prog->AttributeBindings); + read_hash_table(metadata, prog->FragDataBindings); + read_hash_table(metadata, prog->FragDataIndexBindings); +} + +static void +write_shader_subroutine_index(struct blob *metadata, + struct gl_linked_shader *sh, + struct gl_program_resource *res) +{ + assert(sh); + + for (unsigned j = 0; j < sh->Program->sh.NumSubroutineFunctions; j++) { + if (strcmp(((gl_subroutine_function *)res->Data)->name, + sh->Program->sh.SubroutineFunctions[j].name) == 0) { + blob_write_uint32(metadata, j); + break; + } + } +} + +static void +get_shader_var_and_pointer_sizes(size_t *s_var_size, size_t *s_var_ptrs, + const gl_shader_variable *var) +{ + *s_var_size = sizeof(gl_shader_variable); + *s_var_ptrs = + sizeof(var->type) + + sizeof(var->interface_type) + + sizeof(var->outermost_struct_type) + + sizeof(var->name); +} + +static void +write_program_resource_data(struct blob *metadata, + struct gl_shader_program *prog, + struct gl_program_resource *res) +{ + struct gl_linked_shader *sh; + + switch(res->Type) { + case GL_PROGRAM_INPUT: + case GL_PROGRAM_OUTPUT: { + const gl_shader_variable *var = (gl_shader_variable *)res->Data; + + encode_type_to_blob(metadata, var->type); + encode_type_to_blob(metadata, var->interface_type); + encode_type_to_blob(metadata, var->outermost_struct_type); + + blob_write_string(metadata, var->name); + + size_t s_var_size, s_var_ptrs; + get_shader_var_and_pointer_sizes(&s_var_size, &s_var_ptrs, var); + + /* Write gl_shader_variable skipping over the pointers */ + blob_write_bytes(metadata, ((char *)var) + s_var_ptrs, + s_var_size - s_var_ptrs); + break; + } + case GL_UNIFORM_BLOCK: + for (unsigned i = 0; i < prog->data->NumUniformBlocks; i++) { + if (strcmp(((gl_uniform_block *)res->Data)->Name, + prog->data->UniformBlocks[i].Name) == 0) { + blob_write_uint32(metadata, i); + break; + } + } + break; + case GL_SHADER_STORAGE_BLOCK: + for (unsigned i = 0; i < prog->data->NumShaderStorageBlocks; i++) { + if (strcmp(((gl_uniform_block *)res->Data)->Name, + prog->data->ShaderStorageBlocks[i].Name) == 0) { + blob_write_uint32(metadata, i); + break; + } + } + break; + case GL_BUFFER_VARIABLE: + case GL_VERTEX_SUBROUTINE_UNIFORM: + case GL_GEOMETRY_SUBROUTINE_UNIFORM: + case GL_FRAGMENT_SUBROUTINE_UNIFORM: + case GL_COMPUTE_SUBROUTINE_UNIFORM: + case GL_TESS_CONTROL_SUBROUTINE_UNIFORM: + case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM: + case GL_UNIFORM: + for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) { + if (strcmp(((gl_uniform_storage *)res->Data)->name, + prog->data->UniformStorage[i].name) == 0) { + blob_write_uint32(metadata, i); + break; + } + } + break; + case GL_ATOMIC_COUNTER_BUFFER: + for (unsigned i = 0; i < prog->data->NumAtomicBuffers; i++) { + if (((gl_active_atomic_buffer *)res->Data)->Binding == + prog->data->AtomicBuffers[i].Binding) { + blob_write_uint32(metadata, i); + break; + } + } + break; + case GL_TRANSFORM_FEEDBACK_BUFFER: + for (unsigned i = 0; i < MAX_FEEDBACK_BUFFERS; i++) { + if (((gl_transform_feedback_buffer *)res->Data)->Binding == + prog->last_vert_prog->sh.LinkedTransformFeedback->Buffers[i].Binding) { + blob_write_uint32(metadata, i); + break; + } + } + break; + case GL_TRANSFORM_FEEDBACK_VARYING: + for (int i = 0; i < prog->last_vert_prog->sh.LinkedTransformFeedback->NumVarying; i++) { + if (strcmp(((gl_transform_feedback_varying_info *)res->Data)->Name, + prog->last_vert_prog->sh.LinkedTransformFeedback->Varyings[i].Name) == 0) { + blob_write_uint32(metadata, i); + break; + } + } + break; + case GL_VERTEX_SUBROUTINE: + case GL_TESS_CONTROL_SUBROUTINE: + case GL_TESS_EVALUATION_SUBROUTINE: + case GL_GEOMETRY_SUBROUTINE: + case GL_FRAGMENT_SUBROUTINE: + case GL_COMPUTE_SUBROUTINE: + sh = + prog->_LinkedShaders[_mesa_shader_stage_from_subroutine(res->Type)]; + write_shader_subroutine_index(metadata, sh, res); + break; + default: + assert(!"Support for writing resource not yet implemented."); + } +} + +static void +read_program_resource_data(struct blob_reader *metadata, + struct gl_shader_program *prog, + struct gl_program_resource *res) +{ + struct gl_linked_shader *sh; + + switch(res->Type) { + case GL_PROGRAM_INPUT: + case GL_PROGRAM_OUTPUT: { + gl_shader_variable *var = ralloc(prog, struct gl_shader_variable); + + var->type = decode_type_from_blob(metadata); + var->interface_type = decode_type_from_blob(metadata); + var->outermost_struct_type = decode_type_from_blob(metadata); + + var->name = ralloc_strdup(prog, blob_read_string(metadata)); + + size_t s_var_size, s_var_ptrs; + get_shader_var_and_pointer_sizes(&s_var_size, &s_var_ptrs, var); + + blob_copy_bytes(metadata, ((uint8_t *) var) + s_var_ptrs, + s_var_size - s_var_ptrs); + + res->Data = var; + break; + } + case GL_UNIFORM_BLOCK: + res->Data = &prog->data->UniformBlocks[blob_read_uint32(metadata)]; + break; + case GL_SHADER_STORAGE_BLOCK: + res->Data = &prog->data->ShaderStorageBlocks[blob_read_uint32(metadata)]; + break; + case GL_BUFFER_VARIABLE: + case GL_VERTEX_SUBROUTINE_UNIFORM: + case GL_GEOMETRY_SUBROUTINE_UNIFORM: + case GL_FRAGMENT_SUBROUTINE_UNIFORM: + case GL_COMPUTE_SUBROUTINE_UNIFORM: + case GL_TESS_CONTROL_SUBROUTINE_UNIFORM: + case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM: + case GL_UNIFORM: + res->Data = &prog->data->UniformStorage[blob_read_uint32(metadata)]; + break; + case GL_ATOMIC_COUNTER_BUFFER: + res->Data = &prog->data->AtomicBuffers[blob_read_uint32(metadata)]; + break; + case GL_TRANSFORM_FEEDBACK_BUFFER: + res->Data = &prog->last_vert_prog-> + sh.LinkedTransformFeedback->Buffers[blob_read_uint32(metadata)]; + break; + case GL_TRANSFORM_FEEDBACK_VARYING: + res->Data = &prog->last_vert_prog-> + sh.LinkedTransformFeedback->Varyings[blob_read_uint32(metadata)]; + break; + case GL_VERTEX_SUBROUTINE: + case GL_TESS_CONTROL_SUBROUTINE: + case GL_TESS_EVALUATION_SUBROUTINE: + case GL_GEOMETRY_SUBROUTINE: + case GL_FRAGMENT_SUBROUTINE: + case GL_COMPUTE_SUBROUTINE: + sh = + prog->_LinkedShaders[_mesa_shader_stage_from_subroutine(res->Type)]; + res->Data = + &sh->Program->sh.SubroutineFunctions[blob_read_uint32(metadata)]; + break; + default: + assert(!"Support for reading resource not yet implemented."); + } +} + +static void +write_program_resource_list(struct blob *metadata, + struct gl_shader_program *prog) +{ + blob_write_uint32(metadata, prog->data->NumProgramResourceList); + + for (unsigned i = 0; i < prog->data->NumProgramResourceList; i++) { + blob_write_uint32(metadata, prog->data->ProgramResourceList[i].Type); + write_program_resource_data(metadata, prog, + &prog->data->ProgramResourceList[i]); + blob_write_bytes(metadata, + &prog->data->ProgramResourceList[i].StageReferences, + sizeof(prog->data->ProgramResourceList[i].StageReferences)); + } +} + +static void +read_program_resource_list(struct blob_reader *metadata, + struct gl_shader_program *prog) +{ + prog->data->NumProgramResourceList = blob_read_uint32(metadata); + + prog->data->ProgramResourceList = + ralloc_array(prog->data, gl_program_resource, + prog->data->NumProgramResourceList); + + for (unsigned i = 0; i < prog->data->NumProgramResourceList; i++) { + prog->data->ProgramResourceList[i].Type = blob_read_uint32(metadata); + read_program_resource_data(metadata, prog, + &prog->data->ProgramResourceList[i]); + blob_copy_bytes(metadata, + (uint8_t *) &prog->data->ProgramResourceList[i].StageReferences, + sizeof(prog->data->ProgramResourceList[i].StageReferences)); + } +} + +static void +write_shader_parameters(struct blob *metadata, + struct gl_program_parameter_list *params) +{ + blob_write_uint32(metadata, params->NumParameters); + blob_write_uint32(metadata, params->NumParameterValues); + uint32_t i = 0; + + while (i < params->NumParameters) { + struct gl_program_parameter *param = ¶ms->Parameters[i]; + + blob_write_uint32(metadata, param->Type); + blob_write_string(metadata, param->Name); + blob_write_uint32(metadata, param->Size); + blob_write_uint32(metadata, param->DataType); + blob_write_bytes(metadata, param->StateIndexes, + sizeof(param->StateIndexes)); + + i++; + } + + blob_write_bytes(metadata, params->ParameterValues, + sizeof(gl_constant_value) * params->NumParameterValues); + + blob_write_bytes(metadata, params->ParameterValueOffset, + sizeof(uint32_t) * params->NumParameters); + + blob_write_uint32(metadata, params->StateFlags); +} + +static void +read_shader_parameters(struct blob_reader *metadata, + struct gl_program_parameter_list *params) +{ + gl_state_index16 state_indexes[STATE_LENGTH]; + uint32_t i = 0; + uint32_t num_parameters = blob_read_uint32(metadata); + uint32_t num_parameters_values = blob_read_uint32(metadata); + + _mesa_reserve_parameter_storage(params, num_parameters); + while (i < num_parameters) { + gl_register_file type = (gl_register_file) blob_read_uint32(metadata); + const char *name = blob_read_string(metadata); + unsigned size = blob_read_uint32(metadata); + unsigned data_type = blob_read_uint32(metadata); + blob_copy_bytes(metadata, (uint8_t *) state_indexes, + sizeof(state_indexes)); + + _mesa_add_parameter(params, type, name, size, data_type, + NULL, state_indexes, false); + + i++; + } + + blob_copy_bytes(metadata, (uint8_t *) params->ParameterValues, + sizeof(gl_constant_value) * num_parameters_values); + + blob_copy_bytes(metadata, (uint8_t *) params->ParameterValueOffset, + sizeof(uint32_t) * num_parameters); + + params->StateFlags = blob_read_uint32(metadata); +} + +static void +write_shader_metadata(struct blob *metadata, gl_linked_shader *shader) +{ + assert(shader->Program); + struct gl_program *glprog = shader->Program; + unsigned i; + + blob_write_uint64(metadata, glprog->DualSlotInputs); + blob_write_bytes(metadata, glprog->TexturesUsed, + sizeof(glprog->TexturesUsed)); + blob_write_uint64(metadata, glprog->SamplersUsed); + + blob_write_bytes(metadata, glprog->SamplerUnits, + sizeof(glprog->SamplerUnits)); + blob_write_bytes(metadata, glprog->sh.SamplerTargets, + sizeof(glprog->sh.SamplerTargets)); + blob_write_uint32(metadata, glprog->ShadowSamplers); + blob_write_uint32(metadata, glprog->ExternalSamplersUsed); + + blob_write_bytes(metadata, glprog->sh.ImageAccess, + sizeof(glprog->sh.ImageAccess)); + blob_write_bytes(metadata, glprog->sh.ImageUnits, + sizeof(glprog->sh.ImageUnits)); + + size_t ptr_size = sizeof(GLvoid *); + + blob_write_uint32(metadata, glprog->sh.NumBindlessSamplers); + blob_write_uint32(metadata, glprog->sh.HasBoundBindlessSampler); + for (i = 0; i < glprog->sh.NumBindlessSamplers; i++) { + blob_write_bytes(metadata, &glprog->sh.BindlessSamplers[i], + sizeof(struct gl_bindless_sampler) - ptr_size); + } + + blob_write_uint32(metadata, glprog->sh.NumBindlessImages); + blob_write_uint32(metadata, glprog->sh.HasBoundBindlessImage); + for (i = 0; i < glprog->sh.NumBindlessImages; i++) { + blob_write_bytes(metadata, &glprog->sh.BindlessImages[i], + sizeof(struct gl_bindless_image) - ptr_size); + } + + blob_write_bytes(metadata, &glprog->sh.fs.BlendSupport, + sizeof(glprog->sh.fs.BlendSupport)); + + write_shader_parameters(metadata, glprog->Parameters); + + assert((glprog->driver_cache_blob == NULL) == + (glprog->driver_cache_blob_size == 0)); + blob_write_uint32(metadata, (uint32_t)glprog->driver_cache_blob_size); + if (glprog->driver_cache_blob_size > 0) { + blob_write_bytes(metadata, glprog->driver_cache_blob, + glprog->driver_cache_blob_size); + } +} + +static void +read_shader_metadata(struct blob_reader *metadata, + struct gl_program *glprog, + gl_linked_shader *linked) +{ + unsigned i; + + glprog->DualSlotInputs = blob_read_uint64(metadata); + blob_copy_bytes(metadata, (uint8_t *) glprog->TexturesUsed, + sizeof(glprog->TexturesUsed)); + glprog->SamplersUsed = blob_read_uint64(metadata); + + blob_copy_bytes(metadata, (uint8_t *) glprog->SamplerUnits, + sizeof(glprog->SamplerUnits)); + blob_copy_bytes(metadata, (uint8_t *) glprog->sh.SamplerTargets, + sizeof(glprog->sh.SamplerTargets)); + glprog->ShadowSamplers = blob_read_uint32(metadata); + glprog->ExternalSamplersUsed = blob_read_uint32(metadata); + + blob_copy_bytes(metadata, (uint8_t *) glprog->sh.ImageAccess, + sizeof(glprog->sh.ImageAccess)); + blob_copy_bytes(metadata, (uint8_t *) glprog->sh.ImageUnits, + sizeof(glprog->sh.ImageUnits)); + + size_t ptr_size = sizeof(GLvoid *); + + glprog->sh.NumBindlessSamplers = blob_read_uint32(metadata); + glprog->sh.HasBoundBindlessSampler = blob_read_uint32(metadata); + if (glprog->sh.NumBindlessSamplers > 0) { + glprog->sh.BindlessSamplers = + rzalloc_array(glprog, gl_bindless_sampler, + glprog->sh.NumBindlessSamplers); + + for (i = 0; i < glprog->sh.NumBindlessSamplers; i++) { + blob_copy_bytes(metadata, (uint8_t *) &glprog->sh.BindlessSamplers[i], + sizeof(struct gl_bindless_sampler) - ptr_size); + } + } + + glprog->sh.NumBindlessImages = blob_read_uint32(metadata); + glprog->sh.HasBoundBindlessImage = blob_read_uint32(metadata); + if (glprog->sh.NumBindlessImages > 0) { + glprog->sh.BindlessImages = + rzalloc_array(glprog, gl_bindless_image, + glprog->sh.NumBindlessImages); + + for (i = 0; i < glprog->sh.NumBindlessImages; i++) { + blob_copy_bytes(metadata, (uint8_t *) &glprog->sh.BindlessImages[i], + sizeof(struct gl_bindless_image) - ptr_size); + } + } + + blob_copy_bytes(metadata, (uint8_t *) &glprog->sh.fs.BlendSupport, + sizeof(glprog->sh.fs.BlendSupport)); + + glprog->Parameters = _mesa_new_parameter_list(); + read_shader_parameters(metadata, glprog->Parameters); + + glprog->driver_cache_blob_size = (size_t)blob_read_uint32(metadata); + if (glprog->driver_cache_blob_size > 0) { + glprog->driver_cache_blob = + (uint8_t*)ralloc_size(glprog, glprog->driver_cache_blob_size); + blob_copy_bytes(metadata, glprog->driver_cache_blob, + glprog->driver_cache_blob_size); + } +} + +static void +get_shader_info_and_pointer_sizes(size_t *s_info_size, size_t *s_info_ptrs, + shader_info *info) +{ + *s_info_size = sizeof(shader_info); + *s_info_ptrs = sizeof(info->name) + sizeof(info->label); +} + +static void +create_linked_shader_and_program(struct gl_context *ctx, + gl_shader_stage stage, + struct gl_shader_program *prog, + struct blob_reader *metadata) +{ + struct gl_program *glprog; + + struct gl_linked_shader *linked = rzalloc(NULL, struct gl_linked_shader); + linked->Stage = stage; + + glprog = ctx->Driver.NewProgram(ctx, _mesa_shader_stage_to_program(stage), + prog->Name, false); + glprog->info.stage = stage; + linked->Program = glprog; + + read_shader_metadata(metadata, glprog, linked); + + glprog->info.name = ralloc_strdup(glprog, blob_read_string(metadata)); + glprog->info.label = ralloc_strdup(glprog, blob_read_string(metadata)); + + size_t s_info_size, s_info_ptrs; + get_shader_info_and_pointer_sizes(&s_info_size, &s_info_ptrs, + &glprog->info); + + /* Restore shader info */ + blob_copy_bytes(metadata, ((uint8_t *) &glprog->info) + s_info_ptrs, + s_info_size - s_info_ptrs); + + _mesa_reference_shader_program_data(ctx, &glprog->sh.data, prog->data); + _mesa_reference_program(ctx, &linked->Program, glprog); + prog->_LinkedShaders[stage] = linked; +} + +extern "C" void +serialize_glsl_program(struct blob *blob, struct gl_context *ctx, + struct gl_shader_program *prog) +{ + blob_write_bytes(blob, prog->data->sha1, sizeof(prog->data->sha1)); + + write_uniforms(blob, prog); + + write_hash_tables(blob, prog); + + blob_write_uint32(blob, prog->data->Version); + blob_write_uint32(blob, prog->data->linked_stages); + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = prog->_LinkedShaders[i]; + if (sh) { + write_shader_metadata(blob, sh); + + if (sh->Program->info.name) + blob_write_string(blob, sh->Program->info.name); + else + blob_write_string(blob, ""); + + if (sh->Program->info.label) + blob_write_string(blob, sh->Program->info.label); + else + blob_write_string(blob, ""); + + size_t s_info_size, s_info_ptrs; + get_shader_info_and_pointer_sizes(&s_info_size, &s_info_ptrs, + &sh->Program->info); + + /* Store shader info */ + blob_write_bytes(blob, + ((char *) &sh->Program->info) + s_info_ptrs, + s_info_size - s_info_ptrs); + } + } + + write_xfb(blob, prog); + + write_uniform_remap_tables(blob, prog); + + write_atomic_buffers(blob, prog); + + write_buffer_blocks(blob, prog); + + write_subroutines(blob, prog); + + write_program_resource_list(blob, prog); +} + +extern "C" bool +deserialize_glsl_program(struct blob_reader *blob, struct gl_context *ctx, + struct gl_shader_program *prog) +{ + /* Fixed function programs generated by Mesa can't be serialized. */ + if (prog->Name == 0) + return false; + + assert(prog->data->UniformStorage == NULL); + + blob_copy_bytes(blob, prog->data->sha1, sizeof(prog->data->sha1)); + + read_uniforms(blob, prog); + + read_hash_tables(blob, prog); + + prog->data->Version = blob_read_uint32(blob); + prog->data->linked_stages = blob_read_uint32(blob); + + unsigned mask = prog->data->linked_stages; + while (mask) { + const int j = u_bit_scan(&mask); + create_linked_shader_and_program(ctx, (gl_shader_stage) j, prog, + blob); + } + + read_xfb(blob, prog); + + read_uniform_remap_tables(blob, prog); + + read_atomic_buffers(blob, prog); + + read_buffer_blocks(blob, prog); + + read_subroutines(blob, prog); + + read_program_resource_list(blob, prog); + + return !blob->overrun; +} diff --git a/lib/mesa/src/compiler/glsl/serialize.h b/lib/mesa/src/compiler/glsl/serialize.h new file mode 100644 index 000000000..789e307e9 --- /dev/null +++ b/lib/mesa/src/compiler/glsl/serialize.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef GLSL_SERIALIZE +#define GLSL_SERIALIZE + +#include <stdbool.h> + +struct blob; +struct blob_reader; +struct gl_context; +struct gl_shader_program; + +#ifdef __cplusplus +extern "C" { +#endif + +void +serialize_glsl_program(struct blob *blob, struct gl_context *ctx, + struct gl_shader_program *prog); + +bool +deserialize_glsl_program(struct blob_reader *blob, struct gl_context *ctx, + struct gl_shader_program *prog); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* GLSL_SERIALIZE */ diff --git a/lib/mesa/src/compiler/glsl/tests/.deps/cache_test-cache_test.Po b/lib/mesa/src/compiler/glsl/tests/.deps/cache_test-cache_test.Po new file mode 100644 index 000000000..9ce06a81e --- /dev/null +++ b/lib/mesa/src/compiler/glsl/tests/.deps/cache_test-cache_test.Po @@ -0,0 +1 @@ +# dummy diff --git a/lib/mesa/src/compiler/glsl/tests/array_refcount_test.cpp b/lib/mesa/src/compiler/glsl/tests/array_refcount_test.cpp index 0d8f4521c..45c204dc2 100644 --- a/lib/mesa/src/compiler/glsl/tests/array_refcount_test.cpp +++ b/lib/mesa/src/compiler/glsl/tests/array_refcount_test.cpp @@ -160,7 +160,6 @@ validate_variables_in_hash_table(struct hash_table *ht, va_end(args); - struct hash_entry *entry; hash_table_foreach(ht, entry) { const ir_instruction *const ir = (ir_instruction *) entry->key; const ir_variable *const v = ir->as_variable(); diff --git a/lib/mesa/src/compiler/glsl/tests/lower_jump_cases.py b/lib/mesa/src/compiler/glsl/tests/lower_jump_cases.py new file mode 100644 index 000000000..1977f3a9b --- /dev/null +++ b/lib/mesa/src/compiler/glsl/tests/lower_jump_cases.py @@ -0,0 +1,643 @@ +# coding=utf-8 +# +# Copyright © 2011, 2018 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. + +from sexps import * + +def make_test_case(f_name, ret_type, body): + """Create a simple optimization test case consisting of a single + function with the given name, return type, and body. + + Global declarations are automatically created for any undeclared + variables that are referenced by the function. All undeclared + variables are assumed to be floats. + """ + check_sexp(body) + declarations = {} + def make_declarations(sexp, already_declared = ()): + if isinstance(sexp, list): + if len(sexp) == 2 and sexp[0] == 'var_ref': + if sexp[1] not in already_declared: + declarations[sexp[1]] = [ + 'declare', ['in'], 'float', sexp[1]] + elif len(sexp) == 4 and sexp[0] == 'assign': + assert sexp[2][0] == 'var_ref' + if sexp[2][1] not in already_declared: + declarations[sexp[2][1]] = [ + 'declare', ['out'], 'float', sexp[2][1]] + make_declarations(sexp[3], already_declared) + else: + already_declared = set(already_declared) + for s in sexp: + if isinstance(s, list) and len(s) >= 4 and \ + s[0] == 'declare': + already_declared.add(s[3]) + else: + make_declarations(s, already_declared) + make_declarations(body) + return list(declarations.values()) + \ + [['function', f_name, ['signature', ret_type, ['parameters'], body]]] + + +# The following functions can be used to build expressions. + +def const_float(value): + """Create an expression representing the given floating point value.""" + return ['constant', 'float', ['{0:.6f}'.format(value)]] + +def const_bool(value): + """Create an expression representing the given boolean value. + + If value is not a boolean, it is converted to a boolean. So, for + instance, const_bool(1) is equivalent to const_bool(True). + """ + return ['constant', 'bool', ['{0}'.format(1 if value else 0)]] + +def gt_zero(var_name): + """Create Construct the expression var_name > 0""" + return ['expression', 'bool', '<', const_float(0), ['var_ref', var_name]] + + +# The following functions can be used to build complex control flow +# statements. All of these functions return statement lists (even +# those which only create a single statement), so that statements can +# be sequenced together using the '+' operator. + +def return_(value = None): + """Create a return statement.""" + if value is not None: + return [['return', value]] + else: + return [['return']] + +def break_(): + """Create a break statement.""" + return ['break'] + +def continue_(): + """Create a continue statement.""" + return ['continue'] + +def simple_if(var_name, then_statements, else_statements = None): + """Create a statement of the form + + if (var_name > 0.0) { + <then_statements> + } else { + <else_statements> + } + + else_statements may be omitted. + """ + if else_statements is None: + else_statements = [] + check_sexp(then_statements) + check_sexp(else_statements) + return [['if', gt_zero(var_name), then_statements, else_statements]] + +def loop(statements): + """Create a loop containing the given statements as its loop + body. + """ + check_sexp(statements) + return [['loop', statements]] + +def declare_temp(var_type, var_name): + """Create a declaration of the form + + (declare (temporary) <var_type> <var_name) + """ + return [['declare', ['temporary'], var_type, var_name]] + +def assign_x(var_name, value): + """Create a statement that assigns <value> to the variable + <var_name>. The assignment uses the mask (x). + """ + check_sexp(value) + return [['assign', ['x'], ['var_ref', var_name], value]] + +def complex_if(var_prefix, statements): + """Create a statement of the form + + if (<var_prefix>a > 0.0) { + if (<var_prefix>b > 0.0) { + <statements> + } + } + + This is useful in testing jump lowering, because if <statements> + ends in a jump, lower_jumps.cpp won't try to combine this + construct with the code that follows it, as it might do for a + simple if. + + All variables used in the if statement are prefixed with + var_prefix. This can be used to ensure uniqueness. + """ + check_sexp(statements) + return simple_if(var_prefix + 'a', simple_if(var_prefix + 'b', statements)) + +def declare_execute_flag(): + """Create the statements that lower_jumps.cpp uses to declare and + initialize the temporary boolean execute_flag. + """ + return declare_temp('bool', 'execute_flag') + \ + assign_x('execute_flag', const_bool(True)) + +def declare_return_flag(): + """Create the statements that lower_jumps.cpp uses to declare and + initialize the temporary boolean return_flag. + """ + return declare_temp('bool', 'return_flag') + \ + assign_x('return_flag', const_bool(False)) + +def declare_return_value(): + """Create the statements that lower_jumps.cpp uses to declare and + initialize the temporary variable return_value. Assume that + return_value is a float. + """ + return declare_temp('float', 'return_value') + +def declare_break_flag(): + """Create the statements that lower_jumps.cpp uses to declare and + initialize the temporary boolean break_flag. + """ + return declare_temp('bool', 'break_flag') + \ + assign_x('break_flag', const_bool(False)) + +def lowered_return_simple(value = None): + """Create the statements that lower_jumps.cpp lowers a return + statement to, in situations where it does not need to clear the + execute flag. + """ + if value: + result = assign_x('return_value', value) + else: + result = [] + return result + assign_x('return_flag', const_bool(True)) + +def lowered_return(value = None): + """Create the statements that lower_jumps.cpp lowers a return + statement to, in situations where it needs to clear the execute + flag. + """ + return lowered_return_simple(value) + \ + assign_x('execute_flag', const_bool(False)) + +def lowered_continue(): + """Create the statement that lower_jumps.cpp lowers a continue + statement to. + """ + return assign_x('execute_flag', const_bool(False)) + +def lowered_break_simple(): + """Create the statement that lower_jumps.cpp lowers a break + statement to, in situations where it does not need to clear the + execute flag. + """ + return assign_x('break_flag', const_bool(True)) + +def lowered_break(): + """Create the statement that lower_jumps.cpp lowers a break + statement to, in situations where it needs to clear the execute + flag. + """ + return lowered_break_simple() + assign_x('execute_flag', const_bool(False)) + +def if_execute_flag(statements): + """Wrap statements in an if test so that they will only execute if + execute_flag is True. + """ + check_sexp(statements) + return [['if', ['var_ref', 'execute_flag'], statements, []]] + +def if_return_flag(then_statements, else_statements): + """Wrap statements in an if test with return_flag as the condition. + """ + check_sexp(then_statements) + check_sexp(else_statements) + return [['if', ['var_ref', 'return_flag'], then_statements, else_statements]] + +def if_not_return_flag(statements): + """Wrap statements in an if test so that they will only execute if + return_flag is False. + """ + check_sexp(statements) + return [['if', ['var_ref', 'return_flag'], [], statements]] + +def final_return(): + """Create the return statement that lower_jumps.cpp places at the + end of a function when lowering returns. + """ + return [['return', ['var_ref', 'return_value']]] + +def final_break(): + """Create the conditional break statement that lower_jumps.cpp + places at the end of a function when lowering breaks. + """ + return [['if', ['var_ref', 'break_flag'], break_(), []]] + +def bash_quote(*args): + """Quote the arguments appropriately so that bash will understand + each argument as a single word. + """ + def quote_word(word): + for c in word: + if not (c.isalpha() or c.isdigit() or c in '@%_-+=:,./'): + break + else: + if not word: + return "''" + return word + return "'{0}'".format(word.replace("'", "'\"'\"'")) + return ' '.join(quote_word(word) for word in args) + +def create_test_case(input_sexp, expected_sexp, test_name, + pull_out_jumps=False, lower_sub_return=False, + lower_main_return=False, lower_continue=False, + lower_break=False): + """Create a test case that verifies that do_lower_jumps transforms + the given code in the expected way. + """ + check_sexp(input_sexp) + check_sexp(expected_sexp) + input_str = sexp_to_string(sort_decls(input_sexp)) + expected_output = sexp_to_string(sort_decls(expected_sexp)) # XXX: don't stringify this + optimization = ( + 'do_lower_jumps({0:d}, {1:d}, {2:d}, {3:d}, {4:d})'.format( + pull_out_jumps, lower_sub_return, lower_main_return, + lower_continue, lower_break)) + + return (test_name, optimization, input_str, expected_output) + +def test_lower_returns_main(): + """Test that do_lower_jumps respects the lower_main_return flag in deciding + whether to lower returns in the main function. + """ + input_sexp = make_test_case('main', 'void', ( + complex_if('', return_()) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_execute_flag() + + declare_return_flag() + + complex_if('', lowered_return()) + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_returns_main_true', + lower_main_return=True) + yield create_test_case( + input_sexp, input_sexp, 'lower_returns_main_false', + lower_main_return=False) + +def test_lower_returns_sub(): + """Test that do_lower_jumps respects the lower_sub_return flag in deciding + whether to lower returns in subroutines. + """ + input_sexp = make_test_case('sub', 'void', ( + complex_if('', return_()) + )) + expected_sexp = make_test_case('sub', 'void', ( + declare_execute_flag() + + declare_return_flag() + + complex_if('', lowered_return()) + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_returns_sub_true', + lower_sub_return=True) + yield create_test_case( + input_sexp, input_sexp, 'lower_returns_sub_false', + lower_sub_return=False) + +def test_lower_returns_1(): + """Test that a void return at the end of a function is eliminated.""" + input_sexp = make_test_case('main', 'void', ( + assign_x('a', const_float(1)) + + return_() + )) + expected_sexp = make_test_case('main', 'void', ( + assign_x('a', const_float(1)) + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_returns_1', lower_main_return=True) + +def test_lower_returns_2(): + """Test that lowering is not performed on a non-void return at the end of + subroutine. + """ + input_sexp = make_test_case('sub', 'float', ( + assign_x('a', const_float(1)) + + return_(const_float(1)) + )) + yield create_test_case( + input_sexp, input_sexp, 'lower_returns_2', lower_sub_return=True) + +def test_lower_returns_3(): + """Test lowering of returns when there is one nested inside a complex + structure of ifs, and one at the end of a function. + + In this case, the latter return needs to be lowered because it will not be + at the end of the function once the final return is inserted. + """ + input_sexp = make_test_case('sub', 'float', ( + complex_if('', return_(const_float(1))) + + return_(const_float(2)) + )) + expected_sexp = make_test_case('sub', 'float', ( + declare_execute_flag() + + declare_return_value() + + declare_return_flag() + + complex_if('', lowered_return(const_float(1))) + + if_execute_flag(lowered_return(const_float(2))) + + final_return() + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_returns_3', lower_sub_return=True) + +def test_lower_returns_4(): + """Test that returns are properly lowered when they occur in both branches + of an if-statement. + """ + input_sexp = make_test_case('sub', 'float', ( + simple_if('a', return_(const_float(1)), + return_(const_float(2))) + )) + expected_sexp = make_test_case('sub', 'float', ( + declare_execute_flag() + + declare_return_value() + + declare_return_flag() + + simple_if('a', lowered_return(const_float(1)), + lowered_return(const_float(2))) + + final_return() + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_returns_4', lower_sub_return=True) + +def test_lower_unified_returns(): + """If both branches of an if statement end in a return, and pull_out_jumps + is True, then those returns should be lifted outside the if and then + properly lowered. + + Verify that this lowering occurs during the same pass as the lowering of + other returns by checking that extra temporary variables aren't generated. + """ + input_sexp = make_test_case('main', 'void', ( + complex_if('a', return_()) + + simple_if('b', simple_if('c', return_(), return_())) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_execute_flag() + + declare_return_flag() + + complex_if('a', lowered_return()) + + if_execute_flag(simple_if('b', (simple_if('c', [], []) + + lowered_return()))) + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_unified_returns', + lower_main_return=True, pull_out_jumps=True) + +def test_lower_pulled_out_jump(): + doc_string = """If one branch of an if ends in a jump, and control cannot + fall out the bottom of the other branch, and pull_out_jumps is + True, then the jump is lifted outside the if. + + Verify that this lowering occurs during the same pass as the + lowering of other jumps by checking that extra temporary + variables aren't generated. + """ + input_sexp = make_test_case('main', 'void', ( + complex_if('a', return_()) + + loop(simple_if('b', simple_if('c', break_(), continue_()), + return_())) + + assign_x('d', const_float(1)) + )) + # Note: optimization produces two other effects: the break + # gets lifted out of the if statements, and the code after the + # loop gets guarded so that it only executes if the return + # flag is clear. + expected_sexp = make_test_case('main', 'void', ( + declare_execute_flag() + + declare_return_flag() + + complex_if('a', lowered_return()) + + if_execute_flag( + loop(simple_if('b', simple_if('c', [], continue_()), + lowered_return_simple()) + + break_()) + + + if_return_flag(assign_x('return_flag', const_bool(1)) + + assign_x('execute_flag', const_bool(0)), + assign_x('d', const_float(1)))) + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_pulled_out_jump', + lower_main_return=True, pull_out_jumps=True) + +def test_lower_breaks_1(): + """If a loop contains an unconditional break at the bottom of it, it should + not be lowered. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + break_()) + )) + expected_sexp = input_sexp + yield create_test_case( + input_sexp, expected_sexp, 'lower_breaks_1', lower_break=True) + +def test_lower_breaks_2(): + """If a loop contains a conditional break at the bottom of it, it should + not be lowered if it is in the then-clause. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + simple_if('b', break_())) + )) + expected_sexp = input_sexp + yield create_test_case( + input_sexp, expected_sexp, 'lower_breaks_2', lower_break=True) + +def test_lower_breaks_3(): + """If a loop contains a conditional break at the bottom of it, it should + not be lowered if it is in the then-clause, even if there are statements + preceding the break. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + simple_if('b', (assign_x('c', const_float(1)) + + break_()))) + )) + expected_sexp = input_sexp + yield create_test_case( + input_sexp, expected_sexp, 'lower_breaks_3', lower_break=True) + +def test_lower_breaks_4(): + """If a loop contains a conditional break at the bottom of it, it should + not be lowered if it is in the else-clause. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + simple_if('b', [], break_())) + )) + expected_sexp = input_sexp + yield create_test_case( + input_sexp, expected_sexp, 'lower_breaks_4', lower_break=True) + +def test_lower_breaks_5(): + """If a loop contains a conditional break at the bottom of it, it should + not be lowered if it is in the else-clause, even if there are statements + preceding the break. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + simple_if('b', [], (assign_x('c', const_float(1)) + + break_()))) + )) + expected_sexp = input_sexp + yield create_test_case( + input_sexp, expected_sexp, 'lower_breaks_5', lower_break=True) + +def test_lower_breaks_6(): + """If a loop contains conditional breaks and continues, and ends in an + unconditional break, then the unconditional break needs to be lowered, + because it will no longer be at the end of the loop after the final break + is added. + """ + input_sexp = make_test_case('main', 'void', ( + loop(simple_if('a', (complex_if('b', continue_()) + + complex_if('c', break_()))) + + break_()) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_break_flag() + + loop(declare_execute_flag() + + simple_if( + 'a', + (complex_if('b', lowered_continue()) + + if_execute_flag( + complex_if('c', lowered_break())))) + + if_execute_flag(lowered_break_simple()) + + final_break()) + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_breaks_6', lower_break=True, + lower_continue=True) + +def test_lower_guarded_conditional_break(): + """Normally a conditional break at the end of a loop isn't lowered, however + if the conditional break gets placed inside an if(execute_flag) because of + earlier lowering of continues, then the break needs to be lowered. + """ + input_sexp = make_test_case('main', 'void', ( + loop(complex_if('a', continue_()) + + simple_if('b', break_())) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_break_flag() + + loop(declare_execute_flag() + + complex_if('a', lowered_continue()) + + if_execute_flag(simple_if('b', lowered_break())) + + final_break()) + )) + yield create_test_case( + input_sexp, expected_sexp, 'lower_guarded_conditional_break', + lower_break=True, lower_continue=True) + +def test_remove_continue_at_end_of_loop(): + """Test that a redundant continue-statement at the end of a loop is + removed. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + continue_()) + )) + expected_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1))) + )) + yield create_test_case(input_sexp, expected_sexp, 'remove_continue_at_end_of_loop') + +def test_lower_return_void_at_end_of_loop(): + """Test that a return of void at the end of a loop is properly lowered.""" + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + return_()) + + assign_x('b', const_float(2)) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_execute_flag() + + declare_return_flag() + + loop(assign_x('a', const_float(1)) + + lowered_return_simple() + + break_()) + + if_return_flag(assign_x('return_flag', const_bool(1)) + + assign_x('execute_flag', const_bool(0)), + assign_x('b', const_float(2))) + )) + yield create_test_case( + input_sexp, input_sexp, 'return_void_at_end_of_loop_lower_nothing') + yield create_test_case( + input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return', + lower_main_return=True) + yield create_test_case( + input_sexp, expected_sexp, + 'return_void_at_end_of_loop_lower_return_and_break', + lower_main_return=True, lower_break=True) + +def test_lower_return_non_void_at_end_of_loop(): + """Test that a non-void return at the end of a loop is properly lowered.""" + input_sexp = make_test_case('sub', 'float', ( + loop(assign_x('a', const_float(1)) + + return_(const_float(2))) + + assign_x('b', const_float(3)) + + return_(const_float(4)) + )) + expected_sexp = make_test_case('sub', 'float', ( + declare_execute_flag() + + declare_return_value() + + declare_return_flag() + + loop(assign_x('a', const_float(1)) + + lowered_return_simple(const_float(2)) + + break_()) + + if_return_flag(assign_x('return_value', '(var_ref return_value)') + + assign_x('return_flag', const_bool(1)) + + assign_x('execute_flag', const_bool(0)), + assign_x('b', const_float(3)) + + lowered_return(const_float(4))) + + final_return() + )) + yield create_test_case( + input_sexp, input_sexp, 'return_non_void_at_end_of_loop_lower_nothing') + yield create_test_case( + input_sexp, expected_sexp, + 'return_non_void_at_end_of_loop_lower_return', lower_sub_return=True) + yield create_test_case( + input_sexp, expected_sexp, + 'return_non_void_at_end_of_loop_lower_return_and_break', + lower_sub_return=True, lower_break=True) + +CASES = [ + test_lower_breaks_1, test_lower_breaks_2, test_lower_breaks_3, + test_lower_breaks_4, test_lower_breaks_5, test_lower_breaks_6, + test_lower_guarded_conditional_break, test_lower_pulled_out_jump, + test_lower_return_non_void_at_end_of_loop, + test_lower_return_void_at_end_of_loop, + test_lower_returns_1, test_lower_returns_2, test_lower_returns_3, + test_lower_returns_4, test_lower_returns_main, test_lower_returns_sub, + test_lower_unified_returns, test_remove_continue_at_end_of_loop, +] diff --git a/lib/mesa/src/compiler/glsl/tests/meson.build b/lib/mesa/src/compiler/glsl/tests/meson.build index 27f34075b..2a41e30a2 100644 --- a/lib/mesa/src/compiler/glsl/tests/meson.build +++ b/lib/mesa/src/compiler/glsl/tests/meson.build @@ -18,59 +18,87 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -glsl_blob_test = executable( +test( 'blob_test', - 'blob_test.c', - c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args], - include_directories : [inc_common, inc_compiler], - link_with : [libglsl], + executable( + 'blob_test', + 'blob_test.c', + c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args], + include_directories : [inc_common, inc_compiler], + link_with : [libglsl], + ) ) -glsl_cache_test = executable( +test( 'cache_test', - 'cache_test.c', - c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args], - include_directories : [inc_common, inc_glsl], - link_with : [libglsl], - dependencies : [dep_clock, dep_thread], + executable( + 'cache_test', + 'cache_test.c', + c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args], + include_directories : [inc_common, inc_glsl], + link_with : [libglsl], + dependencies : [dep_clock, dep_thread], + ) ) -glsl_general_ir_test = executable( + +test( 'general_ir_test', - ['array_refcount_test.cpp', 'builtin_variable_test.cpp', - 'invalidate_locations_test.cpp', 'general_ir_test.cpp', - 'lower_int64_test.cpp', 'opt_add_neg_to_sub_test.cpp', 'varyings_test.cpp', - ir_expression_operation_h], - cpp_args : [cpp_vis_args, cpp_msvc_compat_args], - include_directories : [inc_common, inc_glsl], - link_with : [libglsl, libglsl_standalone, libglsl_util], - dependencies : [dep_clock, dep_thread, idep_gtest], + executable( + 'general_ir_test', + ['array_refcount_test.cpp', 'builtin_variable_test.cpp', + 'invalidate_locations_test.cpp', 'general_ir_test.cpp', + 'lower_int64_test.cpp', 'opt_add_neg_to_sub_test.cpp', + 'varyings_test.cpp', ir_expression_operation_h], + cpp_args : [cpp_vis_args, cpp_msvc_compat_args], + include_directories : [inc_common, inc_glsl], + link_with : [libglsl, libglsl_standalone, libglsl_util], + dependencies : [dep_clock, dep_thread, idep_gtest], + ) ) -glsl_uniform_initializer_test = executable( +test( 'uniform_initializer_test', - ['copy_constant_to_storage_tests.cpp', 'set_uniform_initializer_tests.cpp', - 'uniform_initializer_utils.cpp', 'uniform_initializer_utils.h', - ir_expression_operation_h], - cpp_args : [cpp_vis_args, cpp_msvc_compat_args], - include_directories : [inc_common, inc_glsl], - link_with : [libglsl, libglsl_util], - dependencies : [dep_thread, idep_gtest], + executable( + 'uniform_initializer_test', + ['copy_constant_to_storage_tests.cpp', 'set_uniform_initializer_tests.cpp', + 'uniform_initializer_utils.cpp', 'uniform_initializer_utils.h', + ir_expression_operation_h], + cpp_args : [cpp_vis_args, cpp_msvc_compat_args], + include_directories : [inc_common, inc_glsl], + link_with : [libglsl, libglsl_util], + dependencies : [dep_thread, idep_gtest], + ) ) -glsl_sampler_types_test = executable( +test( 'sampler_types_test', - ['sampler_types_test.cpp', ir_expression_operation_h], - cpp_args : [cpp_vis_args, cpp_msvc_compat_args], - include_directories : [inc_common, inc_glsl], - link_with : [libglsl, libglsl_util], - dependencies : [dep_thread, idep_gtest], + executable( + 'sampler_types_test', + ['sampler_types_test.cpp', ir_expression_operation_h], + cpp_args : [cpp_vis_args, cpp_msvc_compat_args], + include_directories : [inc_common, inc_glsl], + link_with : [libglsl, libglsl_util], + dependencies : [dep_thread, idep_gtest], + ) ) -test('blob_test', glsl_blob_test) -test('cache_test', glsl_cache_test) -test('general_ir_test', glsl_general_ir_test) -test('uniform_initializer_test', glsl_uniform_initializer_test) -test('sampler_types_test', glsl_sampler_types_test) - -# TODO: figure out how to get the shell based tests to work? +test( + 'glsl compiler warnings', + prog_python, + args : [ + join_paths(meson.current_source_dir(), 'warnings_test.py'), + '--glsl-compiler', glsl_compiler, + '--test-directory', join_paths( + meson.source_root(), 'src', 'compiler', 'glsl', 'tests', 'warnings' + ), + ], +) +test( + 'glsl optimization', + prog_python, + args : [ + join_paths(meson.current_source_dir(), 'optimization_test.py'), + '--test-runner', glsl_test + ], +) diff --git a/lib/mesa/src/compiler/glsl/tests/optimization_test.py b/lib/mesa/src/compiler/glsl/tests/optimization_test.py new file mode 100644 index 000000000..15ca3216f --- /dev/null +++ b/lib/mesa/src/compiler/glsl/tests/optimization_test.py @@ -0,0 +1,96 @@ +# encoding=utf-8 +# Copyright © 2018 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 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. + +"""Script to generate and run glsl optimization tests.""" + +from __future__ import print_function +import argparse +import difflib +import subprocess +import sys + +import sexps +import lower_jump_cases + + +def arg_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--test-runner', + required=True, + help='The glsl_test binary.') + return parser.parse_args() + + +def compare(actual, expected): + """Compare the s-expresions and return a diff if they are different.""" + actual = sexps.sort_decls(sexps.parse_sexp(actual)) + expected = sexps.sort_decls(sexps.parse_sexp(expected)) + + if actual == expected: + return None + + actual = sexps.sexp_to_string(actual) + expected = sexps.sexp_to_string(expected) + + return difflib.unified_diff(expected.splitlines(), actual.splitlines()) + + +def main(): + """Generate each test and report pass or fail.""" + args = arg_parser() + + total = 0 + passes = 0 + + for gen in lower_jump_cases.CASES: + for name, opt, source, expected in gen(): + total += 1 + print('{}: '.format(name), end='') + proc = subprocess.Popen( + [args.test_runner, 'optpass', '--quiet', '--input-ir', opt], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + out, err = proc.communicate(source.encode('utf-8')) + out = out.decode('utf-8') + err = err.decode('utf-8') + if err: + print('FAIL') + print('Unexpected output on stderr: {}'.format(err), + file=sys.stdout) + continue + + result = compare(out, expected) + if result is not None: + print('FAIL') + for l in result: + print(l, file=sys.stderr) + else: + print('PASS') + passes += 1 + + print('{}/{} tests returned correct results'.format(passes, total)) + exit(0 if passes == total else 1) + + +if __name__ == '__main__': + main() diff --git a/lib/mesa/src/compiler/glsl/tests/sexps.py b/lib/mesa/src/compiler/glsl/tests/sexps.py index a714af8d2..7939b42f9 100644 --- a/lib/mesa/src/compiler/glsl/tests/sexps.py +++ b/lib/mesa/src/compiler/glsl/tests/sexps.py @@ -28,6 +28,11 @@ # as ['constant', 'float', ['1.000000']]. import re +import sys +if sys.version_info >= (3, 0, 0): + STRING_TYPE = str +else: + STRING_TYPE = unicode def check_sexp(sexp): """Verify that the argument is a proper sexp. @@ -39,7 +44,7 @@ def check_sexp(sexp): if isinstance(sexp, list): for s in sexp: check_sexp(s) - elif not isinstance(sexp, basestring): + elif not isinstance(sexp, (STRING_TYPE, bytes)): raise Exception('Not a sexp: {0!r}'.format(sexp)) def parse_sexp(sexp): @@ -70,8 +75,10 @@ def sexp_to_string(sexp): """Convert a sexp, represented as nested lists containing strings, into a single string of the form parseable by mesa. """ - if isinstance(sexp, basestring): + if isinstance(sexp, STRING_TYPE): return sexp + if isinstance(sexp, bytes): + return sexp.encode('utf-8') assert isinstance(sexp, list) result = '' for s in sexp: diff --git a/lib/mesa/src/compiler/glsl/tests/warnings_test.py b/lib/mesa/src/compiler/glsl/tests/warnings_test.py new file mode 100644 index 000000000..2c4fa5a0d --- /dev/null +++ b/lib/mesa/src/compiler/glsl/tests/warnings_test.py @@ -0,0 +1,73 @@ +# encoding=utf-8 +# Copyright © 2017 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 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. + +from __future__ import print_function +import argparse +import os +import subprocess + + +def arg_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--glsl-compiler', + required=True, + help='Path to the standalone glsl compiler') + parser.add_argument( + '--test-directory', + required=True, + help='Directory containing tests to run.') + return parser.parse_args() + + +def main(): + args = arg_parser() + files = [f for f in os.listdir(args.test_directory) if f.endswith('.vert')] + passed = 0 + + if not files: + print('Could not find any tests') + exit(1) + + print('====== Testing compilation output ======') + for file in files: + print('Testing {} ...'.format(file), end='') + file = os.path.join(args.test_directory, file) + + with open('{}.expected'.format(file), 'rb') as f: + expected = f.read().strip() + + actual = subprocess.check_output( + [args.glsl_compiler, '--just-log', '--version', '150', file] + ).strip() + + if actual == expected: + print('PASS') + passed += 1 + else: + print('FAIL') + + print('{}/{} tests returned correct results'.format(passed, len(files))) + exit(0 if passed == len(files) else 1) + + +if __name__ == '__main__': + main() |