• 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 "spirv_capabilities.h"
28 #include "mtypes.h"
29 
30 #include "compiler/nir/nir.h"
31 #include "compiler/spirv/nir_spirv.h"
32 #include "compiler/spirv/spirv_info.h"
33 
34 #include "program/program.h"
35 
36 #include "util/u_atomic.h"
37 #include "api_exec_decl.h"
38 
39 void
_mesa_spirv_module_reference(struct gl_spirv_module ** dest,struct gl_spirv_module * src)40 _mesa_spirv_module_reference(struct gl_spirv_module **dest,
41                              struct gl_spirv_module *src)
42 {
43    struct gl_spirv_module *old = *dest;
44 
45    if (old && p_atomic_dec_zero(&old->RefCount))
46       free(old);
47 
48    *dest = src;
49 
50    if (src)
51       p_atomic_inc(&src->RefCount);
52 }
53 
54 void
_mesa_shader_spirv_data_reference(struct gl_shader_spirv_data ** dest,struct gl_shader_spirv_data * src)55 _mesa_shader_spirv_data_reference(struct gl_shader_spirv_data **dest,
56                                   struct gl_shader_spirv_data *src)
57 {
58    struct gl_shader_spirv_data *old = *dest;
59 
60    if (old && p_atomic_dec_zero(&old->RefCount)) {
61       _mesa_spirv_module_reference(&(*dest)->SpirVModule, NULL);
62       ralloc_free(old);
63    }
64 
65    *dest = src;
66 
67    if (src)
68       p_atomic_inc(&src->RefCount);
69 }
70 
71 void
_mesa_spirv_shader_binary(struct gl_context * ctx,unsigned n,struct gl_shader ** shaders,const void * binary,size_t length)72 _mesa_spirv_shader_binary(struct gl_context *ctx,
73                           unsigned n, struct gl_shader **shaders,
74                           const void* binary, size_t length)
75 {
76    struct gl_spirv_module *module;
77    struct gl_shader_spirv_data *spirv_data;
78 
79    /* From OpenGL 4.6 Core spec, "7.2 Shader Binaries" :
80     *
81     * "An INVALID_VALUE error is generated if the data pointed to by binary
82     *  does not match the specified binaryformat."
83     *
84     * However, the ARB_gl_spirv spec, under issue #16 says:
85     *
86     * "ShaderBinary is expected to form an association between the SPIR-V
87     *  module and likely would not parse the module as would be required to
88     *  detect unsupported capabilities or other validation failures."
89     *
90     * Which specifies little to no validation requirements. Nevertheless, the
91     * two small checks below seem reasonable.
92     */
93    if (!binary || (length % 4) != 0) {
94       _mesa_error(ctx, GL_INVALID_VALUE, "glShaderBinary");
95       return;
96    }
97 
98    module = malloc(sizeof(*module) + length);
99    if (!module) {
100       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glShaderBinary");
101       return;
102    }
103 
104    p_atomic_set(&module->RefCount, 0);
105    module->Length = length;
106    memcpy(&module->Binary[0], binary, length);
107 
108    for (int i = 0; i < n; ++i) {
109       struct gl_shader *sh = shaders[i];
110 
111       spirv_data = rzalloc(NULL, struct gl_shader_spirv_data);
112       _mesa_shader_spirv_data_reference(&sh->spirv_data, spirv_data);
113       _mesa_spirv_module_reference(&spirv_data->SpirVModule, module);
114 
115       sh->CompileStatus = COMPILE_FAILURE;
116 
117       free((void *)sh->Source);
118       sh->Source = NULL;
119       free((void *)sh->FallbackSource);
120       sh->FallbackSource = NULL;
121 
122       ralloc_free(sh->ir);
123       sh->ir = NULL;
124    }
125 }
126 
127 /**
128  * This method just creates the gl_linked_shader structs with a reference to
129  * the SPIR-V data collected during previous steps.
130  *
131  * The real linking happens later in the driver-specifc call LinkShader().
132  * This is so backends can implement different linking strategies for
133  * SPIR-V programs.
134  */
135 void
_mesa_spirv_link_shaders(struct gl_context * ctx,struct gl_shader_program * prog)136 _mesa_spirv_link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
137 {
138    prog->data->LinkStatus = LINKING_SUCCESS;
139    prog->data->Validated = false;
140 
141    for (unsigned i = 0; i < prog->NumShaders; i++) {
142       struct gl_shader *shader = prog->Shaders[i];
143       gl_shader_stage shader_type = shader->Stage;
144 
145       /* We only support one shader per stage. The gl_spirv spec doesn't seem
146        * to prevent this, but the way the API is designed, requiring all shaders
147        * to be specialized with an entry point, makes supporting this quite
148        * undefined.
149        *
150        * TODO: Turn this into a proper error once the spec bug
151        * <https://gitlab.khronos.org/opengl/API/issues/58> is resolved.
152        */
153       if (prog->_LinkedShaders[shader_type]) {
154          ralloc_strcat(&prog->data->InfoLog,
155                        "\nError trying to link more than one SPIR-V shader "
156                        "per stage.\n");
157          prog->data->LinkStatus = LINKING_FAILURE;
158          return;
159       }
160 
161       assert(shader->spirv_data);
162 
163       struct gl_linked_shader *linked = rzalloc(NULL, struct gl_linked_shader);
164       linked->Stage = shader_type;
165 
166       /* Create program and attach it to the linked shader */
167       struct gl_program *gl_prog =
168          ctx->Driver.NewProgram(ctx, shader_type, prog->Name, false);
169       if (!gl_prog) {
170          prog->data->LinkStatus = LINKING_FAILURE;
171          _mesa_delete_linked_shader(ctx, linked);
172          return;
173       }
174 
175       _mesa_reference_shader_program_data(&gl_prog->sh.data,
176                                           prog->data);
177 
178       /* Don't use _mesa_reference_program() just take ownership */
179       linked->Program = gl_prog;
180 
181       /* Reference the SPIR-V data from shader to the linked shader */
182       _mesa_shader_spirv_data_reference(&linked->spirv_data,
183                                         shader->spirv_data);
184 
185       prog->_LinkedShaders[shader_type] = linked;
186       prog->data->linked_stages |= 1 << shader_type;
187    }
188 
189    int last_vert_stage =
190       util_last_bit(prog->data->linked_stages &
191                     ((1 << (MESA_SHADER_GEOMETRY + 1)) - 1));
192 
193    if (last_vert_stage)
194       prog->last_vert_prog = prog->_LinkedShaders[last_vert_stage - 1]->Program;
195 
196    /* Some shaders have to be linked with some other shaders present. */
197    if (!prog->SeparateShader) {
198       static const struct {
199          gl_shader_stage a, b;
200       } stage_pairs[] = {
201          { MESA_SHADER_GEOMETRY, MESA_SHADER_VERTEX },
202          { MESA_SHADER_TESS_EVAL, MESA_SHADER_VERTEX },
203          { MESA_SHADER_TESS_CTRL, MESA_SHADER_VERTEX },
204          { MESA_SHADER_TESS_CTRL, MESA_SHADER_TESS_EVAL },
205       };
206 
207       for (unsigned i = 0; i < ARRAY_SIZE(stage_pairs); i++) {
208          gl_shader_stage a = stage_pairs[i].a;
209          gl_shader_stage b = stage_pairs[i].b;
210          if ((prog->data->linked_stages & ((1 << a) | (1 << b))) == (1 << a)) {
211             ralloc_asprintf_append(&prog->data->InfoLog,
212                                    "%s shader must be linked with %s shader\n",
213                                    _mesa_shader_stage_to_string(a),
214                                    _mesa_shader_stage_to_string(b));
215             prog->data->LinkStatus = LINKING_FAILURE;
216             return;
217          }
218       }
219    }
220 
221    /* Compute shaders have additional restrictions. */
222    if ((prog->data->linked_stages & (1 << MESA_SHADER_COMPUTE)) &&
223        (prog->data->linked_stages & ~(1 << MESA_SHADER_COMPUTE))) {
224       ralloc_asprintf_append(&prog->data->InfoLog,
225                              "Compute shaders may not be linked with any other "
226                              "type of shader\n");
227       prog->data->LinkStatus = LINKING_FAILURE;
228       return;
229    }
230 }
231 
232 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)233 _mesa_spirv_to_nir(struct gl_context *ctx,
234                    const struct gl_shader_program *prog,
235                    gl_shader_stage stage,
236                    const nir_shader_compiler_options *options)
237 {
238    struct gl_linked_shader *linked_shader = prog->_LinkedShaders[stage];
239    assert (linked_shader);
240 
241    struct gl_shader_spirv_data *spirv_data = linked_shader->spirv_data;
242    assert(spirv_data);
243 
244    struct gl_spirv_module *spirv_module = spirv_data->SpirVModule;
245    assert (spirv_module != NULL);
246 
247    const char *entry_point_name = spirv_data->SpirVEntryPoint;
248    assert(entry_point_name);
249 
250    struct nir_spirv_specialization *spec_entries =
251       calloc(sizeof(*spec_entries),
252              spirv_data->NumSpecializationConstants);
253 
254    for (unsigned i = 0; i < spirv_data->NumSpecializationConstants; ++i) {
255       spec_entries[i].id = spirv_data->SpecializationConstantsIndex[i];
256       spec_entries[i].value.u32 = spirv_data->SpecializationConstantsValue[i];
257       spec_entries[i].defined_on_module = false;
258    }
259 
260    struct spirv_capabilities spirv_caps;
261    _mesa_fill_supported_spirv_capabilities(&spirv_caps, &ctx->Const,
262                                            &ctx->Extensions);
263 
264    struct spirv_to_nir_options spirv_options = {
265       .environment = NIR_SPIRV_OPENGL,
266       .capabilities = &spirv_caps,
267       .subgroup_size = SUBGROUP_SIZE_UNIFORM,
268       .ubo_addr_format = nir_address_format_32bit_index_offset,
269       .ssbo_addr_format = nir_address_format_32bit_index_offset,
270 
271       /* TODO: Consider changing this to an address format that has the NULL
272        * pointer equals to 0.  That might be a better format to play nice
273        * with certain code / code generators.
274        */
275       .shared_addr_format = nir_address_format_32bit_offset,
276 
277    };
278 
279    nir_shader *nir =
280       spirv_to_nir((const uint32_t *) &spirv_module->Binary[0],
281                    spirv_module->Length / 4,
282                    spec_entries, spirv_data->NumSpecializationConstants,
283                    stage, entry_point_name,
284                    &spirv_options,
285                    options);
286    free(spec_entries);
287 
288    assert(nir);
289    assert(nir->info.stage == stage);
290 
291    nir->options = options;
292 
293    nir->info.name =
294       ralloc_asprintf(nir, "SPIRV:%s:%d",
295                       _mesa_shader_stage_to_abbrev(nir->info.stage),
296                       prog->Name);
297    nir_validate_shader(nir, "after spirv_to_nir");
298 
299    nir->info.separate_shader = linked_shader->Program->info.separate_shader;
300 
301    /* Convert some sysvals to input varyings. */
302    const struct nir_lower_sysvals_to_varyings_options sysvals_to_varyings = {
303       .frag_coord = !ctx->Const.GLSLFragCoordIsSysVal,
304       .point_coord = !ctx->Const.GLSLPointCoordIsSysVal,
305       .front_face = !ctx->Const.GLSLFrontFacingIsSysVal,
306    };
307    NIR_PASS(_, nir, nir_lower_sysvals_to_varyings, &sysvals_to_varyings);
308 
309    /* We have to lower away local constant initializers right before we
310     * inline functions.  That way they get properly initialized at the top
311     * of the function and not at the top of its caller.
312     */
313    NIR_PASS(_, nir, nir_lower_variable_initializers, nir_var_function_temp);
314    NIR_PASS(_, nir, nir_lower_returns);
315    NIR_PASS(_, nir, nir_inline_functions);
316    NIR_PASS(_, nir, nir_copy_prop);
317    NIR_PASS(_, nir, nir_opt_deref);
318 
319    /* Pick off the single entrypoint that we want */
320    nir_remove_non_entrypoints(nir);
321 
322    /* Now that we've deleted all but the main function, we can go ahead and
323     * lower the rest of the constant initializers.  We do this here so that
324     * nir_remove_dead_variables and split_per_member_structs below see the
325     * corresponding stores.
326     */
327    NIR_PASS(_, nir, nir_lower_variable_initializers, ~0);
328 
329    /* Split member structs.  We do this before lower_io_to_temporaries so that
330     * it doesn't lower system values to temporaries by accident.
331     */
332    NIR_PASS(_, nir, nir_split_var_copies);
333    NIR_PASS(_, nir, nir_split_per_member_structs);
334 
335    NIR_PASS(_, nir, nir_lower_frexp);
336 
337    return nir;
338 }
339 
340 void GLAPIENTRY
_mesa_SpecializeShaderARB(GLuint shader,const GLchar * pEntryPoint,GLuint numSpecializationConstants,const GLuint * pConstantIndex,const GLuint * pConstantValue)341 _mesa_SpecializeShaderARB(GLuint shader,
342                           const GLchar *pEntryPoint,
343                           GLuint numSpecializationConstants,
344                           const GLuint *pConstantIndex,
345                           const GLuint *pConstantValue)
346 {
347    GET_CURRENT_CONTEXT(ctx);
348    struct gl_shader *sh;
349    struct nir_spirv_specialization *spec_entries = NULL;
350 
351    if (!ctx->Extensions.ARB_gl_spirv) {
352       _mesa_error(ctx, GL_INVALID_OPERATION, "glSpecializeShaderARB");
353       return;
354    }
355 
356    sh = _mesa_lookup_shader_err(ctx, shader, "glSpecializeShaderARB");
357    if (!sh)
358       return;
359 
360    if (!sh->spirv_data) {
361       _mesa_error(ctx, GL_INVALID_OPERATION,
362                   "glSpecializeShaderARB(not SPIR-V)");
363       return;
364    }
365 
366    if (sh->CompileStatus) {
367       _mesa_error(ctx, GL_INVALID_OPERATION,
368                   "glSpecializeShaderARB(already specialized)");
369       return;
370    }
371 
372    struct gl_shader_spirv_data *spirv_data = sh->spirv_data;
373 
374    /* From the GL_ARB_gl_spirv spec:
375     *
376     *    "The OpenGL API expects the SPIR-V module to have already been
377     *     validated, and can return an error if it discovers anything invalid
378     *     in the module. An invalid SPIR-V module is allowed to result in
379     *     undefined behavior."
380     *
381     * However, the following errors still need to be detected (from the same
382     * spec):
383     *
384     *    "INVALID_VALUE is generated if <pEntryPoint> does not name a valid
385     *     entry point for <shader>.
386     *
387     *     INVALID_VALUE is generated if any element of <pConstantIndex>
388     *     refers to a specialization constant that does not exist in the
389     *     shader module contained in <shader>."
390     *
391     * We cannot flag those errors a-priori because detecting them requires
392     * parsing the module. However, flagging them during specialization is okay,
393     * since it makes no difference in terms of application-visible state.
394     */
395    spec_entries = calloc(sizeof(*spec_entries), numSpecializationConstants);
396 
397    for (unsigned i = 0; i < numSpecializationConstants; ++i) {
398       spec_entries[i].id = pConstantIndex[i];
399       spec_entries[i].value.u32 = pConstantValue[i];
400       spec_entries[i].defined_on_module = false;
401    }
402 
403    enum spirv_verify_result r = spirv_verify_gl_specialization_constants(
404       (uint32_t *)&spirv_data->SpirVModule->Binary[0],
405       spirv_data->SpirVModule->Length / 4,
406       spec_entries, numSpecializationConstants,
407       sh->Stage, pEntryPoint);
408 
409    switch (r) {
410    case SPIRV_VERIFY_OK:
411       break;
412    case SPIRV_VERIFY_PARSER_ERROR:
413       _mesa_error(ctx, GL_INVALID_VALUE,
414                   "glSpecializeShaderARB(failed to parse entry point \"%s\""
415                   " for shader)", pEntryPoint);
416       goto end;
417    case SPIRV_VERIFY_ENTRY_POINT_NOT_FOUND:
418       _mesa_error(ctx, GL_INVALID_VALUE,
419                   "glSpecializeShaderARB(could not find entry point \"%s\""
420                   " for shader)", pEntryPoint);
421       goto end;
422    case SPIRV_VERIFY_UNKNOWN_SPEC_INDEX:
423       for (unsigned i = 0; i < numSpecializationConstants; ++i) {
424          if (spec_entries[i].defined_on_module == false) {
425             _mesa_error(ctx, GL_INVALID_VALUE,
426                         "glSpecializeShaderARB(constant \"%i\" does not exist "
427                         "in shader)", spec_entries[i].id);
428             break;
429          }
430       }
431       goto end;
432    }
433 
434    spirv_data->SpirVEntryPoint = ralloc_strdup(spirv_data, pEntryPoint);
435 
436    /* Note that we didn't make a real compilation of the module (spirv_to_nir),
437     * but just checked some error conditions. Real "compilation" will be done
438     * later, upon linking.
439     */
440    sh->CompileStatus = COMPILE_SUCCESS;
441 
442    spirv_data->NumSpecializationConstants = numSpecializationConstants;
443    spirv_data->SpecializationConstantsIndex =
444       rzalloc_array_size(spirv_data, sizeof(GLuint),
445                          numSpecializationConstants);
446    spirv_data->SpecializationConstantsValue =
447       rzalloc_array_size(spirv_data, sizeof(GLuint),
448                          numSpecializationConstants);
449    for (unsigned i = 0; i < numSpecializationConstants; ++i) {
450       spirv_data->SpecializationConstantsIndex[i] = pConstantIndex[i];
451       spirv_data->SpecializationConstantsValue[i] = pConstantValue[i];
452    }
453 
454  end:
455    free(spec_entries);
456 }
457