• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * on the rights to use, copy, modify, merge, publish, distribute, sub
8  * license, and/or sell copies of the Software, and to permit persons to whom
9  * the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "glspirv.h"
25 #include "errors.h"
26 #include "shaderobj.h"
27 #include "mtypes.h"
28 
29 #include "compiler/nir/nir.h"
30 #include "compiler/spirv/nir_spirv.h"
31 
32 #include "program/program.h"
33 
34 #include "util/u_atomic.h"
35 
36 void
_mesa_spirv_module_reference(struct gl_spirv_module ** dest,struct gl_spirv_module * src)37 _mesa_spirv_module_reference(struct gl_spirv_module **dest,
38                              struct gl_spirv_module *src)
39 {
40    struct gl_spirv_module *old = *dest;
41 
42    if (old && p_atomic_dec_zero(&old->RefCount))
43       free(old);
44 
45    *dest = src;
46 
47    if (src)
48       p_atomic_inc(&src->RefCount);
49 }
50 
51 void
_mesa_shader_spirv_data_reference(struct gl_shader_spirv_data ** dest,struct gl_shader_spirv_data * src)52 _mesa_shader_spirv_data_reference(struct gl_shader_spirv_data **dest,
53                                   struct gl_shader_spirv_data *src)
54 {
55    struct gl_shader_spirv_data *old = *dest;
56 
57    if (old && p_atomic_dec_zero(&old->RefCount)) {
58       _mesa_spirv_module_reference(&(*dest)->SpirVModule, NULL);
59       ralloc_free(old);
60    }
61 
62    *dest = src;
63 
64    if (src)
65       p_atomic_inc(&src->RefCount);
66 }
67 
68 void
_mesa_spirv_shader_binary(struct gl_context * ctx,unsigned n,struct gl_shader ** shaders,const void * binary,size_t length)69 _mesa_spirv_shader_binary(struct gl_context *ctx,
70                           unsigned n, struct gl_shader **shaders,
71                           const void* binary, size_t length)
72 {
73    struct gl_spirv_module *module;
74    struct gl_shader_spirv_data *spirv_data;
75 
76    module = malloc(sizeof(*module) + length);
77    if (!module) {
78       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glShaderBinary");
79       return;
80    }
81 
82    p_atomic_set(&module->RefCount, 0);
83    module->Length = length;
84    memcpy(&module->Binary[0], binary, length);
85 
86    for (int i = 0; i < n; ++i) {
87       struct gl_shader *sh = shaders[i];
88 
89       spirv_data = rzalloc(NULL, struct gl_shader_spirv_data);
90       _mesa_shader_spirv_data_reference(&sh->spirv_data, spirv_data);
91       _mesa_spirv_module_reference(&spirv_data->SpirVModule, module);
92 
93       sh->CompileStatus = COMPILE_FAILURE;
94 
95       free((void *)sh->Source);
96       sh->Source = NULL;
97       free((void *)sh->FallbackSource);
98       sh->FallbackSource = NULL;
99 
100       ralloc_free(sh->ir);
101       sh->ir = NULL;
102       ralloc_free(sh->symbols);
103       sh->symbols = NULL;
104    }
105 }
106 
107 /**
108  * This is the equivalent to compiler/glsl/linker.cpp::link_shaders()
109  * but for SPIR-V programs.
110  *
111  * This method just creates the gl_linked_shader structs with a reference to
112  * the SPIR-V data collected during previous steps.
113  *
114  * The real linking happens later in the driver-specifc call LinkShader().
115  * This is so backends can implement different linking strategies for
116  * SPIR-V programs.
117  */
118 void
_mesa_spirv_link_shaders(struct gl_context * ctx,struct gl_shader_program * prog)119 _mesa_spirv_link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
120 {
121    prog->data->LinkStatus = LINKING_SUCCESS;
122    prog->data->Validated = false;
123 
124    for (unsigned i = 0; i < prog->NumShaders; i++) {
125       struct gl_shader *shader = prog->Shaders[i];
126       gl_shader_stage shader_type = shader->Stage;
127 
128       /* We only support one shader per stage. The gl_spirv spec doesn't seem
129        * to prevent this, but the way the API is designed, requiring all shaders
130        * to be specialized with an entry point, makes supporting this quite
131        * undefined.
132        *
133        * TODO: Turn this into a proper error once the spec bug
134        * <https://gitlab.khronos.org/opengl/API/issues/58> is resolved.
135        */
136       if (prog->_LinkedShaders[shader_type]) {
137          ralloc_strcat(&prog->data->InfoLog,
138                        "\nError trying to link more than one SPIR-V shader "
139                        "per stage.\n");
140          prog->data->LinkStatus = LINKING_FAILURE;
141          return;
142       }
143 
144       assert(shader->spirv_data);
145 
146       struct gl_linked_shader *linked = rzalloc(NULL, struct gl_linked_shader);
147       linked->Stage = shader_type;
148 
149       /* Create program and attach it to the linked shader */
150       struct gl_program *gl_prog =
151          ctx->Driver.NewProgram(ctx, shader_type, prog->Name, false);
152       if (!gl_prog) {
153          prog->data->LinkStatus = LINKING_FAILURE;
154          _mesa_delete_linked_shader(ctx, linked);
155          return;
156       }
157 
158       _mesa_reference_shader_program_data(ctx,
159                                           &gl_prog->sh.data,
160                                           prog->data);
161 
162       /* Don't use _mesa_reference_program() just take ownership */
163       linked->Program = gl_prog;
164 
165       /* Reference the SPIR-V data from shader to the linked shader */
166       _mesa_shader_spirv_data_reference(&linked->spirv_data,
167                                         shader->spirv_data);
168 
169       prog->_LinkedShaders[shader_type] = linked;
170       prog->data->linked_stages |= 1 << shader_type;
171    }
172 
173    int last_vert_stage =
174       util_last_bit(prog->data->linked_stages &
175                     ((1 << (MESA_SHADER_GEOMETRY + 1)) - 1));
176 
177    if (last_vert_stage)
178       prog->last_vert_prog = prog->_LinkedShaders[last_vert_stage - 1]->Program;
179 
180    /* Some shaders have to be linked with some other shaders present. */
181    if (!prog->SeparateShader) {
182       static const struct {
183          gl_shader_stage a, b;
184       } stage_pairs[] = {
185          { MESA_SHADER_GEOMETRY, MESA_SHADER_VERTEX },
186          { MESA_SHADER_TESS_EVAL, MESA_SHADER_VERTEX },
187          { MESA_SHADER_TESS_CTRL, MESA_SHADER_VERTEX },
188          { MESA_SHADER_TESS_CTRL, MESA_SHADER_TESS_EVAL },
189       };
190 
191       for (unsigned i = 0; i < ARRAY_SIZE(stage_pairs); i++) {
192          gl_shader_stage a = stage_pairs[i].a;
193          gl_shader_stage b = stage_pairs[i].b;
194          if ((prog->data->linked_stages & ((1 << a) | (1 << b))) == (1 << a)) {
195             ralloc_asprintf_append(&prog->data->InfoLog,
196                                    "%s shader must be linked with %s shader\n",
197                                    _mesa_shader_stage_to_string(a),
198                                    _mesa_shader_stage_to_string(b));
199             prog->data->LinkStatus = LINKING_FAILURE;
200             return;
201          }
202       }
203    }
204 
205    /* Compute shaders have additional restrictions. */
206    if ((prog->data->linked_stages & (1 << MESA_SHADER_COMPUTE)) &&
207        (prog->data->linked_stages & ~(1 << MESA_SHADER_COMPUTE))) {
208       ralloc_asprintf_append(&prog->data->InfoLog,
209                              "Compute shaders may not be linked with any other "
210                              "type of shader\n");
211       prog->data->LinkStatus = LINKING_FAILURE;
212       return;
213    }
214 }
215 
216 nir_shader *
_mesa_spirv_to_nir(struct gl_context * ctx,const struct gl_shader_program * prog,gl_shader_stage stage,const nir_shader_compiler_options * options)217 _mesa_spirv_to_nir(struct gl_context *ctx,
218                    const struct gl_shader_program *prog,
219                    gl_shader_stage stage,
220                    const nir_shader_compiler_options *options)
221 {
222    struct gl_linked_shader *linked_shader = prog->_LinkedShaders[stage];
223    assert (linked_shader);
224 
225    struct gl_shader_spirv_data *spirv_data = linked_shader->spirv_data;
226    assert(spirv_data);
227 
228    struct gl_spirv_module *spirv_module = spirv_data->SpirVModule;
229    assert (spirv_module != NULL);
230 
231    const char *entry_point_name = spirv_data->SpirVEntryPoint;
232    assert(entry_point_name);
233 
234    struct nir_spirv_specialization *spec_entries =
235       calloc(sizeof(*spec_entries),
236              spirv_data->NumSpecializationConstants);
237 
238    for (unsigned i = 0; i < spirv_data->NumSpecializationConstants; ++i) {
239       spec_entries[i].id = spirv_data->SpecializationConstantsIndex[i];
240       spec_entries[i].value.u32 = spirv_data->SpecializationConstantsValue[i];
241       spec_entries[i].defined_on_module = false;
242    }
243 
244    const struct spirv_to_nir_options spirv_options = {
245       .environment = NIR_SPIRV_OPENGL,
246       .use_deref_buffer_array_length = true,
247       .caps = ctx->Const.SpirVCapabilities,
248       .ubo_addr_format = nir_address_format_32bit_index_offset,
249       .ssbo_addr_format = nir_address_format_32bit_index_offset,
250 
251       /* TODO: Consider changing this to an address format that has the NULL
252        * pointer equals to 0.  That might be a better format to play nice
253        * with certain code / code generators.
254        */
255       .shared_addr_format = nir_address_format_32bit_offset,
256 
257    };
258 
259    nir_shader *nir =
260       spirv_to_nir((const uint32_t *) &spirv_module->Binary[0],
261                    spirv_module->Length / 4,
262                    spec_entries, spirv_data->NumSpecializationConstants,
263                    stage, entry_point_name,
264                    &spirv_options,
265                    options);
266    free(spec_entries);
267 
268    assert(nir);
269    assert(nir->info.stage == stage);
270 
271    nir->options = options;
272 
273    nir->info.name =
274       ralloc_asprintf(nir, "SPIRV:%s:%d",
275                       _mesa_shader_stage_to_abbrev(nir->info.stage),
276                       prog->Name);
277    nir_validate_shader(nir, "after spirv_to_nir");
278 
279    nir->info.separate_shader = linked_shader->Program->info.separate_shader;
280 
281    /* Convert some sysvals to input varyings. */
282    const struct nir_lower_sysvals_to_varyings_options sysvals_to_varyings = {
283       .frag_coord = !ctx->Const.GLSLFragCoordIsSysVal,
284       .point_coord = !ctx->Const.GLSLPointCoordIsSysVal,
285       .front_face = !ctx->Const.GLSLFrontFacingIsSysVal,
286    };
287    NIR_PASS_V(nir, nir_lower_sysvals_to_varyings, &sysvals_to_varyings);
288 
289    /* We have to lower away local constant initializers right before we
290     * inline functions.  That way they get properly initialized at the top
291     * of the function and not at the top of its caller.
292     */
293    NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
294    NIR_PASS_V(nir, nir_lower_returns);
295    NIR_PASS_V(nir, nir_inline_functions);
296    NIR_PASS_V(nir, nir_copy_prop);
297    NIR_PASS_V(nir, nir_opt_deref);
298 
299    /* Pick off the single entrypoint that we want */
300    foreach_list_typed_safe(nir_function, func, node, &nir->functions) {
301       if (!func->is_entrypoint)
302          exec_node_remove(&func->node);
303    }
304    assert(exec_list_length(&nir->functions) == 1);
305 
306    /* Now that we've deleted all but the main function, we can go ahead and
307     * lower the rest of the constant initializers.  We do this here so that
308     * nir_remove_dead_variables and split_per_member_structs below see the
309     * corresponding stores.
310     */
311    NIR_PASS_V(nir, nir_lower_variable_initializers, ~0);
312 
313    /* Split member structs.  We do this before lower_io_to_temporaries so that
314     * it doesn't lower system values to temporaries by accident.
315     */
316    NIR_PASS_V(nir, nir_split_var_copies);
317    NIR_PASS_V(nir, nir_split_per_member_structs);
318 
319    if (nir->info.stage == MESA_SHADER_VERTEX)
320       nir_remap_dual_slot_attributes(nir, &linked_shader->Program->DualSlotInputs);
321 
322    NIR_PASS_V(nir, nir_lower_frexp);
323 
324    return nir;
325 }
326 
327 void GLAPIENTRY
_mesa_SpecializeShaderARB(GLuint shader,const GLchar * pEntryPoint,GLuint numSpecializationConstants,const GLuint * pConstantIndex,const GLuint * pConstantValue)328 _mesa_SpecializeShaderARB(GLuint shader,
329                           const GLchar *pEntryPoint,
330                           GLuint numSpecializationConstants,
331                           const GLuint *pConstantIndex,
332                           const GLuint *pConstantValue)
333 {
334    GET_CURRENT_CONTEXT(ctx);
335    struct gl_shader *sh;
336    bool has_entry_point;
337    struct nir_spirv_specialization *spec_entries = NULL;
338 
339    if (!ctx->Extensions.ARB_gl_spirv) {
340       _mesa_error(ctx, GL_INVALID_OPERATION, "glSpecializeShaderARB");
341       return;
342    }
343 
344    sh = _mesa_lookup_shader_err(ctx, shader, "glSpecializeShaderARB");
345    if (!sh)
346       return;
347 
348    if (!sh->spirv_data) {
349       _mesa_error(ctx, GL_INVALID_OPERATION,
350                   "glSpecializeShaderARB(not SPIR-V)");
351       return;
352    }
353 
354    if (sh->CompileStatus) {
355       _mesa_error(ctx, GL_INVALID_OPERATION,
356                   "glSpecializeShaderARB(already specialized)");
357       return;
358    }
359 
360    struct gl_shader_spirv_data *spirv_data = sh->spirv_data;
361 
362    /* From the GL_ARB_gl_spirv spec:
363     *
364     *    "The OpenGL API expects the SPIR-V module to have already been
365     *     validated, and can return an error if it discovers anything invalid
366     *     in the module. An invalid SPIR-V module is allowed to result in
367     *     undefined behavior."
368     *
369     * However, the following errors still need to be detected (from the same
370     * spec):
371     *
372     *    "INVALID_VALUE is generated if <pEntryPoint> does not name a valid
373     *     entry point for <shader>.
374     *
375     *     INVALID_VALUE is generated if any element of <pConstantIndex>
376     *     refers to a specialization constant that does not exist in the
377     *     shader module contained in <shader>."
378     *
379     * We cannot flag those errors a-priori because detecting them requires
380     * parsing the module. However, flagging them during specialization is okay,
381     * since it makes no difference in terms of application-visible state.
382     */
383    spec_entries = calloc(sizeof(*spec_entries), numSpecializationConstants);
384 
385    for (unsigned i = 0; i < numSpecializationConstants; ++i) {
386       spec_entries[i].id = pConstantIndex[i];
387       spec_entries[i].value.u32 = pConstantValue[i];
388       spec_entries[i].defined_on_module = false;
389    }
390 
391    has_entry_point =
392       gl_spirv_validation((uint32_t *)&spirv_data->SpirVModule->Binary[0],
393                           spirv_data->SpirVModule->Length / 4,
394                           spec_entries, numSpecializationConstants,
395                           sh->Stage, pEntryPoint);
396 
397    /* See previous spec comment */
398    if (!has_entry_point) {
399       _mesa_error(ctx, GL_INVALID_VALUE,
400                   "glSpecializeShaderARB(\"%s\" is not a valid entry point"
401                   " for shader)", pEntryPoint);
402       goto end;
403    }
404 
405    for (unsigned i = 0; i < numSpecializationConstants; ++i) {
406       if (spec_entries[i].defined_on_module == false) {
407          _mesa_error(ctx, GL_INVALID_VALUE,
408                      "glSpecializeShaderARB(constant \"%i\" does not exist "
409                      "in shader)", spec_entries[i].id);
410          goto end;
411       }
412    }
413 
414    spirv_data->SpirVEntryPoint = ralloc_strdup(spirv_data, pEntryPoint);
415 
416    /* Note that we didn't make a real compilation of the module (spirv_to_nir),
417     * but just checked some error conditions. Real "compilation" will be done
418     * later, upon linking.
419     */
420    sh->CompileStatus = COMPILE_SUCCESS;
421 
422    spirv_data->NumSpecializationConstants = numSpecializationConstants;
423    spirv_data->SpecializationConstantsIndex =
424       rzalloc_array_size(spirv_data, sizeof(GLuint),
425                          numSpecializationConstants);
426    spirv_data->SpecializationConstantsValue =
427       rzalloc_array_size(spirv_data, sizeof(GLuint),
428                          numSpecializationConstants);
429    for (unsigned i = 0; i < numSpecializationConstants; ++i) {
430       spirv_data->SpecializationConstantsIndex[i] = pConstantIndex[i];
431       spirv_data->SpecializationConstantsValue[i] = pConstantValue[i];
432    }
433 
434  end:
435    free(spec_entries);
436 }
437