mesa/glspirv: Add a _mesa_spirv_to_nir() function
[mesa.git] / src / mesa / main / glspirv.c
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
28 #include "compiler/nir/nir.h"
29 #include "compiler/spirv/nir_spirv.h"
30
31 #include "program/program.h"
32
33 #include "util/u_atomic.h"
34
35 void
36 _mesa_spirv_module_reference(struct gl_spirv_module **dest,
37 struct gl_spirv_module *src)
38 {
39 struct gl_spirv_module *old = *dest;
40
41 if (old && p_atomic_dec_zero(&old->RefCount))
42 free(old);
43
44 *dest = src;
45
46 if (src)
47 p_atomic_inc(&src->RefCount);
48 }
49
50 void
51 _mesa_shader_spirv_data_reference(struct gl_shader_spirv_data **dest,
52 struct gl_shader_spirv_data *src)
53 {
54 struct gl_shader_spirv_data *old = *dest;
55
56 if (old && p_atomic_dec_zero(&old->RefCount)) {
57 _mesa_spirv_module_reference(&(*dest)->SpirVModule, NULL);
58 ralloc_free(old);
59 }
60
61 *dest = src;
62
63 if (src)
64 p_atomic_inc(&src->RefCount);
65 }
66
67 void
68 _mesa_spirv_shader_binary(struct gl_context *ctx,
69 unsigned n, struct gl_shader **shaders,
70 const void* binary, size_t length)
71 {
72 struct gl_spirv_module *module;
73 struct gl_shader_spirv_data *spirv_data;
74
75 assert(length >= 0);
76
77 module = malloc(sizeof(*module) + length);
78 if (!module) {
79 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glShaderBinary");
80 return;
81 }
82
83 p_atomic_set(&module->RefCount, 0);
84 module->Length = length;
85 memcpy(&module->Binary[0], binary, length);
86
87 for (int i = 0; i < n; ++i) {
88 struct gl_shader *sh = shaders[i];
89
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);
93
94 sh->CompileStatus = COMPILE_FAILURE;
95
96 free((void *)sh->Source);
97 sh->Source = NULL;
98 free((void *)sh->FallbackSource);
99 sh->FallbackSource = NULL;
100
101 ralloc_free(sh->ir);
102 sh->ir = NULL;
103 ralloc_free(sh->symbols);
104 sh->symbols = NULL;
105 }
106 }
107
108 /**
109 * This is the equivalent to compiler/glsl/linker.cpp::link_shaders()
110 * but for SPIR-V programs.
111 *
112 * This method just creates the gl_linked_shader structs with a reference to
113 * the SPIR-V data collected during previous steps.
114 *
115 * The real linking happens later in the driver-specifc call LinkShader().
116 * This is so backends can implement different linking strategies for
117 * SPIR-V programs.
118 */
119 void
120 _mesa_spirv_link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
121 {
122 prog->data->LinkStatus = LINKING_SUCCESS;
123 prog->data->Validated = false;
124
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;
128
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
132 * undefined.
133 *
134 * TODO: Turn this into a proper error once the spec bug
135 * <https://gitlab.khronos.org/opengl/API/issues/58> is resolved.
136 */
137 if (prog->_LinkedShaders[shader_type]) {
138 ralloc_strcat(&prog->data->InfoLog,
139 "\nError trying to link more than one SPIR-V shader "
140 "per stage.\n");
141 prog->data->LinkStatus = LINKING_FAILURE;
142 return;
143 }
144
145 assert(shader->spirv_data);
146
147 struct gl_linked_shader *linked = rzalloc(NULL, struct gl_linked_shader);
148 linked->Stage = shader_type;
149
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),
154 prog->Name, false);
155 if (!gl_prog) {
156 prog->data->LinkStatus = LINKING_FAILURE;
157 _mesa_delete_linked_shader(ctx, linked);
158 return;
159 }
160
161 _mesa_reference_shader_program_data(ctx,
162 &gl_prog->sh.data,
163 prog->data);
164
165 /* Don't use _mesa_reference_program() just take ownership */
166 linked->Program = gl_prog;
167
168 /* Reference the SPIR-V data from shader to the linked shader */
169 _mesa_shader_spirv_data_reference(&linked->spirv_data,
170 shader->spirv_data);
171
172 prog->_LinkedShaders[shader_type] = linked;
173 prog->data->linked_stages |= 1 << shader_type;
174 }
175 }
176
177 nir_shader *
178 _mesa_spirv_to_nir(struct gl_context *ctx,
179 const struct gl_shader_program *prog,
180 gl_shader_stage stage,
181 const nir_shader_compiler_options *options)
182 {
183 nir_shader *nir = NULL;
184
185 struct gl_linked_shader *linked_shader = prog->_LinkedShaders[stage];
186 assert (linked_shader);
187
188 struct gl_shader_spirv_data *spirv_data = linked_shader->spirv_data;
189 assert(spirv_data);
190
191 struct gl_spirv_module *spirv_module = spirv_data->SpirVModule;
192 assert (spirv_module != NULL);
193
194 const char *entry_point_name = spirv_data->SpirVEntryPoint;
195 assert(entry_point_name);
196
197 struct nir_spirv_specialization *spec_entries =
198 calloc(sizeof(*spec_entries),
199 spirv_data->NumSpecializationConstants);
200
201 for (unsigned i = 0; i < spirv_data->NumSpecializationConstants; ++i) {
202 spec_entries[i].id = spirv_data->SpecializationConstantsIndex[i];
203 spec_entries[i].data32 = spirv_data->SpecializationConstantsValue[i];
204 spec_entries[i].defined_on_module = false;
205 }
206
207 const struct spirv_to_nir_options spirv_options = {
208 .caps = ctx->Const.SpirVCapabilities
209 };
210
211 nir_function *entry_point =
212 spirv_to_nir((const uint32_t *) &spirv_module->Binary[0],
213 spirv_module->Length / 4,
214 spec_entries, spirv_data->NumSpecializationConstants,
215 stage, entry_point_name,
216 &spirv_options,
217 options);
218 free(spec_entries);
219
220 assert (entry_point);
221 nir = entry_point->shader;
222 assert(nir->info.stage == stage);
223
224 nir->options = options;
225
226 nir->info.name =
227 ralloc_asprintf(nir, "SPIRV:%s:%d",
228 _mesa_shader_stage_to_abbrev(nir->info.stage),
229 prog->Name);
230 nir_validate_shader(nir);
231
232 return nir;
233 }
234
235 void GLAPIENTRY
236 _mesa_SpecializeShaderARB(GLuint shader,
237 const GLchar *pEntryPoint,
238 GLuint numSpecializationConstants,
239 const GLuint *pConstantIndex,
240 const GLuint *pConstantValue)
241 {
242 GET_CURRENT_CONTEXT(ctx);
243 struct gl_shader *sh;
244 bool has_entry_point;
245 struct nir_spirv_specialization *spec_entries = NULL;
246
247 if (!ctx->Extensions.ARB_gl_spirv) {
248 _mesa_error(ctx, GL_INVALID_OPERATION, "glSpecializeShaderARB");
249 return;
250 }
251
252 sh = _mesa_lookup_shader_err(ctx, shader, "glSpecializeShaderARB");
253 if (!sh)
254 return;
255
256 if (!sh->spirv_data) {
257 _mesa_error(ctx, GL_INVALID_OPERATION,
258 "glSpecializeShaderARB(not SPIR-V)");
259 return;
260 }
261
262 if (sh->CompileStatus) {
263 _mesa_error(ctx, GL_INVALID_OPERATION,
264 "glSpecializeShaderARB(already specialized)");
265 return;
266 }
267
268 struct gl_shader_spirv_data *spirv_data = sh->spirv_data;
269
270 /* From the GL_ARB_gl_spirv spec:
271 *
272 * "The OpenGL API expects the SPIR-V module to have already been
273 * validated, and can return an error if it discovers anything invalid
274 * in the module. An invalid SPIR-V module is allowed to result in
275 * undefined behavior."
276 *
277 * However, the following errors still need to be detected (from the same
278 * spec):
279 *
280 * "INVALID_VALUE is generated if <pEntryPoint> does not name a valid
281 * entry point for <shader>.
282 *
283 * INVALID_VALUE is generated if any element of <pConstantIndex>
284 * refers to a specialization constant that does not exist in the
285 * shader module contained in <shader>."
286 *
287 * We cannot flag those errors a-priori because detecting them requires
288 * parsing the module. However, flagging them during specialization is okay,
289 * since it makes no difference in terms of application-visible state.
290 */
291 spec_entries = calloc(sizeof(*spec_entries), numSpecializationConstants);
292
293 for (unsigned i = 0; i < numSpecializationConstants; ++i) {
294 spec_entries[i].id = pConstantIndex[i];
295 spec_entries[i].data32 = pConstantValue[i];
296 spec_entries[i].defined_on_module = false;
297 }
298
299 has_entry_point =
300 gl_spirv_validation((uint32_t *)&spirv_data->SpirVModule->Binary[0],
301 spirv_data->SpirVModule->Length / 4,
302 spec_entries, numSpecializationConstants,
303 sh->Stage, pEntryPoint);
304
305 /* See previous spec comment */
306 if (!has_entry_point) {
307 _mesa_error(ctx, GL_INVALID_VALUE,
308 "glSpecializeShaderARB(\"%s\" is not a valid entry point"
309 " for shader)", pEntryPoint);
310 goto end;
311 }
312
313 for (unsigned i = 0; i < numSpecializationConstants; ++i) {
314 if (spec_entries[i].defined_on_module == false) {
315 _mesa_error(ctx, GL_INVALID_VALUE,
316 "glSpecializeShaderARB(constant \"%i\" does not exist "
317 "in shader)", spec_entries[i].id);
318 goto end;
319 }
320 }
321
322 spirv_data->SpirVEntryPoint = ralloc_strdup(spirv_data, pEntryPoint);
323
324 /* Note that we didn't make a real compilation of the module (spirv_to_nir),
325 * but just checked some error conditions. Real "compilation" will be done
326 * later, upon linking.
327 */
328 sh->CompileStatus = COMPILE_SUCCESS;
329
330 spirv_data->NumSpecializationConstants = numSpecializationConstants;
331 spirv_data->SpecializationConstantsIndex =
332 rzalloc_array_size(spirv_data, sizeof(GLuint),
333 numSpecializationConstants);
334 spirv_data->SpecializationConstantsValue =
335 rzalloc_array_size(spirv_data, sizeof(GLuint),
336 numSpecializationConstants);
337 for (unsigned i = 0; i < numSpecializationConstants; ++i) {
338 spirv_data->SpecializationConstantsIndex[i] = pConstantIndex[i];
339 spirv_data->SpecializationConstantsValue[i] = pConstantValue[i];
340 }
341
342 end:
343 free(spec_entries);
344 }