| /* |
| * 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 "nir_xfb_info.h" |
| #include "gl_nir_linker.h" |
| #include "linker_util.h" |
| #include "main/context.h" |
| #include "util/u_math.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. |
| */ |
| |
| 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); |
| |
| nir_xfb_info *xfb_info = NULL; |
| nir_xfb_varyings_info *varyings_info = NULL; |
| |
| /* Find last stage before fragment shader */ |
| for (int stage = MESA_SHADER_FRAGMENT - 1; stage >= 0; stage--) { |
| struct gl_linked_shader *sh = prog->_LinkedShaders[stage]; |
| |
| if (sh && stage != MESA_SHADER_TESS_CTRL) { |
| xfb_info = nir_gather_xfb_info_with_varyings(sh->Program->nir, NULL, &varyings_info); |
| break; |
| } |
| } |
| |
| struct gl_transform_feedback_info *linked_xfb = |
| rzalloc(xfb_prog, struct gl_transform_feedback_info); |
| xfb_prog->sh.LinkedTransformFeedback = linked_xfb; |
| |
| if (!xfb_info) { |
| prog->TransformFeedback.NumVarying = 0; |
| linked_xfb->NumOutputs = 0; |
| linked_xfb->NumVarying = 0; |
| linked_xfb->ActiveBuffers = 0; |
| return; |
| } |
| |
| for (unsigned buf = 0; buf < MAX_FEEDBACK_BUFFERS; buf++) |
| prog->TransformFeedback.BufferStride[buf] = xfb_info->buffers[buf].stride; |
| |
| prog->TransformFeedback.NumVarying = varyings_info->varying_count; |
| prog->TransformFeedback.VaryingNames = |
| malloc(sizeof(GLchar *) * varyings_info->varying_count); |
| |
| linked_xfb->Outputs = |
| rzalloc_array(xfb_prog, |
| struct gl_transform_feedback_output, |
| xfb_info->output_count); |
| linked_xfb->NumOutputs = xfb_info->output_count; |
| |
| linked_xfb->Varyings = |
| rzalloc_array(xfb_prog, |
| struct gl_transform_feedback_varying_info, |
| varyings_info->varying_count); |
| linked_xfb->NumVarying = varyings_info->varying_count; |
| |
| int buffer_index = 0; /* Corresponds to GL_TRANSFORM_FEEDBACK_BUFFER_INDEX */ |
| int xfb_buffer = |
| (varyings_info->varying_count > 0) ? |
| xfb_info->outputs[0].buffer : 0; |
| |
| for (unsigned i = 0; i < varyings_info->varying_count; i++) { |
| nir_xfb_varying_info *xfb_varying = &varyings_info->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; |
| |
| if (xfb_buffer != xfb_varying->buffer) { |
| buffer_index++; |
| xfb_buffer = xfb_varying->buffer; |
| } |
| |
| 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(xfb_varying->type); |
| varying->BufferIndex = buffer_index; |
| varying->Size = glsl_type_is_array(xfb_varying->type) ? |
| glsl_get_length(xfb_varying->type) : 1; |
| varying->Offset = xfb_varying->offset; |
| } |
| |
| for (unsigned i = 0; i < xfb_info->output_count; i++) { |
| nir_xfb_output_info *xfb_output = &xfb_info->outputs[i]; |
| |
| struct gl_transform_feedback_output *output = |
| linked_xfb->Outputs + i; |
| |
| output->OutputRegister = xfb_output->location; |
| output->OutputBuffer = xfb_output->buffer; |
| output->NumComponents = util_bitcount(xfb_output->component_mask); |
| output->StreamId = xfb_info->buffer_to_stream[xfb_output->buffer]; |
| output->DstOffset = xfb_output->offset / 4; |
| output->ComponentOffset = xfb_output->component_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 (xfb_info->buffers[buf].stride > 0) { |
| linked_xfb->Buffers[buf].Stride = xfb_info->buffers[buf].stride / 4; |
| linked_xfb->Buffers[buf].NumVaryings = xfb_info->buffers[buf].varying_count; |
| buffers |= 1 << buf; |
| } |
| } |
| |
| linked_xfb->ActiveBuffers = buffers; |
| |
| ralloc_free(xfb_info); |
| } |