summaryrefslogtreecommitdiff
path: root/lib/mesa/src/compiler/glsl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mesa/src/compiler/glsl')
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir.h47
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_link_atomics.c282
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_link_uniform_initializers.c292
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_link_uniforms.c529
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_link_xfb.c316
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_linker.c71
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_linker.h53
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_lower_atomics.c178
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_lower_samplers.c184
-rw-r--r--lib/mesa/src/compiler/glsl/gl_nir_lower_samplers_as_deref.c303
-rw-r--r--lib/mesa/src/compiler/glsl/glcpp/glcpp-parse.h4
-rw-r--r--lib/mesa/src/compiler/glsl/glcpp/meson.build75
-rw-r--r--lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c4
-rw-r--r--lib/mesa/src/compiler/glsl/glcpp/tests/122-redefine-whitespace.c.expected10
-rw-r--r--lib/mesa/src/compiler/glsl/glcpp/tests/149-hex-const-uppercase-prefix.c5
-rw-r--r--lib/mesa/src/compiler/glsl/glcpp/tests/149-hex-const-uppercase-prefix.c.expected5
-rw-r--r--lib/mesa/src/compiler/glsl/glcpp/tests/glcpp_test.py235
-rw-r--r--lib/mesa/src/compiler/glsl/ir_function_detect_recursion.cpp1
-rw-r--r--lib/mesa/src/compiler/glsl/link_functions.cpp2
-rw-r--r--lib/mesa/src/compiler/glsl/linker_util.cpp119
-rw-r--r--lib/mesa/src/compiler/glsl/linker_util.h69
-rw-r--r--lib/mesa/src/compiler/glsl/loop_analysis.cpp23
-rw-r--r--lib/mesa/src/compiler/glsl/lower_shared_reference.cpp27
-rw-r--r--lib/mesa/src/compiler/glsl/lower_tess_level.cpp1
-rw-r--r--lib/mesa/src/compiler/glsl/lower_vector_derefs.cpp14
-rw-r--r--lib/mesa/src/compiler/glsl/meson.build263
-rw-r--r--lib/mesa/src/compiler/glsl/opt_constant_variable.cpp1
-rw-r--r--lib/mesa/src/compiler/glsl/opt_dead_builtin_varyings.cpp19
-rw-r--r--lib/mesa/src/compiler/glsl/opt_dead_code.cpp1
-rw-r--r--lib/mesa/src/compiler/glsl/opt_swizzle.cpp119
-rw-r--r--lib/mesa/src/compiler/glsl/s_expression.cpp2
-rw-r--r--lib/mesa/src/compiler/glsl/serialize.cpp1292
-rw-r--r--lib/mesa/src/compiler/glsl/serialize.h50
-rw-r--r--lib/mesa/src/compiler/glsl/tests/.deps/cache_test-cache_test.Po1
-rw-r--r--lib/mesa/src/compiler/glsl/tests/array_refcount_test.cpp1
-rw-r--r--lib/mesa/src/compiler/glsl/tests/lower_jump_cases.py643
-rw-r--r--lib/mesa/src/compiler/glsl/tests/meson.build110
-rw-r--r--lib/mesa/src/compiler/glsl/tests/optimization_test.py96
-rw-r--r--lib/mesa/src/compiler/glsl/tests/sexps.py11
-rw-r--r--lib/mesa/src/compiler/glsl/tests/warnings_test.py73
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,
+ &current_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 = &params->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()