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