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