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"
28 #include "compiler/nir/nir.h"
29 #include "compiler/spirv/nir_spirv.h"
31 #include "program/program.h"
33 #include "util/u_atomic.h"
36 _mesa_spirv_module_reference(struct gl_spirv_module
**dest
,
37 struct gl_spirv_module
*src
)
39 struct gl_spirv_module
*old
= *dest
;
41 if (old
&& p_atomic_dec_zero(&old
->RefCount
))
47 p_atomic_inc(&src
->RefCount
);
51 _mesa_shader_spirv_data_reference(struct gl_shader_spirv_data
**dest
,
52 struct gl_shader_spirv_data
*src
)
54 struct gl_shader_spirv_data
*old
= *dest
;
56 if (old
&& p_atomic_dec_zero(&old
->RefCount
)) {
57 _mesa_spirv_module_reference(&(*dest
)->SpirVModule
, NULL
);
64 p_atomic_inc(&src
->RefCount
);
68 _mesa_spirv_shader_binary(struct gl_context
*ctx
,
69 unsigned n
, struct gl_shader
**shaders
,
70 const void* binary
, size_t length
)
72 struct gl_spirv_module
*module
;
73 struct gl_shader_spirv_data
*spirv_data
;
77 module
= malloc(sizeof(*module
) + length
);
79 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glShaderBinary");
83 p_atomic_set(&module
->RefCount
, 0);
84 module
->Length
= length
;
85 memcpy(&module
->Binary
[0], binary
, length
);
87 for (int i
= 0; i
< n
; ++i
) {
88 struct gl_shader
*sh
= shaders
[i
];
90 spirv_data
= rzalloc(NULL
, struct gl_shader_spirv_data
);
91 _mesa_shader_spirv_data_reference(&sh
->spirv_data
, spirv_data
);
92 _mesa_spirv_module_reference(&spirv_data
->SpirVModule
, module
);
94 sh
->CompileStatus
= COMPILE_FAILURE
;
96 free((void *)sh
->Source
);
98 free((void *)sh
->FallbackSource
);
99 sh
->FallbackSource
= NULL
;
103 ralloc_free(sh
->symbols
);
109 * This is the equivalent to compiler/glsl/linker.cpp::link_shaders()
110 * but for SPIR-V programs.
112 * This method just creates the gl_linked_shader structs with a reference to
113 * the SPIR-V data collected during previous steps.
115 * The real linking happens later in the driver-specifc call LinkShader().
116 * This is so backends can implement different linking strategies for
120 _mesa_spirv_link_shaders(struct gl_context
*ctx
, struct gl_shader_program
*prog
)
122 prog
->data
->LinkStatus
= LINKING_SUCCESS
;
123 prog
->data
->Validated
= false;
125 for (unsigned i
= 0; i
< prog
->NumShaders
; i
++) {
126 struct gl_shader
*shader
= prog
->Shaders
[i
];
127 gl_shader_stage shader_type
= shader
->Stage
;
129 /* We only support one shader per stage. The gl_spirv spec doesn't seem
130 * to prevent this, but the way the API is designed, requiring all shaders
131 * to be specialized with an entry point, makes supporting this quite
134 * TODO: Turn this into a proper error once the spec bug
135 * <https://gitlab.khronos.org/opengl/API/issues/58> is resolved.
137 if (prog
->_LinkedShaders
[shader_type
]) {
138 ralloc_strcat(&prog
->data
->InfoLog
,
139 "\nError trying to link more than one SPIR-V shader "
141 prog
->data
->LinkStatus
= LINKING_FAILURE
;
145 assert(shader
->spirv_data
);
147 struct gl_linked_shader
*linked
= rzalloc(NULL
, struct gl_linked_shader
);
148 linked
->Stage
= shader_type
;
150 /* Create program and attach it to the linked shader */
151 struct gl_program
*gl_prog
=
152 ctx
->Driver
.NewProgram(ctx
,
153 _mesa_shader_stage_to_program(shader_type
),
156 prog
->data
->LinkStatus
= LINKING_FAILURE
;
157 _mesa_delete_linked_shader(ctx
, linked
);
161 _mesa_reference_shader_program_data(ctx
,
165 /* Don't use _mesa_reference_program() just take ownership */
166 linked
->Program
= gl_prog
;
168 /* Reference the SPIR-V data from shader to the linked shader */
169 _mesa_shader_spirv_data_reference(&linked
->spirv_data
,
172 prog
->_LinkedShaders
[shader_type
] = linked
;
173 prog
->data
->linked_stages
|= 1 << shader_type
;
178 _mesa_SpecializeShaderARB(GLuint shader
,
179 const GLchar
*pEntryPoint
,
180 GLuint numSpecializationConstants
,
181 const GLuint
*pConstantIndex
,
182 const GLuint
*pConstantValue
)
184 GET_CURRENT_CONTEXT(ctx
);
185 struct gl_shader
*sh
;
186 bool has_entry_point
;
187 struct nir_spirv_specialization
*spec_entries
= NULL
;
189 if (!ctx
->Extensions
.ARB_gl_spirv
) {
190 _mesa_error(ctx
, GL_INVALID_OPERATION
, "glSpecializeShaderARB");
194 sh
= _mesa_lookup_shader_err(ctx
, shader
, "glSpecializeShaderARB");
198 if (!sh
->spirv_data
) {
199 _mesa_error(ctx
, GL_INVALID_OPERATION
,
200 "glSpecializeShaderARB(not SPIR-V)");
204 if (sh
->CompileStatus
) {
205 _mesa_error(ctx
, GL_INVALID_OPERATION
,
206 "glSpecializeShaderARB(already specialized)");
210 struct gl_shader_spirv_data
*spirv_data
= sh
->spirv_data
;
212 /* From the GL_ARB_gl_spirv spec:
214 * "The OpenGL API expects the SPIR-V module to have already been
215 * validated, and can return an error if it discovers anything invalid
216 * in the module. An invalid SPIR-V module is allowed to result in
217 * undefined behavior."
219 * However, the following errors still need to be detected (from the same
222 * "INVALID_VALUE is generated if <pEntryPoint> does not name a valid
223 * entry point for <shader>.
225 * INVALID_VALUE is generated if any element of <pConstantIndex>
226 * refers to a specialization constant that does not exist in the
227 * shader module contained in <shader>."
229 * We cannot flag those errors a-priori because detecting them requires
230 * parsing the module. However, flagging them during specialization is okay,
231 * since it makes no difference in terms of application-visible state.
233 spec_entries
= calloc(sizeof(*spec_entries
), numSpecializationConstants
);
235 for (unsigned i
= 0; i
< numSpecializationConstants
; ++i
) {
236 spec_entries
[i
].id
= pConstantIndex
[i
];
237 spec_entries
[i
].data32
= pConstantValue
[i
];
238 spec_entries
[i
].defined_on_module
= false;
242 gl_spirv_validation((uint32_t *)&spirv_data
->SpirVModule
->Binary
[0],
243 spirv_data
->SpirVModule
->Length
/ 4,
244 spec_entries
, numSpecializationConstants
,
245 sh
->Stage
, pEntryPoint
);
247 /* See previous spec comment */
248 if (!has_entry_point
) {
249 _mesa_error(ctx
, GL_INVALID_VALUE
,
250 "glSpecializeShaderARB(\"%s\" is not a valid entry point"
251 " for shader)", pEntryPoint
);
255 for (unsigned i
= 0; i
< numSpecializationConstants
; ++i
) {
256 if (spec_entries
[i
].defined_on_module
== false) {
257 _mesa_error(ctx
, GL_INVALID_VALUE
,
258 "glSpecializeShaderARB(constant \"%i\" does not exist "
259 "in shader)", spec_entries
[i
].id
);
264 spirv_data
->SpirVEntryPoint
= ralloc_strdup(spirv_data
, pEntryPoint
);
266 /* Note that we didn't make a real compilation of the module (spirv_to_nir),
267 * but just checked some error conditions. Real "compilation" will be done
268 * later, upon linking.
270 sh
->CompileStatus
= COMPILE_SUCCESS
;
272 spirv_data
->NumSpecializationConstants
= numSpecializationConstants
;
273 spirv_data
->SpecializationConstantsIndex
=
274 rzalloc_array_size(spirv_data
, sizeof(GLuint
),
275 numSpecializationConstants
);
276 spirv_data
->SpecializationConstantsValue
=
277 rzalloc_array_size(spirv_data
, sizeof(GLuint
),
278 numSpecializationConstants
);
279 for (unsigned i
= 0; i
< numSpecializationConstants
; ++i
) {
280 spirv_data
->SpecializationConstantsIndex
[i
] = pConstantIndex
[i
];
281 spirv_data
->SpecializationConstantsValue
[i
] = pConstantValue
[i
];