| /* |
| * 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 |
| */ |
| |
| static bool |
| add_vars_from_list(const struct gl_context *ctx, |
| struct gl_shader_program *prog, struct set *resource_set, |
| const struct exec_list *var_list, unsigned stage, |
| GLenum programInterface) |
| { |
| nir_foreach_variable(var, var_list) { |
| if (var->data.how_declared == nir_var_hidden) |
| continue; |
| |
| int loc_bias = 0; |
| switch(var->data.mode) { |
| case nir_var_system_value: |
| case nir_var_shader_in: |
| if (programInterface != GL_PROGRAM_INPUT) |
| continue; |
| loc_bias = (stage == MESA_SHADER_VERTEX) ? VERT_ATTRIB_GENERIC0 |
| : VARYING_SLOT_VAR0; |
| break; |
| case nir_var_shader_out: |
| if (programInterface != GL_PROGRAM_OUTPUT) |
| continue; |
| loc_bias = (stage == MESA_SHADER_FRAGMENT) ? FRAG_RESULT_DATA0 |
| : VARYING_SLOT_VAR0; |
| break; |
| default: |
| continue; |
| } |
| |
| if (var->data.patch) |
| loc_bias = VARYING_SLOT_PATCH0; |
| |
| struct gl_shader_variable *sh_var = |
| rzalloc(prog, struct gl_shader_variable); |
| |
| /* In the ARB_gl_spirv spec, names are considered optional debug info, so |
| * the linker needs to work without them. Returning them is optional. |
| * For simplicity, we ignore names. |
| */ |
| sh_var->name = NULL; |
| sh_var->type = var->type; |
| sh_var->location = var->data.location - loc_bias; |
| sh_var->index = var->data.index; |
| |
| if (!link_util_add_program_resource(prog, resource_set, |
| programInterface, |
| sh_var, 1 << stage)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| static bool |
| add_interface_variables(const struct gl_context *ctx, |
| struct gl_shader_program *prog, |
| struct set *resource_set, |
| unsigned stage, GLenum programInterface) |
| { |
| struct gl_linked_shader *sh = prog->_LinkedShaders[stage]; |
| if (!sh) |
| return true; |
| |
| nir_shader *nir = sh->Program->nir; |
| assert(nir); |
| |
| switch (programInterface) { |
| case GL_PROGRAM_INPUT: { |
| bool result = add_vars_from_list(ctx, prog, resource_set, |
| &nir->inputs, stage, programInterface); |
| result &= add_vars_from_list(ctx, prog, resource_set, &nir->system_values, |
| stage, programInterface); |
| return result; |
| } |
| case GL_PROGRAM_OUTPUT: |
| return add_vars_from_list(ctx, prog, resource_set, &nir->outputs, stage, |
| programInterface); |
| default: |
| assert("!Should not get here"); |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* TODO: as we keep adding features, this method is becoming more and more |
| * similar to its GLSL counterpart at linker.cpp. Eventually it would be good |
| * to check if they could be refactored, and reduce code duplication somehow |
| */ |
| 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; |
| } |
| |
| int input_stage = MESA_SHADER_STAGES, output_stage = 0; |
| |
| /* Determine first input and final output stage. These are used to |
| * detect which variables should be enumerated in the resource list |
| * for GL_PROGRAM_INPUT and GL_PROGRAM_OUTPUT. |
| */ |
| for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { |
| if (!prog->_LinkedShaders[i]) |
| continue; |
| if (input_stage == MESA_SHADER_STAGES) |
| input_stage = i; |
| output_stage = i; |
| } |
| |
| /* Empty shader, no resources. */ |
| if (input_stage == MESA_SHADER_STAGES && output_stage == 0) |
| return; |
| |
| struct set *resource_set = _mesa_pointer_set_create(NULL); |
| |
| /* Add inputs and outputs to the resource list. */ |
| if (!add_interface_variables(ctx, prog, resource_set, input_stage, |
| GL_PROGRAM_INPUT)) { |
| return; |
| } |
| |
| if (!add_interface_variables(ctx, prog, resource_set, output_stage, |
| GL_PROGRAM_OUTPUT)) { |
| return; |
| } |
| |
| /* Add transform feedback varyings and buffers. */ |
| if (prog->last_vert_prog) { |
| struct gl_transform_feedback_info *linked_xfb = |
| prog->last_vert_prog->sh.LinkedTransformFeedback; |
| |
| /* Add varyings. */ |
| if (linked_xfb->NumVarying > 0) { |
| for (int i = 0; i < linked_xfb->NumVarying; i++) { |
| if (!link_util_add_program_resource(prog, resource_set, |
| GL_TRANSFORM_FEEDBACK_VARYING, |
| &linked_xfb->Varyings[i], 0)) |
| return; |
| } |
| } |
| |
| /* Add buffers. */ |
| for (unsigned i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) { |
| if ((linked_xfb->ActiveBuffers >> i) & 1) { |
| linked_xfb->Buffers[i].Binding = i; |
| if (!link_util_add_program_resource(prog, resource_set, |
| GL_TRANSFORM_FEEDBACK_BUFFER, |
| &linked_xfb->Buffers[i], 0)) |
| return; |
| } |
| } |
| } |
| |
| /* Add uniforms |
| * |
| * Here, it is expected that nir_link_uniforms() has already been |
| * called, so that UniformStorage table is already available. |
| */ |
| int top_level_array_base_offset = -1; |
| int top_level_array_size_in_bytes = -1; |
| int second_element_offset = -1; |
| int block_index = -1; |
| 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_should_add_buffer_variable(prog, uniform, |
| top_level_array_base_offset, |
| top_level_array_size_in_bytes, |
| second_element_offset, block_index)) |
| continue; |
| |
| |
| if (prog->data->UniformStorage[i].offset >= second_element_offset) { |
| top_level_array_base_offset = |
| prog->data->UniformStorage[i].offset; |
| |
| top_level_array_size_in_bytes = |
| prog->data->UniformStorage[i].top_level_array_size * |
| prog->data->UniformStorage[i].top_level_array_stride; |
| |
| /* Set or reset the second element offset. For non arrays this |
| * will be set to -1. |
| */ |
| second_element_offset = top_level_array_size_in_bytes ? |
| top_level_array_base_offset + |
| prog->data->UniformStorage[i].top_level_array_stride : -1; |
| } |
| block_index = uniform->block_index; |
| |
| |
| GLenum interface = uniform->is_shader_storage ? GL_BUFFER_VARIABLE : GL_UNIFORM; |
| if (!link_util_add_program_resource(prog, resource_set, interface, uniform, |
| uniform->active_shader_mask)) { |
| return; |
| } |
| } |
| |
| |
| for (unsigned i = 0; i < prog->data->NumUniformBlocks; i++) { |
| if (!link_util_add_program_resource(prog, resource_set, GL_UNIFORM_BLOCK, |
| &prog->data->UniformBlocks[i], |
| prog->data->UniformBlocks[i].stageref)) |
| return; |
| } |
| |
| for (unsigned i = 0; i < prog->data->NumShaderStorageBlocks; i++) { |
| if (!link_util_add_program_resource(prog, resource_set, GL_SHADER_STORAGE_BLOCK, |
| &prog->data->ShaderStorageBlocks[i], |
| prog->data->ShaderStorageBlocks[i].stageref)) |
| return; |
| } |
| |
| /* Add atomic counter buffers. */ |
| for (unsigned i = 0; i < prog->data->NumAtomicBuffers; i++) { |
| if (!link_util_add_program_resource(prog, resource_set, GL_ATOMIC_COUNTER_BUFFER, |
| &prog->data->AtomicBuffers[i], 0)) |
| return; |
| } |
| |
| _mesa_set_destroy(resource_set, NULL); |
| } |
| |
| bool |
| gl_nir_link(struct gl_context *ctx, struct gl_shader_program *prog, |
| const struct gl_nir_linker_options *options) |
| { |
| if (!gl_nir_link_uniform_blocks(ctx, prog)) |
| return false; |
| |
| if (!gl_nir_link_uniforms(ctx, prog, options->fill_parameters)) |
| return false; |
| |
| gl_nir_link_assign_atomic_counter_resources(ctx, prog); |
| gl_nir_link_assign_xfb_resources(ctx, prog); |
| |
| return true; |
| } |