1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2017 - Hans-Kristian Arntzen
3  *
4  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
5  *  of the GNU General Public License as published by the Free Software Found-
6  *  ation, either version 3 of the License, or (at your option) any later version.
7  *
8  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10  *  PURPOSE.  See the GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License along with RetroArch.
13  *  If not, see <http://www.gnu.org/licenses/>.
14  */
15 
16 #include "spirv_cross.hpp"
17 #include "slang_reflection.h"
18 #include "slang_reflection.hpp"
19 #include <vector>
20 #include <algorithm>
21 #include <stdio.h>
22 #include <compat/strl.h>
23 #include "glslang_util.h"
24 #include "../../verbosity.h"
25 
26 using namespace std;
27 using namespace spirv_cross;
28 
29 static const char *texture_semantic_names[] = {
30    "Original",
31    "Source",
32    "OriginalHistory",
33    "PassOutput",
34    "PassFeedback",
35    "User",
36    nullptr
37 };
38 
39 static const char *texture_semantic_uniform_names[] = {
40    "OriginalSize",
41    "SourceSize",
42    "OriginalHistorySize",
43    "PassOutputSize",
44    "PassFeedbackSize",
45    "UserSize",
46    nullptr
47 };
48 
49 static const char *semantic_uniform_names[] = {
50    "MVP",
51    "OutputSize",
52    "FinalViewportSize",
53    "FrameCount",
54    "FrameDirection",
55 };
56 
slang_name_to_texture_semantic(const std::unordered_map<std::string,slang_texture_semantic_map> & semantic_map,const std::string & name,unsigned * index)57 static slang_texture_semantic slang_name_to_texture_semantic(
58       const std::unordered_map<std::string, slang_texture_semantic_map> &semantic_map,
59       const std::string &name, unsigned *index)
60 {
61    auto itr = semantic_map.find(name);
62    if (itr != end(semantic_map))
63    {
64       *index = itr->second.index;
65       return itr->second.semantic;
66    }
67 
68    return slang_name_to_texture_semantic_array(
69          name.c_str(), texture_semantic_names, index);
70 }
71 
slang_uniform_name_to_texture_semantic(const std::unordered_map<std::string,slang_texture_semantic_map> & semantic_map,const std::string & name,unsigned * index)72 static slang_texture_semantic slang_uniform_name_to_texture_semantic(
73       const std::unordered_map<std::string, slang_texture_semantic_map> &semantic_map,
74       const std::string &name, unsigned *index)
75 {
76    auto itr = semantic_map.find(name);
77    if (itr != end(semantic_map))
78    {
79       *index = itr->second.index;
80       return itr->second.semantic;
81    }
82 
83    return slang_name_to_texture_semantic_array(name.c_str(),
84          texture_semantic_uniform_names, index);
85 }
86 
slang_uniform_name_to_semantic(const std::unordered_map<std::string,slang_semantic_map> & semantic_map,const std::string & name,unsigned * index)87 static slang_semantic slang_uniform_name_to_semantic(
88       const std::unordered_map<std::string, slang_semantic_map> &semantic_map,
89       const std::string &name, unsigned *index)
90 {
91    unsigned i = 0;
92    auto itr   = semantic_map.find(name);
93 
94    if (itr != end(semantic_map))
95    {
96       *index = itr->second.index;
97       return itr->second.semantic;
98    }
99 
100    /* No builtin semantics are arrayed. */
101    *index = 0;
102    for (auto n : semantic_uniform_names)
103    {
104       if (name == n)
105          return static_cast<slang_semantic>(i);
106       i++;
107    }
108 
109    return SLANG_INVALID_SEMANTIC;
110 }
111 
112 template <typename T>
resize_minimum(T & vec,unsigned minimum)113 static void resize_minimum(T &vec, unsigned minimum)
114 {
115    if (vec.size() < minimum)
116       vec.resize(minimum);
117 }
118 
set_ubo_texture_offset(slang_reflection * reflection,slang_texture_semantic semantic,unsigned index,size_t offset,bool push_constant)119 static bool set_ubo_texture_offset(
120       slang_reflection *reflection,
121       slang_texture_semantic semantic,
122       unsigned index,
123       size_t offset, bool push_constant)
124 {
125    resize_minimum(reflection->semantic_textures[semantic], index + 1);
126    slang_texture_semantic_meta &sem = reflection->semantic_textures[semantic][index];
127    bool &active                     = push_constant ? sem.push_constant : sem.uniform;
128    size_t &active_offset            = push_constant ? sem.push_constant_offset : sem.ubo_offset;
129 
130    if (active)
131    {
132       if (active_offset != offset)
133       {
134          RARCH_ERR("[slang]: Vertex and fragment have"
135                " different offsets for same semantic %s #%u (%u vs. %u).\n",
136                texture_semantic_uniform_names[semantic],
137                index,
138                unsigned(active_offset),
139                unsigned(offset));
140          return false;
141       }
142    }
143 
144    active        = true;
145    active_offset = offset;
146    return true;
147 }
148 
set_ubo_float_parameter_offset(slang_reflection * reflection,unsigned index,size_t offset,unsigned num_components,bool push_constant)149 static bool set_ubo_float_parameter_offset(
150       slang_reflection *reflection,
151       unsigned index, size_t offset,
152       unsigned num_components,
153       bool push_constant)
154 {
155    resize_minimum(reflection->semantic_float_parameters, index + 1);
156    slang_semantic_meta &sem = reflection->semantic_float_parameters[index];
157    bool   &active           = push_constant ? sem.push_constant : sem.uniform;
158    size_t &active_offset    = push_constant ? sem.push_constant_offset : sem.ubo_offset;
159 
160    if (active)
161    {
162       if (active_offset != offset)
163       {
164          RARCH_ERR("[slang]: Vertex and fragment have different"
165                " offsets for same parameter #%u (%u vs. %u).\n",
166                index,
167                unsigned(active_offset),
168                unsigned(offset));
169          return false;
170       }
171    }
172 
173    if (  (sem.num_components != num_components) &&
174          (sem.uniform || sem.push_constant))
175    {
176       RARCH_ERR("[slang]: Vertex and fragment have different "
177             "components for same parameter #%u (%u vs. %u).\n",
178             index,
179             unsigned(sem.num_components),
180             unsigned(num_components));
181       return false;
182    }
183 
184    active             = true;
185    active_offset      = offset;
186    sem.num_components = num_components;
187    return true;
188 }
189 
set_ubo_offset(slang_reflection * reflection,slang_semantic semantic,size_t offset,unsigned num_components,bool push_constant)190 static bool set_ubo_offset(
191       slang_reflection *reflection,
192       slang_semantic semantic,
193       size_t offset, unsigned num_components, bool push_constant)
194 {
195    slang_semantic_meta &sem = reflection->semantics[semantic];
196    bool &active             = push_constant ? sem.push_constant : sem.uniform;
197    size_t &active_offset    = push_constant ? sem.push_constant_offset : sem.ubo_offset;
198 
199    if (active)
200    {
201       if (active_offset != offset)
202       {
203          RARCH_ERR("[slang]: Vertex and fragment have "
204                "different offsets for same semantic %s (%u vs. %u).\n",
205                semantic_uniform_names[semantic],
206                unsigned(active_offset),
207                unsigned(offset));
208          return false;
209       }
210 
211    }
212 
213    if (  (sem.num_components != num_components) &&
214          (sem.uniform || sem.push_constant))
215    {
216       RARCH_ERR("[slang]: Vertex and fragment have different"
217             " components for same semantic %s (%u vs. %u).\n",
218             semantic_uniform_names[semantic],
219             unsigned(sem.num_components),
220             unsigned(num_components));
221       return false;
222    }
223 
224    active             = true;
225    active_offset      = offset;
226    sem.num_components = num_components;
227    return true;
228 }
229 
validate_type_for_semantic(const SPIRType & type,slang_semantic sem)230 static bool validate_type_for_semantic(const SPIRType &type, slang_semantic sem)
231 {
232    if (!type.array.empty())
233       return false;
234    if (type.basetype != SPIRType::Float && type.basetype != SPIRType::Int && type.basetype != SPIRType::UInt)
235       return false;
236 
237    switch (sem)
238    {
239          /* mat4 */
240       case SLANG_SEMANTIC_MVP:
241          return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 4;
242          /* uint */
243       case SLANG_SEMANTIC_FRAME_COUNT:
244          return type.basetype == SPIRType::UInt  && type.vecsize == 1 && type.columns == 1;
245          /* int */
246       case SLANG_SEMANTIC_FRAME_DIRECTION:
247          return type.basetype == SPIRType::Int   && type.vecsize == 1 && type.columns == 1;
248          /* float */
249       case SLANG_SEMANTIC_FLOAT_PARAMETER:
250          return type.basetype == SPIRType::Float && type.vecsize == 1 && type.columns == 1;
251          /* vec4 */
252       default:
253          return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 1;
254    }
255 }
256 
validate_type_for_texture_semantic(const SPIRType & type)257 static bool validate_type_for_texture_semantic(const SPIRType &type)
258 {
259    if (!type.array.empty())
260       return false;
261    return (type.basetype == SPIRType::Float) &&
262           (type.vecsize  == 4)               &&
263           (type.columns  == 1);
264 }
265 
add_active_buffer_ranges(const Compiler & compiler,const Resource & resource,slang_reflection * reflection,bool push_constant)266 static bool add_active_buffer_ranges(
267       const Compiler &compiler,
268       const Resource &resource,
269       slang_reflection *reflection,
270       bool push_constant)
271 {
272    unsigned i;
273    /* Get which uniforms are actually in use by this shader. */
274    auto ranges = compiler.get_active_buffer_ranges(resource.id);
275 
276    for (i = 0; i < ranges.size(); i++)
277    {
278       unsigned sem_index             = 0;
279       unsigned tex_sem_index         = 0;
280       const std::string &name        = compiler.get_member_name(
281             resource.base_type_id, ranges[i].index);
282       const SPIRType &type           = compiler.get_type(
283             compiler.get_type(resource.base_type_id).member_types[
284             ranges[i].index]);
285       slang_semantic sem             = slang_uniform_name_to_semantic(
286             *reflection->semantic_map, name, &sem_index);
287       slang_texture_semantic tex_sem = slang_uniform_name_to_texture_semantic(
288             *reflection->texture_semantic_uniform_map,
289             name, &tex_sem_index);
290 
291       if (tex_sem == SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT && tex_sem_index >= reflection->pass_number)
292       {
293          RARCH_ERR("[slang]: Non causal filter chain detected. "
294                "Shader is trying to use output from pass #%u,"
295                " but this shader is pass #%u.\n",
296                tex_sem_index, reflection->pass_number);
297          return false;
298       }
299 
300       if (sem != SLANG_INVALID_SEMANTIC)
301       {
302          if (!validate_type_for_semantic(type, sem))
303          {
304             RARCH_ERR("[slang]: Underlying type of semantic is invalid.\n");
305             return false;
306          }
307 
308          switch (sem)
309          {
310             case SLANG_SEMANTIC_FLOAT_PARAMETER:
311                if (!set_ubo_float_parameter_offset(reflection, sem_index,
312                         ranges[i].offset, type.vecsize, push_constant))
313                   return false;
314                break;
315 
316             default:
317                if (!set_ubo_offset(reflection, sem,
318                         ranges[i].offset,
319                         type.vecsize * type.columns, push_constant))
320                   return false;
321                break;
322          }
323       }
324       else if (tex_sem != SLANG_INVALID_TEXTURE_SEMANTIC)
325       {
326          if (!validate_type_for_texture_semantic(type))
327          {
328             RARCH_ERR("[slang]: Underlying type of texture"
329                   " semantic is invalid.\n");
330             return false;
331          }
332 
333          if (!set_ubo_texture_offset(reflection, tex_sem, tex_sem_index,
334                   ranges[i].offset, push_constant))
335             return false;
336       }
337       else
338       {
339          RARCH_ERR("[slang]: Unknown semantic found.\n");
340          return false;
341       }
342    }
343    return true;
344 }
345 
346 
slang_reflection()347 slang_reflection::slang_reflection()
348 {
349    unsigned i;
350 
351    for (i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
352       semantic_textures[i].resize(
353             slang_texture_semantic_is_array(
354                static_cast<slang_texture_semantic>(i))
355             ? 0 : 1);
356 }
357 
slang_reflect(const Compiler & vertex_compiler,const Compiler & fragment_compiler,const ShaderResources & vertex,const ShaderResources & fragment,slang_reflection * reflection)358 bool slang_reflect(
359       const Compiler &vertex_compiler,
360       const Compiler &fragment_compiler,
361       const ShaderResources &vertex,
362       const ShaderResources &fragment,
363       slang_reflection *reflection)
364 {
365    uint32_t location_mask = 0;
366    uint32_t binding_mask  = 0;
367    unsigned i             = 0;
368 
369    /* Validate use of unexpected types. */
370    if (
371          !vertex.sampled_images.empty()    ||
372          !vertex.storage_buffers.empty()   ||
373          !vertex.subpass_inputs.empty()    ||
374          !vertex.storage_images.empty()    ||
375          !vertex.atomic_counters.empty()   ||
376          !fragment.storage_buffers.empty() ||
377          !fragment.subpass_inputs.empty()  ||
378          !fragment.storage_images.empty()  ||
379          !fragment.atomic_counters.empty())
380    {
381       RARCH_ERR("[slang]: Invalid resource type detected.\n");
382       return false;
383    }
384 
385    /* Validate vertex input. */
386    if (vertex.stage_inputs.size() != 2)
387    {
388       RARCH_ERR("[slang]: Vertex must have two attributes.\n");
389       return false;
390    }
391 
392    if (fragment.stage_outputs.size() != 1)
393    {
394       RARCH_ERR("[slang]: Multiple render targets not supported.\n");
395       return false;
396    }
397 
398    if (fragment_compiler.get_decoration(
399             fragment.stage_outputs[0].id, spv::DecorationLocation) != 0)
400    {
401       RARCH_ERR("[slang]: Render target must use location = 0.\n");
402       return false;
403    }
404 
405    for (i = 0; i < vertex.stage_inputs.size(); i++)
406       location_mask |= 1 << vertex_compiler.get_decoration(
407             vertex.stage_inputs[i].id, spv::DecorationLocation);
408 
409    if (location_mask != 0x3)
410    {
411       RARCH_ERR("[slang]: The two vertex attributes do not"
412             " use location = 0 and location = 1.\n");
413       return false;
414    }
415 
416    /* Validate the single uniform buffer. */
417    if (vertex.uniform_buffers.size() > 1)
418    {
419       RARCH_ERR("[slang]: Vertex must use zero or one uniform buffer.\n");
420       return false;
421    }
422 
423    if (fragment.uniform_buffers.size() > 1)
424    {
425       RARCH_ERR("[slang]: Fragment must use zero or one uniform buffer.\n");
426       return false;
427    }
428 
429    /* Validate the single push constant buffer. */
430    if (vertex.push_constant_buffers.size() > 1)
431    {
432       RARCH_ERR("[slang]: Vertex must use zero or one push constant buffers.\n");
433       return false;
434    }
435 
436    if (fragment.push_constant_buffers.size() > 1)
437    {
438       RARCH_ERR("[slang]: Fragment must use zero or one push cosntant buffer.\n");
439       return false;
440    }
441 
442    uint32_t vertex_ubo    = vertex.uniform_buffers.empty() ? 0 : vertex.uniform_buffers[0].id;
443    uint32_t fragment_ubo  = fragment.uniform_buffers.empty() ? 0 : fragment.uniform_buffers[0].id;
444    uint32_t vertex_push   = vertex.push_constant_buffers.empty() ? 0 : vertex.push_constant_buffers[0].id;
445    uint32_t fragment_push = fragment.push_constant_buffers.empty() ? 0 : fragment.push_constant_buffers[0].id;
446 
447    if (vertex_ubo &&
448          vertex_compiler.get_decoration(
449             vertex_ubo, spv::DecorationDescriptorSet) != 0)
450    {
451       RARCH_ERR("[slang]: Resources must use descriptor set #0.\n");
452       return false;
453    }
454 
455    if (fragment_ubo &&
456          fragment_compiler.get_decoration(
457             fragment_ubo, spv::DecorationDescriptorSet) != 0)
458    {
459       RARCH_ERR("[slang]: Resources must use descriptor set #0.\n");
460       return false;
461    }
462 
463    unsigned vertex_ubo_binding   = vertex_ubo
464       ? vertex_compiler.get_decoration(vertex_ubo, spv::DecorationBinding)
465       : -1u;
466    unsigned fragment_ubo_binding = fragment_ubo
467       ? fragment_compiler.get_decoration(fragment_ubo, spv::DecorationBinding)
468       : -1u;
469    bool has_ubo                  = vertex_ubo || fragment_ubo;
470 
471    if (  (vertex_ubo_binding   != -1u) &&
472          (fragment_ubo_binding != -1u) &&
473          (vertex_ubo_binding   != fragment_ubo_binding))
474    {
475       RARCH_ERR("[slang]: Vertex and fragment uniform buffer must have same binding.\n");
476       return false;
477    }
478 
479    unsigned ubo_binding = (vertex_ubo_binding != -1u)
480       ? vertex_ubo_binding
481       : fragment_ubo_binding;
482 
483    if (has_ubo && ubo_binding >= SLANG_NUM_BINDINGS)
484    {
485       RARCH_ERR("[slang]: Binding %u is out of range.\n", ubo_binding);
486       return false;
487    }
488 
489    reflection->ubo_binding              = has_ubo ? ubo_binding : 0;
490    reflection->ubo_stage_mask           = 0;
491    reflection->ubo_size                 = 0;
492    reflection->push_constant_size       = 0;
493    reflection->push_constant_stage_mask = 0;
494 
495    if (vertex_ubo)
496    {
497       reflection->ubo_stage_mask |= SLANG_STAGE_VERTEX_MASK;
498       reflection->ubo_size        = max(reflection->ubo_size,
499             vertex_compiler.get_declared_struct_size(
500                vertex_compiler.get_type(
501                   vertex.uniform_buffers[0].base_type_id)));
502    }
503 
504    if (fragment_ubo)
505    {
506       reflection->ubo_stage_mask |= SLANG_STAGE_FRAGMENT_MASK;
507       reflection->ubo_size        = max(reflection->ubo_size,
508             fragment_compiler.get_declared_struct_size(
509                fragment_compiler.get_type(
510                   fragment.uniform_buffers[0].base_type_id)));
511    }
512 
513    if (vertex_push)
514    {
515       reflection->push_constant_stage_mask |= SLANG_STAGE_VERTEX_MASK;
516       reflection->push_constant_size        = max(
517             reflection->push_constant_size,
518             vertex_compiler.get_declared_struct_size(
519                vertex_compiler.get_type(
520                   vertex.push_constant_buffers[0].base_type_id)));
521    }
522 
523    if (fragment_push)
524    {
525       reflection->push_constant_stage_mask |= SLANG_STAGE_FRAGMENT_MASK;
526       reflection->push_constant_size        = max(
527             reflection->push_constant_size,
528             fragment_compiler.get_declared_struct_size(
529                fragment_compiler.get_type(
530                   fragment.push_constant_buffers[0].base_type_id)));
531    }
532 
533    /* Validate push constant size against Vulkan's
534     * minimum spec to avoid cross-vendor issues. */
535    if (reflection->push_constant_size > 128)
536    {
537       RARCH_ERR("[slang]: Exceeded maximum size of 128 bytes"
538             " for push constant buffer.\n");
539       return false;
540    }
541 
542    /* Find all relevant uniforms and push constants. */
543    if (vertex_ubo && !add_active_buffer_ranges(vertex_compiler,
544             vertex.uniform_buffers[0], reflection, false))
545       return false;
546    if (fragment_ubo && !add_active_buffer_ranges(fragment_compiler,
547             fragment.uniform_buffers[0], reflection, false))
548       return false;
549    if (vertex_push && !add_active_buffer_ranges(vertex_compiler,
550             vertex.push_constant_buffers[0], reflection, true))
551       return false;
552    if (fragment_push && !add_active_buffer_ranges(fragment_compiler,
553             fragment.push_constant_buffers[0], reflection, true))
554       return false;
555 
556    if (has_ubo)
557       binding_mask = 1 << ubo_binding;
558 
559    /* On to textures. */
560    for (i = 0; i < fragment.sampled_images.size(); i++)
561    {
562       unsigned array_index = 0;
563       unsigned set         = fragment_compiler.get_decoration(
564             fragment.sampled_images[i].id,
565             spv::DecorationDescriptorSet);
566       unsigned binding     = fragment_compiler.get_decoration(
567             fragment.sampled_images[i].id,
568             spv::DecorationBinding);
569 
570       if (set != 0)
571       {
572          RARCH_ERR("[slang]: Resources must use descriptor set #0.\n");
573          return false;
574       }
575 
576       if (binding >= SLANG_NUM_BINDINGS)
577       {
578          RARCH_ERR("[slang]: Binding %u is out of range.\n", ubo_binding);
579          return false;
580       }
581 
582       if (binding_mask & (1 << binding))
583       {
584          RARCH_ERR("[slang]: Binding %u is already in use.\n", binding);
585          return false;
586       }
587       binding_mask |= 1 << binding;
588 
589       slang_texture_semantic index = slang_name_to_texture_semantic(
590             *reflection->texture_semantic_map,
591             fragment.sampled_images[i].name, &array_index);
592 
593       if (index == SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT && array_index >= reflection->pass_number)
594       {
595          RARCH_ERR("[slang]: Non causal filter chain detected. "
596                "Shader is trying to use output from pass #%u,"
597                " but this shader is pass #%u.\n",
598                array_index, reflection->pass_number);
599          return false;
600       }
601       else if (index == SLANG_INVALID_TEXTURE_SEMANTIC)
602       {
603          RARCH_ERR("[slang]: Texture name '%s' not found in semantic map, "
604                    "Probably the texture name or pass alias is not defined "
605                    "in the preset (Non-semantic textures not supported yet)\n",
606                    fragment.sampled_images[i].name.c_str());
607          return false;
608       }
609 
610       resize_minimum(reflection->semantic_textures[index], array_index + 1);
611       slang_texture_semantic_meta &semantic =
612          reflection->semantic_textures[index][array_index];
613       semantic.binding                      = binding;
614       semantic.stage_mask                   = SLANG_STAGE_FRAGMENT_MASK;
615       semantic.texture                      = true;
616    }
617 
618 #ifdef DEBUG
619    RARCH_LOG("[slang]: Reflection\n");
620    RARCH_LOG("[slang]:   Textures:\n");
621 
622    for (i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
623    {
624       unsigned index = 0;
625       for (auto &sem : reflection->semantic_textures[i])
626       {
627          if (sem.texture)
628             RARCH_LOG("[slang]:      %s (#%u)\n",
629                   texture_semantic_names[i], index);
630          index++;
631       }
632    }
633 
634    RARCH_LOG("[slang]:\n");
635    RARCH_LOG("[slang]:   Uniforms (Vertex: %s, Fragment: %s):\n",
636          reflection->ubo_stage_mask & SLANG_STAGE_VERTEX_MASK ? "yes": "no",
637          reflection->ubo_stage_mask & SLANG_STAGE_FRAGMENT_MASK ? "yes": "no");
638    RARCH_LOG("[slang]:   Push Constants (Vertex: %s, Fragment: %s):\n",
639          reflection->push_constant_stage_mask & SLANG_STAGE_VERTEX_MASK ? "yes": "no",
640          reflection->push_constant_stage_mask & SLANG_STAGE_FRAGMENT_MASK ? "yes": "no");
641 
642    for (i = 0; i < SLANG_NUM_SEMANTICS; i++)
643    {
644       if (reflection->semantics[i].uniform)
645       {
646          RARCH_LOG("[slang]:      %s (Offset: %u)\n",
647                semantic_uniform_names[i],
648                unsigned(reflection->semantics[i].ubo_offset));
649       }
650 
651       if (reflection->semantics[i].push_constant)
652       {
653          RARCH_LOG("[slang]:      %s (PushOffset: %u)\n",
654                semantic_uniform_names[i],
655                unsigned(reflection->semantics[i].push_constant_offset));
656       }
657    }
658 
659    for (i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
660    {
661       unsigned index = 0;
662       for (auto &sem : reflection->semantic_textures[i])
663       {
664          if (sem.uniform)
665          {
666             RARCH_LOG("[slang]:      %s (#%u) (Offset: %u)\n",
667                   texture_semantic_uniform_names[i],
668                   index,
669                   unsigned(sem.ubo_offset));
670          }
671 
672          if (sem.push_constant)
673          {
674             RARCH_LOG("[slang]:      %s (#%u) (PushOffset: %u)\n",
675                   texture_semantic_uniform_names[i],
676                   index,
677                   unsigned(sem.push_constant_offset));
678          }
679          index++;
680       }
681    }
682 
683    {
684       char buf[64];
685       buf[0] = '\0';
686       snprintf(buf, sizeof(buf),
687             "[slang]:\n%s [slang]:   Parameters:\n", FILE_PATH_LOG_INFO);
688       RARCH_LOG(buf);
689    }
690 
691    for (i = 0; i < reflection->semantic_float_parameters.size(); i++)
692    {
693       slang_semantic_meta *param = (slang_semantic_meta*)
694          &reflection->semantic_float_parameters[i];
695 
696       if (!param)
697          continue;
698 
699       if (param->uniform)
700          RARCH_LOG("[slang]:     #%u (Offset: %u)\n", i,
701                (unsigned int)param->ubo_offset);
702       if (param->push_constant)
703          RARCH_LOG("[slang]:     #%u (PushOffset: %u)\n", i,
704                (unsigned int)param->push_constant_offset);
705    }
706 #endif
707 
708    return true;
709 }
710 
slang_reflect_spirv(const std::vector<uint32_t> & vertex,const std::vector<uint32_t> & fragment,slang_reflection * reflection)711 bool slang_reflect_spirv(const std::vector<uint32_t> &vertex,
712       const std::vector<uint32_t> &fragment,
713       slang_reflection *reflection)
714 {
715    try
716    {
717       Compiler vertex_compiler(vertex);
718       Compiler fragment_compiler(fragment);
719       spirv_cross::ShaderResources
720          vertex_resources     = vertex_compiler.get_shader_resources();
721       spirv_cross::ShaderResources
722          fragment_resources   = fragment_compiler.get_shader_resources();
723 
724       if (!slang_reflect(vertex_compiler, fragment_compiler,
725                vertex_resources, fragment_resources,
726                reflection))
727       {
728          RARCH_ERR("[slang]: Failed to reflect SPIR-V."
729                " Resource usage is inconsistent with expectations.\n");
730          return false;
731       }
732 
733       return true;
734    }
735    catch (const std::exception &e)
736    {
737       RARCH_ERR("[slang]: SPIRV-Cross threw exception: %s.\n", e.what());
738       return false;
739    }
740 }
741