1b8e80941Smrg/*
2b8e80941Smrg * Copyright 2017 Advanced Micro Devices, Inc.
3b8e80941Smrg *
4b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5b8e80941Smrg * copy of this software and associated documentation files (the "Software"),
6b8e80941Smrg * to deal in the Software without restriction, including without limitation
7b8e80941Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
8b8e80941Smrg * license, and/or sell copies of the Software, and to permit persons to whom
9b8e80941Smrg * the Software is furnished to do so, subject to the following conditions:
10b8e80941Smrg *
11b8e80941Smrg * The above copyright notice and this permission notice (including the next
12b8e80941Smrg * paragraph) shall be included in all copies or substantial portions of the
13b8e80941Smrg * Software.
14b8e80941Smrg *
15b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16b8e80941Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17b8e80941Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18b8e80941Smrg * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19b8e80941Smrg * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20b8e80941Smrg * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21b8e80941Smrg * USE OR OTHER DEALINGS IN THE SOFTWARE.
22b8e80941Smrg */
23b8e80941Smrg
24b8e80941Smrg#include "glspirv.h"
25b8e80941Smrg#include "errors.h"
26b8e80941Smrg#include "shaderobj.h"
27b8e80941Smrg#include "mtypes.h"
28b8e80941Smrg
29b8e80941Smrg#include "compiler/nir/nir.h"
30b8e80941Smrg#include "compiler/spirv/nir_spirv.h"
31b8e80941Smrg
32b8e80941Smrg#include "program/program.h"
33b8e80941Smrg
34b8e80941Smrg#include "util/u_atomic.h"
35b8e80941Smrg
36b8e80941Smrgvoid
37b8e80941Smrg_mesa_spirv_module_reference(struct gl_spirv_module **dest,
38b8e80941Smrg                             struct gl_spirv_module *src)
39b8e80941Smrg{
40b8e80941Smrg   struct gl_spirv_module *old = *dest;
41b8e80941Smrg
42b8e80941Smrg   if (old && p_atomic_dec_zero(&old->RefCount))
43b8e80941Smrg      free(old);
44b8e80941Smrg
45b8e80941Smrg   *dest = src;
46b8e80941Smrg
47b8e80941Smrg   if (src)
48b8e80941Smrg      p_atomic_inc(&src->RefCount);
49b8e80941Smrg}
50b8e80941Smrg
51b8e80941Smrgvoid
52b8e80941Smrg_mesa_shader_spirv_data_reference(struct gl_shader_spirv_data **dest,
53b8e80941Smrg                                  struct gl_shader_spirv_data *src)
54b8e80941Smrg{
55b8e80941Smrg   struct gl_shader_spirv_data *old = *dest;
56b8e80941Smrg
57b8e80941Smrg   if (old && p_atomic_dec_zero(&old->RefCount)) {
58b8e80941Smrg      _mesa_spirv_module_reference(&(*dest)->SpirVModule, NULL);
59b8e80941Smrg      ralloc_free(old);
60b8e80941Smrg   }
61b8e80941Smrg
62b8e80941Smrg   *dest = src;
63b8e80941Smrg
64b8e80941Smrg   if (src)
65b8e80941Smrg      p_atomic_inc(&src->RefCount);
66b8e80941Smrg}
67b8e80941Smrg
68b8e80941Smrgvoid
69b8e80941Smrg_mesa_spirv_shader_binary(struct gl_context *ctx,
70b8e80941Smrg                          unsigned n, struct gl_shader **shaders,
71b8e80941Smrg                          const void* binary, size_t length)
72b8e80941Smrg{
73b8e80941Smrg   struct gl_spirv_module *module;
74b8e80941Smrg   struct gl_shader_spirv_data *spirv_data;
75b8e80941Smrg
76b8e80941Smrg   module = malloc(sizeof(*module) + length);
77b8e80941Smrg   if (!module) {
78b8e80941Smrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glShaderBinary");
79b8e80941Smrg      return;
80b8e80941Smrg   }
81b8e80941Smrg
82b8e80941Smrg   p_atomic_set(&module->RefCount, 0);
83b8e80941Smrg   module->Length = length;
84b8e80941Smrg   memcpy(&module->Binary[0], binary, length);
85b8e80941Smrg
86b8e80941Smrg   for (int i = 0; i < n; ++i) {
87b8e80941Smrg      struct gl_shader *sh = shaders[i];
88b8e80941Smrg
89b8e80941Smrg      spirv_data = rzalloc(NULL, struct gl_shader_spirv_data);
90b8e80941Smrg      _mesa_shader_spirv_data_reference(&sh->spirv_data, spirv_data);
91b8e80941Smrg      _mesa_spirv_module_reference(&spirv_data->SpirVModule, module);
92b8e80941Smrg
93b8e80941Smrg      sh->CompileStatus = COMPILE_FAILURE;
94b8e80941Smrg
95b8e80941Smrg      free((void *)sh->Source);
96b8e80941Smrg      sh->Source = NULL;
97b8e80941Smrg      free((void *)sh->FallbackSource);
98b8e80941Smrg      sh->FallbackSource = NULL;
99b8e80941Smrg
100b8e80941Smrg      ralloc_free(sh->ir);
101b8e80941Smrg      sh->ir = NULL;
102b8e80941Smrg      ralloc_free(sh->symbols);
103b8e80941Smrg      sh->symbols = NULL;
104b8e80941Smrg   }
105b8e80941Smrg}
106b8e80941Smrg
107b8e80941Smrg/**
108b8e80941Smrg * This is the equivalent to compiler/glsl/linker.cpp::link_shaders()
109b8e80941Smrg * but for SPIR-V programs.
110b8e80941Smrg *
111b8e80941Smrg * This method just creates the gl_linked_shader structs with a reference to
112b8e80941Smrg * the SPIR-V data collected during previous steps.
113b8e80941Smrg *
114b8e80941Smrg * The real linking happens later in the driver-specifc call LinkShader().
115b8e80941Smrg * This is so backends can implement different linking strategies for
116b8e80941Smrg * SPIR-V programs.
117b8e80941Smrg */
118b8e80941Smrgvoid
119b8e80941Smrg_mesa_spirv_link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
120b8e80941Smrg{
121b8e80941Smrg   prog->data->LinkStatus = LINKING_SUCCESS;
122b8e80941Smrg   prog->data->Validated = false;
123b8e80941Smrg
124b8e80941Smrg   for (unsigned i = 0; i < prog->NumShaders; i++) {
125b8e80941Smrg      struct gl_shader *shader = prog->Shaders[i];
126b8e80941Smrg      gl_shader_stage shader_type = shader->Stage;
127b8e80941Smrg
128b8e80941Smrg      /* We only support one shader per stage. The gl_spirv spec doesn't seem
129b8e80941Smrg       * to prevent this, but the way the API is designed, requiring all shaders
130b8e80941Smrg       * to be specialized with an entry point, makes supporting this quite
131b8e80941Smrg       * undefined.
132b8e80941Smrg       *
133b8e80941Smrg       * TODO: Turn this into a proper error once the spec bug
134b8e80941Smrg       * <https://gitlab.khronos.org/opengl/API/issues/58> is resolved.
135b8e80941Smrg       */
136b8e80941Smrg      if (prog->_LinkedShaders[shader_type]) {
137b8e80941Smrg         ralloc_strcat(&prog->data->InfoLog,
138b8e80941Smrg                       "\nError trying to link more than one SPIR-V shader "
139b8e80941Smrg                       "per stage.\n");
140b8e80941Smrg         prog->data->LinkStatus = LINKING_FAILURE;
141b8e80941Smrg         return;
142b8e80941Smrg      }
143b8e80941Smrg
144b8e80941Smrg      assert(shader->spirv_data);
145b8e80941Smrg
146b8e80941Smrg      struct gl_linked_shader *linked = rzalloc(NULL, struct gl_linked_shader);
147b8e80941Smrg      linked->Stage = shader_type;
148b8e80941Smrg
149b8e80941Smrg      /* Create program and attach it to the linked shader */
150b8e80941Smrg      struct gl_program *gl_prog =
151b8e80941Smrg         ctx->Driver.NewProgram(ctx,
152b8e80941Smrg                                _mesa_shader_stage_to_program(shader_type),
153b8e80941Smrg                                prog->Name, false);
154b8e80941Smrg      if (!gl_prog) {
155b8e80941Smrg         prog->data->LinkStatus = LINKING_FAILURE;
156b8e80941Smrg         _mesa_delete_linked_shader(ctx, linked);
157b8e80941Smrg         return;
158b8e80941Smrg      }
159b8e80941Smrg
160b8e80941Smrg      _mesa_reference_shader_program_data(ctx,
161b8e80941Smrg                                          &gl_prog->sh.data,
162b8e80941Smrg                                          prog->data);
163b8e80941Smrg
164b8e80941Smrg      /* Don't use _mesa_reference_program() just take ownership */
165b8e80941Smrg      linked->Program = gl_prog;
166b8e80941Smrg
167b8e80941Smrg      /* Reference the SPIR-V data from shader to the linked shader */
168b8e80941Smrg      _mesa_shader_spirv_data_reference(&linked->spirv_data,
169b8e80941Smrg                                        shader->spirv_data);
170b8e80941Smrg
171b8e80941Smrg      prog->_LinkedShaders[shader_type] = linked;
172b8e80941Smrg      prog->data->linked_stages |= 1 << shader_type;
173b8e80941Smrg   }
174b8e80941Smrg
175b8e80941Smrg   int last_vert_stage =
176b8e80941Smrg      util_last_bit(prog->data->linked_stages &
177b8e80941Smrg                    ((1 << (MESA_SHADER_GEOMETRY + 1)) - 1));
178b8e80941Smrg
179b8e80941Smrg   if (last_vert_stage)
180b8e80941Smrg      prog->last_vert_prog = prog->_LinkedShaders[last_vert_stage - 1]->Program;
181b8e80941Smrg}
182b8e80941Smrg
183b8e80941Smrgnir_shader *
184b8e80941Smrg_mesa_spirv_to_nir(struct gl_context *ctx,
185b8e80941Smrg                   const struct gl_shader_program *prog,
186b8e80941Smrg                   gl_shader_stage stage,
187b8e80941Smrg                   const nir_shader_compiler_options *options)
188b8e80941Smrg{
189b8e80941Smrg   nir_shader *nir = NULL;
190b8e80941Smrg
191b8e80941Smrg   struct gl_linked_shader *linked_shader = prog->_LinkedShaders[stage];
192b8e80941Smrg   assert (linked_shader);
193b8e80941Smrg
194b8e80941Smrg   struct gl_shader_spirv_data *spirv_data = linked_shader->spirv_data;
195b8e80941Smrg   assert(spirv_data);
196b8e80941Smrg
197b8e80941Smrg   struct gl_spirv_module *spirv_module = spirv_data->SpirVModule;
198b8e80941Smrg   assert (spirv_module != NULL);
199b8e80941Smrg
200b8e80941Smrg   const char *entry_point_name = spirv_data->SpirVEntryPoint;
201b8e80941Smrg   assert(entry_point_name);
202b8e80941Smrg
203b8e80941Smrg   struct nir_spirv_specialization *spec_entries =
204b8e80941Smrg      calloc(sizeof(*spec_entries),
205b8e80941Smrg             spirv_data->NumSpecializationConstants);
206b8e80941Smrg
207b8e80941Smrg   for (unsigned i = 0; i < spirv_data->NumSpecializationConstants; ++i) {
208b8e80941Smrg      spec_entries[i].id = spirv_data->SpecializationConstantsIndex[i];
209b8e80941Smrg      spec_entries[i].data32 = spirv_data->SpecializationConstantsValue[i];
210b8e80941Smrg      spec_entries[i].defined_on_module = false;
211b8e80941Smrg   }
212b8e80941Smrg
213b8e80941Smrg   const struct spirv_to_nir_options spirv_options = {
214b8e80941Smrg      .environment = NIR_SPIRV_OPENGL,
215b8e80941Smrg      .lower_workgroup_access_to_offsets = true,
216b8e80941Smrg      .lower_ubo_ssbo_access_to_offsets = true,
217b8e80941Smrg      .caps = ctx->Const.SpirVCapabilities
218b8e80941Smrg   };
219b8e80941Smrg
220b8e80941Smrg   nir_function *entry_point =
221b8e80941Smrg      spirv_to_nir((const uint32_t *) &spirv_module->Binary[0],
222b8e80941Smrg                   spirv_module->Length / 4,
223b8e80941Smrg                   spec_entries, spirv_data->NumSpecializationConstants,
224b8e80941Smrg                   stage, entry_point_name,
225b8e80941Smrg                   &spirv_options,
226b8e80941Smrg                   options);
227b8e80941Smrg   free(spec_entries);
228b8e80941Smrg
229b8e80941Smrg   assert (entry_point);
230b8e80941Smrg   nir = entry_point->shader;
231b8e80941Smrg   assert(nir->info.stage == stage);
232b8e80941Smrg
233b8e80941Smrg   nir->options = options;
234b8e80941Smrg
235b8e80941Smrg   nir->info.name =
236b8e80941Smrg      ralloc_asprintf(nir, "SPIRV:%s:%d",
237b8e80941Smrg                      _mesa_shader_stage_to_abbrev(nir->info.stage),
238b8e80941Smrg                      prog->Name);
239b8e80941Smrg   nir_validate_shader(nir, "after spirv_to_nir");
240b8e80941Smrg
241b8e80941Smrg   nir->info.separate_shader = linked_shader->Program->info.separate_shader;
242b8e80941Smrg
243b8e80941Smrg   /* We have to lower away local constant initializers right before we
244b8e80941Smrg    * inline functions.  That way they get properly initialized at the top
245b8e80941Smrg    * of the function and not at the top of its caller.
246b8e80941Smrg    */
247b8e80941Smrg   NIR_PASS_V(nir, nir_lower_constant_initializers, nir_var_function_temp);
248b8e80941Smrg   NIR_PASS_V(nir, nir_lower_returns);
249b8e80941Smrg   NIR_PASS_V(nir, nir_inline_functions);
250b8e80941Smrg   NIR_PASS_V(nir, nir_opt_deref);
251b8e80941Smrg
252b8e80941Smrg   /* Pick off the single entrypoint that we want */
253b8e80941Smrg   foreach_list_typed_safe(nir_function, func, node, &nir->functions) {
254b8e80941Smrg      if (func != entry_point)
255b8e80941Smrg         exec_node_remove(&func->node);
256b8e80941Smrg   }
257b8e80941Smrg   assert(exec_list_length(&nir->functions) == 1);
258b8e80941Smrg
259b8e80941Smrg   /* Split member structs.  We do this before lower_io_to_temporaries so that
260b8e80941Smrg    * it doesn't lower system values to temporaries by accident.
261b8e80941Smrg    */
262b8e80941Smrg   NIR_PASS_V(nir, nir_split_var_copies);
263b8e80941Smrg   NIR_PASS_V(nir, nir_split_per_member_structs);
264b8e80941Smrg
265b8e80941Smrg   if (nir->info.stage == MESA_SHADER_VERTEX)
266b8e80941Smrg      nir_remap_dual_slot_attributes(nir, &linked_shader->Program->DualSlotInputs);
267b8e80941Smrg
268b8e80941Smrg   return nir;
269b8e80941Smrg}
270b8e80941Smrg
271b8e80941Smrgvoid GLAPIENTRY
272b8e80941Smrg_mesa_SpecializeShaderARB(GLuint shader,
273b8e80941Smrg                          const GLchar *pEntryPoint,
274b8e80941Smrg                          GLuint numSpecializationConstants,
275b8e80941Smrg                          const GLuint *pConstantIndex,
276b8e80941Smrg                          const GLuint *pConstantValue)
277b8e80941Smrg{
278b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
279b8e80941Smrg   struct gl_shader *sh;
280b8e80941Smrg   bool has_entry_point;
281b8e80941Smrg   struct nir_spirv_specialization *spec_entries = NULL;
282b8e80941Smrg
283b8e80941Smrg   if (!ctx->Extensions.ARB_gl_spirv) {
284b8e80941Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "glSpecializeShaderARB");
285b8e80941Smrg      return;
286b8e80941Smrg   }
287b8e80941Smrg
288b8e80941Smrg   sh = _mesa_lookup_shader_err(ctx, shader, "glSpecializeShaderARB");
289b8e80941Smrg   if (!sh)
290b8e80941Smrg      return;
291b8e80941Smrg
292b8e80941Smrg   if (!sh->spirv_data) {
293b8e80941Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
294b8e80941Smrg                  "glSpecializeShaderARB(not SPIR-V)");
295b8e80941Smrg      return;
296b8e80941Smrg   }
297b8e80941Smrg
298b8e80941Smrg   if (sh->CompileStatus) {
299b8e80941Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
300b8e80941Smrg                  "glSpecializeShaderARB(already specialized)");
301b8e80941Smrg      return;
302b8e80941Smrg   }
303b8e80941Smrg
304b8e80941Smrg   struct gl_shader_spirv_data *spirv_data = sh->spirv_data;
305b8e80941Smrg
306b8e80941Smrg   /* From the GL_ARB_gl_spirv spec:
307b8e80941Smrg    *
308b8e80941Smrg    *    "The OpenGL API expects the SPIR-V module to have already been
309b8e80941Smrg    *     validated, and can return an error if it discovers anything invalid
310b8e80941Smrg    *     in the module. An invalid SPIR-V module is allowed to result in
311b8e80941Smrg    *     undefined behavior."
312b8e80941Smrg    *
313b8e80941Smrg    * However, the following errors still need to be detected (from the same
314b8e80941Smrg    * spec):
315b8e80941Smrg    *
316b8e80941Smrg    *    "INVALID_VALUE is generated if <pEntryPoint> does not name a valid
317b8e80941Smrg    *     entry point for <shader>.
318b8e80941Smrg    *
319b8e80941Smrg    *     INVALID_VALUE is generated if any element of <pConstantIndex>
320b8e80941Smrg    *     refers to a specialization constant that does not exist in the
321b8e80941Smrg    *     shader module contained in <shader>."
322b8e80941Smrg    *
323b8e80941Smrg    * We cannot flag those errors a-priori because detecting them requires
324b8e80941Smrg    * parsing the module. However, flagging them during specialization is okay,
325b8e80941Smrg    * since it makes no difference in terms of application-visible state.
326b8e80941Smrg    */
327b8e80941Smrg   spec_entries = calloc(sizeof(*spec_entries), numSpecializationConstants);
328b8e80941Smrg
329b8e80941Smrg   for (unsigned i = 0; i < numSpecializationConstants; ++i) {
330b8e80941Smrg      spec_entries[i].id = pConstantIndex[i];
331b8e80941Smrg      spec_entries[i].data32 = pConstantValue[i];
332b8e80941Smrg      spec_entries[i].defined_on_module = false;
333b8e80941Smrg   }
334b8e80941Smrg
335b8e80941Smrg   has_entry_point =
336b8e80941Smrg      gl_spirv_validation((uint32_t *)&spirv_data->SpirVModule->Binary[0],
337b8e80941Smrg                          spirv_data->SpirVModule->Length / 4,
338b8e80941Smrg                          spec_entries, numSpecializationConstants,
339b8e80941Smrg                          sh->Stage, pEntryPoint);
340b8e80941Smrg
341b8e80941Smrg   /* See previous spec comment */
342b8e80941Smrg   if (!has_entry_point) {
343b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE,
344b8e80941Smrg                  "glSpecializeShaderARB(\"%s\" is not a valid entry point"
345b8e80941Smrg                  " for shader)", pEntryPoint);
346b8e80941Smrg      goto end;
347b8e80941Smrg   }
348b8e80941Smrg
349b8e80941Smrg   for (unsigned i = 0; i < numSpecializationConstants; ++i) {
350b8e80941Smrg      if (spec_entries[i].defined_on_module == false) {
351b8e80941Smrg         _mesa_error(ctx, GL_INVALID_VALUE,
352b8e80941Smrg                     "glSpecializeShaderARB(constant \"%i\" does not exist "
353b8e80941Smrg                     "in shader)", spec_entries[i].id);
354b8e80941Smrg         goto end;
355b8e80941Smrg      }
356b8e80941Smrg   }
357b8e80941Smrg
358b8e80941Smrg   spirv_data->SpirVEntryPoint = ralloc_strdup(spirv_data, pEntryPoint);
359b8e80941Smrg
360b8e80941Smrg   /* Note that we didn't make a real compilation of the module (spirv_to_nir),
361b8e80941Smrg    * but just checked some error conditions. Real "compilation" will be done
362b8e80941Smrg    * later, upon linking.
363b8e80941Smrg    */
364b8e80941Smrg   sh->CompileStatus = COMPILE_SUCCESS;
365b8e80941Smrg
366b8e80941Smrg   spirv_data->NumSpecializationConstants = numSpecializationConstants;
367b8e80941Smrg   spirv_data->SpecializationConstantsIndex =
368b8e80941Smrg      rzalloc_array_size(spirv_data, sizeof(GLuint),
369b8e80941Smrg                         numSpecializationConstants);
370b8e80941Smrg   spirv_data->SpecializationConstantsValue =
371b8e80941Smrg      rzalloc_array_size(spirv_data, sizeof(GLuint),
372b8e80941Smrg                         numSpecializationConstants);
373b8e80941Smrg   for (unsigned i = 0; i < numSpecializationConstants; ++i) {
374b8e80941Smrg      spirv_data->SpecializationConstantsIndex[i] = pConstantIndex[i];
375b8e80941Smrg      spirv_data->SpecializationConstantsValue[i] = pConstantValue[i];
376b8e80941Smrg   }
377b8e80941Smrg
378b8e80941Smrg end:
379b8e80941Smrg   free(spec_entries);
380b8e80941Smrg}
381