2 * Copyright 2017 Advanced Micro Devices, Inc.
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:
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
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.
26 #include "shaderobj.h"
29 #include "compiler/nir/nir.h"
30 #include "compiler/spirv/nir_spirv.h"
32 #include "program/program.h"
34 #include "util/u_atomic.h"
37 _mesa_spirv_module_reference(struct gl_spirv_module
**dest
,
38 struct gl_spirv_module
*src
)
40 struct gl_spirv_module
*old
= *dest
;
42 if (old
&& p_atomic_dec_zero(&old
->RefCount
))
48 p_atomic_inc(&src
->RefCount
);
52 _mesa_shader_spirv_data_reference(struct gl_shader_spirv_data
**dest
,
53 struct gl_shader_spirv_data
*src
)
55 struct gl_shader_spirv_data
*old
= *dest
;
57 if (old
&& p_atomic_dec_zero(&old
->RefCount
)) {
58 _mesa_spirv_module_reference(&(*dest
)->SpirVModule
, NULL
);
65 p_atomic_inc(&src
->RefCount
);
69 _mesa_spirv_shader_binary(struct gl_context
*ctx
,
70 unsigned n
, struct gl_shader
**shaders
,
71 const void* binary
, size_t length
)
73 struct gl_spirv_module
*module
;
74 struct gl_shader_spirv_data
*spirv_data
;
76 module
= malloc(sizeof(*module
) + length
);
78 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glShaderBinary");
82 p_atomic_set(&module
->RefCount
, 0);
83 module
->Length
= length
;
84 memcpy(&module
->Binary
[0], binary
, length
);
86 for (int i
= 0; i
< n
; ++i
) {
87 struct gl_shader
*sh
= shaders
[i
];
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
);
93 sh
->CompileStatus
= COMPILE_FAILURE
;
95 free((void *)sh
->Source
);
97 free((void *)sh
->FallbackSource
);
98 sh
->FallbackSource
= NULL
;
102 ralloc_free(sh
->symbols
);
108 * This is the equivalent to compiler/glsl/linker.cpp::link_shaders()
109 * but for SPIR-V programs.
111 * This method just creates the gl_linked_shader structs with a reference to
112 * the SPIR-V data collected during previous steps.
114 * The real linking happens later in the driver-specifc call LinkShader().
115 * This is so backends can implement different linking strategies for
119 _mesa_spirv_link_shaders(struct gl_context
*ctx
, struct gl_shader_program
*prog
)
121 prog
->data
->LinkStatus
= LINKING_SUCCESS
;
122 prog
->data
->Validated
= false;
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
;
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
133 * TODO: Turn this into a proper error once the spec bug
134 * <https://gitlab.khronos.org/opengl/API/issues/58> is resolved.
136 if (prog
->_LinkedShaders
[shader_type
]) {
137 ralloc_strcat(&prog
->data
->InfoLog
,
138 "\nError trying to link more than one SPIR-V shader "
140 prog
->data
->LinkStatus
= LINKING_FAILURE
;
144 assert(shader
->spirv_data
);
146 struct gl_linked_shader
*linked
= rzalloc(NULL
, struct gl_linked_shader
);
147 linked
->Stage
= shader_type
;
149 /* Create program and attach it to the linked shader */
150 struct gl_program
*gl_prog
=
151 ctx
->Driver
.NewProgram(ctx
,
152 _mesa_shader_stage_to_program(shader_type
),
155 prog
->data
->LinkStatus
= LINKING_FAILURE
;
156 _mesa_delete_linked_shader(ctx
, linked
);
160 _mesa_reference_shader_program_data(ctx
,
164 /* Don't use _mesa_reference_program() just take ownership */
165 linked
->Program
= gl_prog
;
167 /* Reference the SPIR-V data from shader to the linked shader */
168 _mesa_shader_spirv_data_reference(&linked
->spirv_data
,
171 prog
->_LinkedShaders
[shader_type
] = linked
;
172 prog
->data
->linked_stages
|= 1 << shader_type
;
175 int last_vert_stage
=
176 util_last_bit(prog
->data
->linked_stages
&
177 ((1 << (MESA_SHADER_GEOMETRY
+ 1)) - 1));
180 prog
->last_vert_prog
= prog
->_LinkedShaders
[last_vert_stage
- 1]->Program
;
182 /* Some shaders have to be linked with some other shaders present. */
183 if (!prog
->SeparateShader
) {
184 static const struct {
185 gl_shader_stage a
, b
;
187 { MESA_SHADER_GEOMETRY
, MESA_SHADER_VERTEX
},
188 { MESA_SHADER_TESS_EVAL
, MESA_SHADER_VERTEX
},
189 { MESA_SHADER_TESS_CTRL
, MESA_SHADER_VERTEX
},
190 { MESA_SHADER_TESS_CTRL
, MESA_SHADER_TESS_EVAL
},
193 for (unsigned i
= 0; i
< ARRAY_SIZE(stage_pairs
); i
++) {
194 gl_shader_stage a
= stage_pairs
[i
].a
;
195 gl_shader_stage b
= stage_pairs
[i
].b
;
196 if ((prog
->data
->linked_stages
& ((1 << a
) | (1 << b
))) == (1 << a
)) {
197 ralloc_asprintf_append(&prog
->data
->InfoLog
,
198 "%s shader must be linked with %s shader\n",
199 _mesa_shader_stage_to_string(a
),
200 _mesa_shader_stage_to_string(b
));
201 prog
->data
->LinkStatus
= LINKING_FAILURE
;
207 /* Compute shaders have additional restrictions. */
208 if ((prog
->data
->linked_stages
& (1 << MESA_SHADER_COMPUTE
)) &&
209 (prog
->data
->linked_stages
& ~(1 << MESA_SHADER_COMPUTE
))) {
210 ralloc_asprintf_append(&prog
->data
->InfoLog
,
211 "Compute shaders may not be linked with any other "
213 prog
->data
->LinkStatus
= LINKING_FAILURE
;
219 _mesa_spirv_to_nir(struct gl_context
*ctx
,
220 const struct gl_shader_program
*prog
,
221 gl_shader_stage stage
,
222 const nir_shader_compiler_options
*options
)
224 struct gl_linked_shader
*linked_shader
= prog
->_LinkedShaders
[stage
];
225 assert (linked_shader
);
227 struct gl_shader_spirv_data
*spirv_data
= linked_shader
->spirv_data
;
230 struct gl_spirv_module
*spirv_module
= spirv_data
->SpirVModule
;
231 assert (spirv_module
!= NULL
);
233 const char *entry_point_name
= spirv_data
->SpirVEntryPoint
;
234 assert(entry_point_name
);
236 struct nir_spirv_specialization
*spec_entries
=
237 calloc(sizeof(*spec_entries
),
238 spirv_data
->NumSpecializationConstants
);
240 for (unsigned i
= 0; i
< spirv_data
->NumSpecializationConstants
; ++i
) {
241 spec_entries
[i
].id
= spirv_data
->SpecializationConstantsIndex
[i
];
242 spec_entries
[i
].data32
= spirv_data
->SpecializationConstantsValue
[i
];
243 spec_entries
[i
].defined_on_module
= false;
246 const struct spirv_to_nir_options spirv_options
= {
247 .environment
= NIR_SPIRV_OPENGL
,
248 .frag_coord_is_sysval
= ctx
->Const
.GLSLFragCoordIsSysVal
,
249 .caps
= ctx
->Const
.SpirVCapabilities
,
250 .ubo_addr_format
= nir_address_format_32bit_index_offset
,
251 .ssbo_addr_format
= nir_address_format_32bit_index_offset
,
253 /* TODO: Consider changing this to an address format that has the NULL
254 * pointer equals to 0. That might be a better format to play nice
255 * with certain code / code generators.
257 .shared_addr_format
= nir_address_format_32bit_offset
,
262 spirv_to_nir((const uint32_t *) &spirv_module
->Binary
[0],
263 spirv_module
->Length
/ 4,
264 spec_entries
, spirv_data
->NumSpecializationConstants
,
265 stage
, entry_point_name
,
271 assert(nir
->info
.stage
== stage
);
273 nir
->options
= options
;
276 ralloc_asprintf(nir
, "SPIRV:%s:%d",
277 _mesa_shader_stage_to_abbrev(nir
->info
.stage
),
279 nir_validate_shader(nir
, "after spirv_to_nir");
281 nir
->info
.separate_shader
= linked_shader
->Program
->info
.separate_shader
;
283 /* We have to lower away local constant initializers right before we
284 * inline functions. That way they get properly initialized at the top
285 * of the function and not at the top of its caller.
287 NIR_PASS_V(nir
, nir_lower_constant_initializers
, nir_var_function_temp
);
288 NIR_PASS_V(nir
, nir_lower_returns
);
289 NIR_PASS_V(nir
, nir_inline_functions
);
290 NIR_PASS_V(nir
, nir_opt_deref
);
292 /* Pick off the single entrypoint that we want */
293 foreach_list_typed_safe(nir_function
, func
, node
, &nir
->functions
) {
294 if (!func
->is_entrypoint
)
295 exec_node_remove(&func
->node
);
297 assert(exec_list_length(&nir
->functions
) == 1);
299 /* Split member structs. We do this before lower_io_to_temporaries so that
300 * it doesn't lower system values to temporaries by accident.
302 NIR_PASS_V(nir
, nir_split_var_copies
);
303 NIR_PASS_V(nir
, nir_split_per_member_structs
);
305 if (nir
->info
.stage
== MESA_SHADER_VERTEX
)
306 nir_remap_dual_slot_attributes(nir
, &linked_shader
->Program
->DualSlotInputs
);
308 NIR_PASS_V(nir
, nir_lower_frexp
);
314 _mesa_SpecializeShaderARB(GLuint shader
,
315 const GLchar
*pEntryPoint
,
316 GLuint numSpecializationConstants
,
317 const GLuint
*pConstantIndex
,
318 const GLuint
*pConstantValue
)
320 GET_CURRENT_CONTEXT(ctx
);
321 struct gl_shader
*sh
;
322 bool has_entry_point
;
323 struct nir_spirv_specialization
*spec_entries
= NULL
;
325 if (!ctx
->Extensions
.ARB_gl_spirv
) {
326 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glSpecializeShaderARB");
330 sh
= _mesa_lookup_shader_err(ctx
, shader
, "glSpecializeShaderARB");
334 if (!sh
->spirv_data
) {
335 _mesa_error(ctx
, GL_INVALID_OPERATION
,
336 "glSpecializeShaderARB(not SPIR-V)");
340 if (sh
->CompileStatus
) {
341 _mesa_error(ctx
, GL_INVALID_OPERATION
,
342 "glSpecializeShaderARB(already specialized)");
346 struct gl_shader_spirv_data
*spirv_data
= sh
->spirv_data
;
348 /* From the GL_ARB_gl_spirv spec:
350 * "The OpenGL API expects the SPIR-V module to have already been
351 * validated, and can return an error if it discovers anything invalid
352 * in the module. An invalid SPIR-V module is allowed to result in
353 * undefined behavior."
355 * However, the following errors still need to be detected (from the same
358 * "INVALID_VALUE is generated if <pEntryPoint> does not name a valid
359 * entry point for <shader>.
361 * INVALID_VALUE is generated if any element of <pConstantIndex>
362 * refers to a specialization constant that does not exist in the
363 * shader module contained in <shader>."
365 * We cannot flag those errors a-priori because detecting them requires
366 * parsing the module. However, flagging them during specialization is okay,
367 * since it makes no difference in terms of application-visible state.
369 spec_entries
= calloc(sizeof(*spec_entries
), numSpecializationConstants
);
371 for (unsigned i
= 0; i
< numSpecializationConstants
; ++i
) {
372 spec_entries
[i
].id
= pConstantIndex
[i
];
373 spec_entries
[i
].data32
= pConstantValue
[i
];
374 spec_entries
[i
].defined_on_module
= false;
378 gl_spirv_validation((uint32_t *)&spirv_data
->SpirVModule
->Binary
[0],
379 spirv_data
->SpirVModule
->Length
/ 4,
380 spec_entries
, numSpecializationConstants
,
381 sh
->Stage
, pEntryPoint
);
383 /* See previous spec comment */
384 if (!has_entry_point
) {
385 _mesa_error(ctx
, GL_INVALID_VALUE
,
386 "glSpecializeShaderARB(\"%s\" is not a valid entry point"
387 " for shader)", pEntryPoint
);
391 for (unsigned i
= 0; i
< numSpecializationConstants
; ++i
) {
392 if (spec_entries
[i
].defined_on_module
== false) {
393 _mesa_error(ctx
, GL_INVALID_VALUE
,
394 "glSpecializeShaderARB(constant \"%i\" does not exist "
395 "in shader)", spec_entries
[i
].id
);
400 spirv_data
->SpirVEntryPoint
= ralloc_strdup(spirv_data
, pEntryPoint
);
402 /* Note that we didn't make a real compilation of the module (spirv_to_nir),
403 * but just checked some error conditions. Real "compilation" will be done
404 * later, upon linking.
406 sh
->CompileStatus
= COMPILE_SUCCESS
;
408 spirv_data
->NumSpecializationConstants
= numSpecializationConstants
;
409 spirv_data
->SpecializationConstantsIndex
=
410 rzalloc_array_size(spirv_data
, sizeof(GLuint
),
411 numSpecializationConstants
);
412 spirv_data
->SpecializationConstantsValue
=
413 rzalloc_array_size(spirv_data
, sizeof(GLuint
),
414 numSpecializationConstants
);
415 for (unsigned i
= 0; i
< numSpecializationConstants
; ++i
) {
416 spirv_data
->SpecializationConstantsIndex
[i
] = pConstantIndex
[i
];
417 spirv_data
->SpecializationConstantsValue
[i
] = pConstantValue
[i
];