1 /*
2 * Copyright 2015-2019 Arm Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "spirv_cpp.hpp"
18 #include "spirv_cross_util.hpp"
19 #include "spirv_glsl.hpp"
20 #include "spirv_hlsl.hpp"
21 #include "spirv_msl.hpp"
22 #include "spirv_parser.hpp"
23 #include "spirv_reflect.hpp"
24 #include <algorithm>
25 #include <cstdio>
26 #include <cstring>
27 #include <functional>
28 #include <limits>
29 #include <memory>
30 #include <stdexcept>
31 #include <unordered_map>
32 #include <unordered_set>
33
34 #ifdef HAVE_SPIRV_CROSS_GIT_VERSION
35 #include "gitversion.h"
36 #endif
37
38 #ifdef _MSC_VER
39 #pragma warning(disable : 4996)
40 #endif
41
42 using namespace spv;
43 using namespace SPIRV_CROSS_NAMESPACE;
44 using namespace std;
45
46 #ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
THROW(const char * str)47 static inline void THROW(const char *str)
48 {
49 fprintf(stderr, "SPIRV-Cross will abort: %s\n", str);
50 fflush(stderr);
51 abort();
52 }
53 #else
54 #define THROW(x) throw runtime_error(x)
55 #endif
56
57 struct CLIParser;
58 struct CLICallbacks
59 {
addCLICallbacks60 void add(const char *cli, const function<void(CLIParser &)> &func)
61 {
62 callbacks[cli] = func;
63 }
64 unordered_map<string, function<void(CLIParser &)>> callbacks;
65 function<void()> error_handler;
66 function<void(const char *)> default_handler;
67 };
68
69 struct CLIParser
70 {
CLIParserCLIParser71 CLIParser(CLICallbacks cbs_, int argc_, char *argv_[])
72 : cbs(move(cbs_))
73 , argc(argc_)
74 , argv(argv_)
75 {
76 }
77
parseCLIParser78 bool parse()
79 {
80 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
81 try
82 #endif
83 {
84 while (argc && !ended_state)
85 {
86 const char *next = *argv++;
87 argc--;
88
89 if (*next != '-' && cbs.default_handler)
90 {
91 cbs.default_handler(next);
92 }
93 else
94 {
95 auto itr = cbs.callbacks.find(next);
96 if (itr == ::end(cbs.callbacks))
97 {
98 THROW("Invalid argument");
99 }
100
101 itr->second(*this);
102 }
103 }
104
105 return true;
106 }
107 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
108 catch (...)
109 {
110 if (cbs.error_handler)
111 {
112 cbs.error_handler();
113 }
114 return false;
115 }
116 #endif
117 }
118
endCLIParser119 void end()
120 {
121 ended_state = true;
122 }
123
next_uintCLIParser124 uint32_t next_uint()
125 {
126 if (!argc)
127 {
128 THROW("Tried to parse uint, but nothing left in arguments");
129 }
130
131 uint64_t val = stoul(*argv);
132 if (val > numeric_limits<uint32_t>::max())
133 {
134 THROW("next_uint() out of range");
135 }
136
137 argc--;
138 argv++;
139
140 return uint32_t(val);
141 }
142
next_doubleCLIParser143 double next_double()
144 {
145 if (!argc)
146 {
147 THROW("Tried to parse double, but nothing left in arguments");
148 }
149
150 double val = stod(*argv);
151
152 argc--;
153 argv++;
154
155 return val;
156 }
157
158 // Return a string only if it's not prefixed with `--`, otherwise return the default value
next_value_stringCLIParser159 const char *next_value_string(const char *default_value)
160 {
161 if (!argc)
162 {
163 return default_value;
164 }
165
166 if (0 == strncmp("--", *argv, 2))
167 {
168 return default_value;
169 }
170
171 return next_string();
172 }
173
next_stringCLIParser174 const char *next_string()
175 {
176 if (!argc)
177 {
178 THROW("Tried to parse string, but nothing left in arguments");
179 }
180
181 const char *ret = *argv;
182 argc--;
183 argv++;
184 return ret;
185 }
186
187 CLICallbacks cbs;
188 int argc;
189 char **argv;
190 bool ended_state = false;
191 };
192
read_spirv_file(const char * path)193 static vector<uint32_t> read_spirv_file(const char *path)
194 {
195 FILE *file = fopen(path, "rb");
196 if (!file)
197 {
198 fprintf(stderr, "Failed to open SPIR-V file: %s\n", path);
199 return {};
200 }
201
202 fseek(file, 0, SEEK_END);
203 long len = ftell(file) / sizeof(uint32_t);
204 rewind(file);
205
206 vector<uint32_t> spirv(len);
207 if (fread(spirv.data(), sizeof(uint32_t), len, file) != size_t(len))
208 spirv.clear();
209
210 fclose(file);
211 return spirv;
212 }
213
write_string_to_file(const char * path,const char * string)214 static bool write_string_to_file(const char *path, const char *string)
215 {
216 FILE *file = fopen(path, "w");
217 if (!file)
218 {
219 fprintf(stderr, "Failed to write file: %s\n", path);
220 return false;
221 }
222
223 fprintf(file, "%s", string);
224 fclose(file);
225 return true;
226 }
227
print_resources(const Compiler & compiler,const char * tag,const SmallVector<Resource> & resources)228 static void print_resources(const Compiler &compiler, const char *tag, const SmallVector<Resource> &resources)
229 {
230 fprintf(stderr, "%s\n", tag);
231 fprintf(stderr, "=============\n\n");
232 bool print_ssbo = !strcmp(tag, "ssbos");
233
234 for (auto &res : resources)
235 {
236 auto &type = compiler.get_type(res.type_id);
237
238 if (print_ssbo && compiler.buffer_is_hlsl_counter_buffer(res.id))
239 continue;
240
241 // If we don't have a name, use the fallback for the type instead of the variable
242 // for SSBOs and UBOs since those are the only meaningful names to use externally.
243 // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
244 bool is_push_constant = compiler.get_storage_class(res.id) == StorageClassPushConstant;
245 bool is_block = compiler.get_decoration_bitset(type.self).get(DecorationBlock) ||
246 compiler.get_decoration_bitset(type.self).get(DecorationBufferBlock);
247 bool is_sized_block = is_block && (compiler.get_storage_class(res.id) == StorageClassUniform ||
248 compiler.get_storage_class(res.id) == StorageClassUniformConstant);
249 uint32_t fallback_id = !is_push_constant && is_block ? res.base_type_id : res.id;
250
251 uint32_t block_size = 0;
252 uint32_t runtime_array_stride = 0;
253 if (is_sized_block)
254 {
255 auto &base_type = compiler.get_type(res.base_type_id);
256 block_size = uint32_t(compiler.get_declared_struct_size(base_type));
257 runtime_array_stride = uint32_t(compiler.get_declared_struct_size_runtime_array(base_type, 1) -
258 compiler.get_declared_struct_size_runtime_array(base_type, 0));
259 }
260
261 Bitset mask;
262 if (print_ssbo)
263 mask = compiler.get_buffer_block_flags(res.id);
264 else
265 mask = compiler.get_decoration_bitset(res.id);
266
267 string array;
268 for (auto arr : type.array)
269 array = join("[", arr ? convert_to_string(arr) : "", "]") + array;
270
271 fprintf(stderr, " ID %03u : %s%s", res.id,
272 !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str(), array.c_str());
273
274 if (mask.get(DecorationLocation))
275 fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation));
276 if (mask.get(DecorationDescriptorSet))
277 fprintf(stderr, " (Set : %u)", compiler.get_decoration(res.id, DecorationDescriptorSet));
278 if (mask.get(DecorationBinding))
279 fprintf(stderr, " (Binding : %u)", compiler.get_decoration(res.id, DecorationBinding));
280 if (mask.get(DecorationInputAttachmentIndex))
281 fprintf(stderr, " (Attachment : %u)", compiler.get_decoration(res.id, DecorationInputAttachmentIndex));
282 if (mask.get(DecorationNonReadable))
283 fprintf(stderr, " writeonly");
284 if (mask.get(DecorationNonWritable))
285 fprintf(stderr, " readonly");
286 if (is_sized_block)
287 {
288 fprintf(stderr, " (BlockSize : %u bytes)", block_size);
289 if (runtime_array_stride)
290 fprintf(stderr, " (Unsized array stride: %u bytes)", runtime_array_stride);
291 }
292
293 uint32_t counter_id = 0;
294 if (print_ssbo && compiler.buffer_get_hlsl_counter_buffer(res.id, counter_id))
295 fprintf(stderr, " (HLSL counter buffer ID: %u)", counter_id);
296 fprintf(stderr, "\n");
297 }
298 fprintf(stderr, "=============\n\n");
299 }
300
execution_model_to_str(spv::ExecutionModel model)301 static const char *execution_model_to_str(spv::ExecutionModel model)
302 {
303 switch (model)
304 {
305 case spv::ExecutionModelVertex:
306 return "vertex";
307 case spv::ExecutionModelTessellationControl:
308 return "tessellation control";
309 case ExecutionModelTessellationEvaluation:
310 return "tessellation evaluation";
311 case ExecutionModelGeometry:
312 return "geometry";
313 case ExecutionModelFragment:
314 return "fragment";
315 case ExecutionModelGLCompute:
316 return "compute";
317 case ExecutionModelRayGenerationNV:
318 return "raygenNV";
319 case ExecutionModelIntersectionNV:
320 return "intersectionNV";
321 case ExecutionModelCallableNV:
322 return "callableNV";
323 case ExecutionModelAnyHitNV:
324 return "anyhitNV";
325 case ExecutionModelClosestHitNV:
326 return "closesthitNV";
327 case ExecutionModelMissNV:
328 return "missNV";
329 default:
330 return "???";
331 }
332 }
333
print_resources(const Compiler & compiler,const ShaderResources & res)334 static void print_resources(const Compiler &compiler, const ShaderResources &res)
335 {
336 auto &modes = compiler.get_execution_mode_bitset();
337
338 fprintf(stderr, "Entry points:\n");
339 auto entry_points = compiler.get_entry_points_and_stages();
340 for (auto &e : entry_points)
341 fprintf(stderr, " %s (%s)\n", e.name.c_str(), execution_model_to_str(e.execution_model));
342 fprintf(stderr, "\n");
343
344 fprintf(stderr, "Execution modes:\n");
345 modes.for_each_bit([&](uint32_t i) {
346 auto mode = static_cast<ExecutionMode>(i);
347 uint32_t arg0 = compiler.get_execution_mode_argument(mode, 0);
348 uint32_t arg1 = compiler.get_execution_mode_argument(mode, 1);
349 uint32_t arg2 = compiler.get_execution_mode_argument(mode, 2);
350
351 switch (static_cast<ExecutionMode>(i))
352 {
353 case ExecutionModeInvocations:
354 fprintf(stderr, " Invocations: %u\n", arg0);
355 break;
356
357 case ExecutionModeLocalSize:
358 fprintf(stderr, " LocalSize: (%u, %u, %u)\n", arg0, arg1, arg2);
359 break;
360
361 case ExecutionModeOutputVertices:
362 fprintf(stderr, " OutputVertices: %u\n", arg0);
363 break;
364
365 #define CHECK_MODE(m) \
366 case ExecutionMode##m: \
367 fprintf(stderr, " %s\n", #m); \
368 break
369 CHECK_MODE(SpacingEqual);
370 CHECK_MODE(SpacingFractionalEven);
371 CHECK_MODE(SpacingFractionalOdd);
372 CHECK_MODE(VertexOrderCw);
373 CHECK_MODE(VertexOrderCcw);
374 CHECK_MODE(PixelCenterInteger);
375 CHECK_MODE(OriginUpperLeft);
376 CHECK_MODE(OriginLowerLeft);
377 CHECK_MODE(EarlyFragmentTests);
378 CHECK_MODE(PointMode);
379 CHECK_MODE(Xfb);
380 CHECK_MODE(DepthReplacing);
381 CHECK_MODE(DepthGreater);
382 CHECK_MODE(DepthLess);
383 CHECK_MODE(DepthUnchanged);
384 CHECK_MODE(LocalSizeHint);
385 CHECK_MODE(InputPoints);
386 CHECK_MODE(InputLines);
387 CHECK_MODE(InputLinesAdjacency);
388 CHECK_MODE(Triangles);
389 CHECK_MODE(InputTrianglesAdjacency);
390 CHECK_MODE(Quads);
391 CHECK_MODE(Isolines);
392 CHECK_MODE(OutputPoints);
393 CHECK_MODE(OutputLineStrip);
394 CHECK_MODE(OutputTriangleStrip);
395 CHECK_MODE(VecTypeHint);
396 CHECK_MODE(ContractionOff);
397
398 default:
399 break;
400 }
401 });
402 fprintf(stderr, "\n");
403
404 print_resources(compiler, "subpass inputs", res.subpass_inputs);
405 print_resources(compiler, "inputs", res.stage_inputs);
406 print_resources(compiler, "outputs", res.stage_outputs);
407 print_resources(compiler, "textures", res.sampled_images);
408 print_resources(compiler, "separate images", res.separate_images);
409 print_resources(compiler, "separate samplers", res.separate_samplers);
410 print_resources(compiler, "images", res.storage_images);
411 print_resources(compiler, "ssbos", res.storage_buffers);
412 print_resources(compiler, "ubos", res.uniform_buffers);
413 print_resources(compiler, "push", res.push_constant_buffers);
414 print_resources(compiler, "counters", res.atomic_counters);
415 print_resources(compiler, "acceleration structures", res.acceleration_structures);
416 }
417
print_push_constant_resources(const Compiler & compiler,const SmallVector<Resource> & res)418 static void print_push_constant_resources(const Compiler &compiler, const SmallVector<Resource> &res)
419 {
420 for (auto &block : res)
421 {
422 auto ranges = compiler.get_active_buffer_ranges(block.id);
423 fprintf(stderr, "Active members in buffer: %s\n",
424 !block.name.empty() ? block.name.c_str() : compiler.get_fallback_name(block.id).c_str());
425
426 fprintf(stderr, "==================\n\n");
427 for (auto &range : ranges)
428 {
429 const auto &name = compiler.get_member_name(block.base_type_id, range.index);
430
431 fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index,
432 !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(),
433 unsigned(range.offset), unsigned(range.range));
434 }
435 fprintf(stderr, "==================\n\n");
436 }
437 }
438
print_spec_constants(const Compiler & compiler)439 static void print_spec_constants(const Compiler &compiler)
440 {
441 auto spec_constants = compiler.get_specialization_constants();
442 fprintf(stderr, "Specialization constants\n");
443 fprintf(stderr, "==================\n\n");
444 for (auto &c : spec_constants)
445 fprintf(stderr, "ID: %u, Spec ID: %u\n", c.id, c.constant_id);
446 fprintf(stderr, "==================\n\n");
447 }
448
print_capabilities_and_extensions(const Compiler & compiler)449 static void print_capabilities_and_extensions(const Compiler &compiler)
450 {
451 fprintf(stderr, "Capabilities\n");
452 fprintf(stderr, "============\n");
453 for (auto &capability : compiler.get_declared_capabilities())
454 fprintf(stderr, "Capability: %u\n", static_cast<unsigned>(capability));
455 fprintf(stderr, "============\n\n");
456
457 fprintf(stderr, "Extensions\n");
458 fprintf(stderr, "============\n");
459 for (auto &ext : compiler.get_declared_extensions())
460 fprintf(stderr, "Extension: %s\n", ext.c_str());
461 fprintf(stderr, "============\n\n");
462 }
463
464 struct PLSArg
465 {
466 PlsFormat format;
467 string name;
468 };
469
470 struct Remap
471 {
472 string src_name;
473 string dst_name;
474 unsigned components;
475 };
476
477 struct VariableTypeRemap
478 {
479 string variable_name;
480 string new_variable_type;
481 };
482
483 struct InterfaceVariableRename
484 {
485 StorageClass storageClass;
486 uint32_t location;
487 string variable_name;
488 };
489
490 struct CLIArguments
491 {
492 const char *input = nullptr;
493 const char *output = nullptr;
494 const char *cpp_interface_name = nullptr;
495 uint32_t version = 0;
496 uint32_t shader_model = 0;
497 uint32_t msl_version = 0;
498 bool es = false;
499 bool set_version = false;
500 bool set_shader_model = false;
501 bool set_msl_version = false;
502 bool set_es = false;
503 bool dump_resources = false;
504 bool force_temporary = false;
505 bool flatten_ubo = false;
506 bool fixup = false;
507 bool yflip = false;
508 bool sso = false;
509 bool support_nonzero_baseinstance = true;
510 bool msl_capture_output_to_buffer = false;
511 bool msl_swizzle_texture_samples = false;
512 bool msl_ios = false;
513 bool msl_pad_fragment_output = false;
514 bool msl_domain_lower_left = false;
515 bool msl_argument_buffers = false;
516 bool msl_texture_buffer_native = false;
517 bool msl_multiview = false;
518 bool glsl_emit_push_constant_as_ubo = false;
519 bool glsl_emit_ubo_as_plain_uniforms = false;
520 bool emit_line_directives = false;
521 SmallVector<uint32_t> msl_discrete_descriptor_sets;
522 SmallVector<PLSArg> pls_in;
523 SmallVector<PLSArg> pls_out;
524 SmallVector<Remap> remaps;
525 SmallVector<string> extensions;
526 SmallVector<VariableTypeRemap> variable_type_remaps;
527 SmallVector<InterfaceVariableRename> interface_variable_renames;
528 SmallVector<HLSLVertexAttributeRemap> hlsl_attr_remap;
529 string entry;
530 string entry_stage;
531
532 struct Rename
533 {
534 string old_name;
535 string new_name;
536 ExecutionModel execution_model;
537 };
538 SmallVector<Rename> entry_point_rename;
539
540 uint32_t iterations = 1;
541 bool cpp = false;
542 string reflect;
543 bool msl = false;
544 bool hlsl = false;
545 bool hlsl_compat = false;
546 bool hlsl_support_nonzero_base = false;
547 bool vulkan_semantics = false;
548 bool flatten_multidimensional_arrays = false;
549 bool use_420pack_extension = true;
550 bool remove_unused = false;
551 bool combined_samplers_inherit_bindings = false;
552 };
553
print_version()554 static void print_version()
555 {
556 #ifdef HAVE_SPIRV_CROSS_GIT_VERSION
557 fprintf(stderr, "%s\n", SPIRV_CROSS_GIT_REVISION);
558 #else
559 fprintf(stderr, "Git revision unknown. Build with CMake to create timestamp and revision info.\n");
560 #endif
561 }
562
print_help()563 static void print_help()
564 {
565 print_version();
566
567 fprintf(stderr, "Usage: spirv-cross\n"
568 "\t[--output <output path>]\n"
569 "\t[SPIR-V file]\n"
570 "\t[--es]\n"
571 "\t[--no-es]\n"
572 "\t[--version <GLSL version>]\n"
573 "\t[--dump-resources]\n"
574 "\t[--help]\n"
575 "\t[--revision]\n"
576 "\t[--force-temporary]\n"
577 "\t[--vulkan-semantics]\n"
578 "\t[--flatten-ubo]\n"
579 "\t[--fixup-clipspace]\n"
580 "\t[--flip-vert-y]\n"
581 "\t[--iterations iter]\n"
582 "\t[--cpp]\n"
583 "\t[--cpp-interface-name <name>]\n"
584 "\t[--glsl-emit-push-constant-as-ubo]\n"
585 "\t[--glsl-emit-ubo-as-plain-uniforms]\n"
586 "\t[--msl]\n"
587 "\t[--msl-version <MMmmpp>]\n"
588 "\t[--msl-capture-output]\n"
589 "\t[--msl-swizzle-texture-samples]\n"
590 "\t[--msl-ios]\n"
591 "\t[--msl-pad-fragment-output]\n"
592 "\t[--msl-domain-lower-left]\n"
593 "\t[--msl-argument-buffers]\n"
594 "\t[--msl-texture-buffer-native]\n"
595 "\t[--msl-discrete-descriptor-set <index>]\n"
596 "\t[--msl-multiview]\n"
597 "\t[--hlsl]\n"
598 "\t[--reflect]\n"
599 "\t[--shader-model]\n"
600 "\t[--hlsl-enable-compat]\n"
601 "\t[--hlsl-support-nonzero-basevertex-baseinstance]\n"
602 "\t[--separate-shader-objects]\n"
603 "\t[--pls-in format input-name]\n"
604 "\t[--pls-out format output-name]\n"
605 "\t[--remap source_name target_name components]\n"
606 "\t[--extension ext]\n"
607 "\t[--entry name]\n"
608 "\t[--stage <stage (vert, frag, geom, tesc, tese comp)>]\n"
609 "\t[--remove-unused-variables]\n"
610 "\t[--flatten-multidimensional-arrays]\n"
611 "\t[--no-420pack-extension]\n"
612 "\t[--remap-variable-type <variable_name> <new_variable_type>]\n"
613 "\t[--rename-interface-variable <in|out> <location> <new_variable_name>]\n"
614 "\t[--set-hlsl-vertex-input-semantic <location> <semantic>]\n"
615 "\t[--rename-entry-point <old> <new> <stage>]\n"
616 "\t[--combined-samplers-inherit-bindings]\n"
617 "\t[--no-support-nonzero-baseinstance]\n"
618 "\t[--emit-line-directives]\n"
619 "\n");
620 }
621
remap_generic(Compiler & compiler,const SmallVector<Resource> & resources,const Remap & remap)622 static bool remap_generic(Compiler &compiler, const SmallVector<Resource> &resources, const Remap &remap)
623 {
624 auto itr =
625 find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; });
626
627 if (itr != end(resources))
628 {
629 compiler.set_remapped_variable_state(itr->id, true);
630 compiler.set_name(itr->id, remap.dst_name);
631 compiler.set_subpass_input_remapped_components(itr->id, remap.components);
632 return true;
633 }
634 else
635 return false;
636 }
637
remap_pls(const SmallVector<PLSArg> & pls_variables,const SmallVector<Resource> & resources,const SmallVector<Resource> * secondary_resources)638 static vector<PlsRemap> remap_pls(const SmallVector<PLSArg> &pls_variables, const SmallVector<Resource> &resources,
639 const SmallVector<Resource> *secondary_resources)
640 {
641 vector<PlsRemap> ret;
642
643 for (auto &pls : pls_variables)
644 {
645 bool found = false;
646 for (auto &res : resources)
647 {
648 if (res.name == pls.name)
649 {
650 ret.push_back({ res.id, pls.format });
651 found = true;
652 break;
653 }
654 }
655
656 if (!found && secondary_resources)
657 {
658 for (auto &res : *secondary_resources)
659 {
660 if (res.name == pls.name)
661 {
662 ret.push_back({ res.id, pls.format });
663 found = true;
664 break;
665 }
666 }
667 }
668
669 if (!found)
670 fprintf(stderr, "Did not find stage input/output/target with name \"%s\".\n", pls.name.c_str());
671 }
672
673 return ret;
674 }
675
pls_format(const char * str)676 static PlsFormat pls_format(const char *str)
677 {
678 if (!strcmp(str, "r11f_g11f_b10f"))
679 return PlsR11FG11FB10F;
680 else if (!strcmp(str, "r32f"))
681 return PlsR32F;
682 else if (!strcmp(str, "rg16f"))
683 return PlsRG16F;
684 else if (!strcmp(str, "rg16"))
685 return PlsRG16;
686 else if (!strcmp(str, "rgb10_a2"))
687 return PlsRGB10A2;
688 else if (!strcmp(str, "rgba8"))
689 return PlsRGBA8;
690 else if (!strcmp(str, "rgba8i"))
691 return PlsRGBA8I;
692 else if (!strcmp(str, "rgba8ui"))
693 return PlsRGBA8UI;
694 else if (!strcmp(str, "rg16i"))
695 return PlsRG16I;
696 else if (!strcmp(str, "rgb10_a2ui"))
697 return PlsRGB10A2UI;
698 else if (!strcmp(str, "rg16ui"))
699 return PlsRG16UI;
700 else if (!strcmp(str, "r32ui"))
701 return PlsR32UI;
702 else
703 return PlsNone;
704 }
705
stage_to_execution_model(const std::string & stage)706 static ExecutionModel stage_to_execution_model(const std::string &stage)
707 {
708 if (stage == "vert")
709 return ExecutionModelVertex;
710 else if (stage == "frag")
711 return ExecutionModelFragment;
712 else if (stage == "comp")
713 return ExecutionModelGLCompute;
714 else if (stage == "tesc")
715 return ExecutionModelTessellationControl;
716 else if (stage == "tese")
717 return ExecutionModelTessellationEvaluation;
718 else if (stage == "geom")
719 return ExecutionModelGeometry;
720 else
721 SPIRV_CROSS_THROW("Invalid stage.");
722 }
723
compile_iteration(const CLIArguments & args,std::vector<uint32_t> spirv_file)724 static string compile_iteration(const CLIArguments &args, std::vector<uint32_t> spirv_file)
725 {
726 Parser spirv_parser(move(spirv_file));
727 spirv_parser.parse();
728
729 unique_ptr<CompilerGLSL> compiler;
730 bool combined_image_samplers = false;
731 bool build_dummy_sampler = false;
732
733 if (args.cpp)
734 {
735 compiler.reset(new CompilerCPP(move(spirv_parser.get_parsed_ir())));
736 if (args.cpp_interface_name)
737 static_cast<CompilerCPP *>(compiler.get())->set_interface_name(args.cpp_interface_name);
738 }
739 else if (args.msl)
740 {
741 compiler.reset(new CompilerMSL(move(spirv_parser.get_parsed_ir())));
742
743 auto *msl_comp = static_cast<CompilerMSL *>(compiler.get());
744 auto msl_opts = msl_comp->get_msl_options();
745 if (args.set_msl_version)
746 msl_opts.msl_version = args.msl_version;
747 msl_opts.capture_output_to_buffer = args.msl_capture_output_to_buffer;
748 msl_opts.swizzle_texture_samples = args.msl_swizzle_texture_samples;
749 if (args.msl_ios)
750 msl_opts.platform = CompilerMSL::Options::iOS;
751 msl_opts.pad_fragment_output_components = args.msl_pad_fragment_output;
752 msl_opts.tess_domain_origin_lower_left = args.msl_domain_lower_left;
753 msl_opts.argument_buffers = args.msl_argument_buffers;
754 msl_opts.texture_buffer_native = args.msl_texture_buffer_native;
755 msl_opts.multiview = args.msl_multiview;
756 msl_comp->set_msl_options(msl_opts);
757 for (auto &v : args.msl_discrete_descriptor_sets)
758 msl_comp->add_discrete_descriptor_set(v);
759 }
760 else if (args.hlsl)
761 compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir())));
762 else
763 {
764 combined_image_samplers = !args.vulkan_semantics;
765 if (!args.vulkan_semantics)
766 build_dummy_sampler = true;
767 compiler.reset(new CompilerGLSL(move(spirv_parser.get_parsed_ir())));
768 }
769
770 if (!args.variable_type_remaps.empty())
771 {
772 auto remap_cb = [&](const SPIRType &, const string &name, string &out) -> void {
773 for (const VariableTypeRemap &remap : args.variable_type_remaps)
774 if (name == remap.variable_name)
775 out = remap.new_variable_type;
776 };
777
778 compiler->set_variable_type_remap_callback(move(remap_cb));
779 }
780
781 for (auto &rename : args.entry_point_rename)
782 compiler->rename_entry_point(rename.old_name, rename.new_name, rename.execution_model);
783
784 auto entry_points = compiler->get_entry_points_and_stages();
785 auto entry_point = args.entry;
786 ExecutionModel model = ExecutionModelMax;
787
788 if (!args.entry_stage.empty())
789 {
790 model = stage_to_execution_model(args.entry_stage);
791 if (entry_point.empty())
792 {
793 // Just use the first entry point with this stage.
794 for (auto &e : entry_points)
795 {
796 if (e.execution_model == model)
797 {
798 entry_point = e.name;
799 break;
800 }
801 }
802
803 if (entry_point.empty())
804 {
805 fprintf(stderr, "Could not find an entry point with stage: %s\n", args.entry_stage.c_str());
806 exit(EXIT_FAILURE);
807 }
808 }
809 else
810 {
811 // Make sure both stage and name exists.
812 bool exists = false;
813 for (auto &e : entry_points)
814 {
815 if (e.execution_model == model && e.name == entry_point)
816 {
817 exists = true;
818 break;
819 }
820 }
821
822 if (!exists)
823 {
824 fprintf(stderr, "Could not find an entry point %s with stage: %s\n", entry_point.c_str(),
825 args.entry_stage.c_str());
826 exit(EXIT_FAILURE);
827 }
828 }
829 }
830 else if (!entry_point.empty())
831 {
832 // Make sure there is just one entry point with this name, or the stage
833 // is ambiguous.
834 uint32_t stage_count = 0;
835 for (auto &e : entry_points)
836 {
837 if (e.name == entry_point)
838 {
839 stage_count++;
840 model = e.execution_model;
841 }
842 }
843
844 if (stage_count == 0)
845 {
846 fprintf(stderr, "There is no entry point with name: %s\n", entry_point.c_str());
847 exit(EXIT_FAILURE);
848 }
849 else if (stage_count > 1)
850 {
851 fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", entry_point.c_str());
852 exit(EXIT_FAILURE);
853 }
854 }
855
856 if (!entry_point.empty())
857 compiler->set_entry_point(entry_point, model);
858
859 if (!args.set_version && !compiler->get_common_options().version)
860 {
861 fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n");
862 print_help();
863 exit(EXIT_FAILURE);
864 }
865
866 CompilerGLSL::Options opts = compiler->get_common_options();
867 if (args.set_version)
868 opts.version = args.version;
869 if (args.set_es)
870 opts.es = args.es;
871 opts.force_temporary = args.force_temporary;
872 opts.separate_shader_objects = args.sso;
873 opts.flatten_multidimensional_arrays = args.flatten_multidimensional_arrays;
874 opts.enable_420pack_extension = args.use_420pack_extension;
875 opts.vulkan_semantics = args.vulkan_semantics;
876 opts.vertex.fixup_clipspace = args.fixup;
877 opts.vertex.flip_vert_y = args.yflip;
878 opts.vertex.support_nonzero_base_instance = args.support_nonzero_baseinstance;
879 opts.emit_push_constant_as_uniform_buffer = args.glsl_emit_push_constant_as_ubo;
880 opts.emit_uniform_buffer_as_plain_uniforms = args.glsl_emit_ubo_as_plain_uniforms;
881 opts.emit_line_directives = args.emit_line_directives;
882 compiler->set_common_options(opts);
883
884 // Set HLSL specific options.
885 if (args.hlsl)
886 {
887 auto *hlsl = static_cast<CompilerHLSL *>(compiler.get());
888 auto hlsl_opts = hlsl->get_hlsl_options();
889 if (args.set_shader_model)
890 {
891 if (args.shader_model < 30)
892 {
893 fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n");
894 exit(EXIT_FAILURE);
895 }
896
897 hlsl_opts.shader_model = args.shader_model;
898 }
899
900 if (args.hlsl_compat)
901 {
902 // Enable all compat options.
903 hlsl_opts.point_size_compat = true;
904 hlsl_opts.point_coord_compat = true;
905 }
906
907 if (hlsl_opts.shader_model <= 30)
908 {
909 combined_image_samplers = true;
910 build_dummy_sampler = true;
911 }
912
913 hlsl_opts.support_nonzero_base_vertex_base_instance = args.hlsl_support_nonzero_base;
914 hlsl->set_hlsl_options(hlsl_opts);
915 }
916
917 if (build_dummy_sampler)
918 {
919 uint32_t sampler = compiler->build_dummy_sampler_for_combined_images();
920 if (sampler != 0)
921 {
922 // Set some defaults to make validation happy.
923 compiler->set_decoration(sampler, DecorationDescriptorSet, 0);
924 compiler->set_decoration(sampler, DecorationBinding, 0);
925 }
926 }
927
928 ShaderResources res;
929 if (args.remove_unused)
930 {
931 auto active = compiler->get_active_interface_variables();
932 res = compiler->get_shader_resources(active);
933 compiler->set_enabled_interface_variables(move(active));
934 }
935 else
936 res = compiler->get_shader_resources();
937
938 if (args.flatten_ubo)
939 {
940 for (auto &ubo : res.uniform_buffers)
941 compiler->flatten_buffer_block(ubo.id);
942 for (auto &ubo : res.push_constant_buffers)
943 compiler->flatten_buffer_block(ubo.id);
944 }
945
946 auto pls_inputs = remap_pls(args.pls_in, res.stage_inputs, &res.subpass_inputs);
947 auto pls_outputs = remap_pls(args.pls_out, res.stage_outputs, nullptr);
948 compiler->remap_pixel_local_storage(move(pls_inputs), move(pls_outputs));
949
950 for (auto &ext : args.extensions)
951 compiler->require_extension(ext);
952
953 for (auto &remap : args.remaps)
954 {
955 if (remap_generic(*compiler, res.stage_inputs, remap))
956 continue;
957 if (remap_generic(*compiler, res.stage_outputs, remap))
958 continue;
959 if (remap_generic(*compiler, res.subpass_inputs, remap))
960 continue;
961 }
962
963 for (auto &rename : args.interface_variable_renames)
964 {
965 if (rename.storageClass == StorageClassInput)
966 spirv_cross_util::rename_interface_variable(*compiler, res.stage_inputs, rename.location,
967 rename.variable_name);
968 else if (rename.storageClass == StorageClassOutput)
969 spirv_cross_util::rename_interface_variable(*compiler, res.stage_outputs, rename.location,
970 rename.variable_name);
971 else
972 {
973 fprintf(stderr, "error at --rename-interface-variable <in|out> ...\n");
974 exit(EXIT_FAILURE);
975 }
976 }
977
978 if (args.dump_resources)
979 {
980 print_resources(*compiler, res);
981 print_push_constant_resources(*compiler, res.push_constant_buffers);
982 print_spec_constants(*compiler);
983 print_capabilities_and_extensions(*compiler);
984 }
985
986 if (combined_image_samplers)
987 {
988 compiler->build_combined_image_samplers();
989 if (args.combined_samplers_inherit_bindings)
990 spirv_cross_util::inherit_combined_sampler_bindings(*compiler);
991
992 // Give the remapped combined samplers new names.
993 for (auto &remap : compiler->get_combined_image_samplers())
994 {
995 compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id),
996 compiler->get_name(remap.sampler_id)));
997 }
998 }
999
1000 if (args.hlsl)
1001 {
1002 auto *hlsl_compiler = static_cast<CompilerHLSL *>(compiler.get());
1003 uint32_t new_builtin = hlsl_compiler->remap_num_workgroups_builtin();
1004 if (new_builtin)
1005 {
1006 hlsl_compiler->set_decoration(new_builtin, DecorationDescriptorSet, 0);
1007 hlsl_compiler->set_decoration(new_builtin, DecorationBinding, 0);
1008 }
1009 }
1010
1011 if (args.hlsl)
1012 {
1013 for (auto &remap : args.hlsl_attr_remap)
1014 static_cast<CompilerHLSL *>(compiler.get())->add_vertex_attribute_remap(remap);
1015 }
1016
1017 return compiler->compile();
1018 }
1019
main_inner(int argc,char * argv[])1020 static int main_inner(int argc, char *argv[])
1021 {
1022 CLIArguments args;
1023 CLICallbacks cbs;
1024
1025 cbs.add("--help", [](CLIParser &parser) {
1026 print_help();
1027 parser.end();
1028 });
1029 cbs.add("--revision", [](CLIParser &parser) {
1030 print_version();
1031 parser.end();
1032 });
1033 cbs.add("--output", [&args](CLIParser &parser) { args.output = parser.next_string(); });
1034 cbs.add("--es", [&args](CLIParser &) {
1035 args.es = true;
1036 args.set_es = true;
1037 });
1038 cbs.add("--no-es", [&args](CLIParser &) {
1039 args.es = false;
1040 args.set_es = true;
1041 });
1042 cbs.add("--version", [&args](CLIParser &parser) {
1043 args.version = parser.next_uint();
1044 args.set_version = true;
1045 });
1046 cbs.add("--dump-resources", [&args](CLIParser &) { args.dump_resources = true; });
1047 cbs.add("--force-temporary", [&args](CLIParser &) { args.force_temporary = true; });
1048 cbs.add("--flatten-ubo", [&args](CLIParser &) { args.flatten_ubo = true; });
1049 cbs.add("--fixup-clipspace", [&args](CLIParser &) { args.fixup = true; });
1050 cbs.add("--flip-vert-y", [&args](CLIParser &) { args.yflip = true; });
1051 cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); });
1052 cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; });
1053 cbs.add("--reflect", [&args](CLIParser &parser) { args.reflect = parser.next_value_string("json"); });
1054 cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); });
1055 cbs.add("--metal", [&args](CLIParser &) { args.msl = true; }); // Legacy compatibility
1056 cbs.add("--glsl-emit-push-constant-as-ubo", [&args](CLIParser &) { args.glsl_emit_push_constant_as_ubo = true; });
1057 cbs.add("--glsl-emit-ubo-as-plain-uniforms", [&args](CLIParser &) { args.glsl_emit_ubo_as_plain_uniforms = true; });
1058 cbs.add("--msl", [&args](CLIParser &) { args.msl = true; });
1059 cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; });
1060 cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; });
1061 cbs.add("--hlsl-support-nonzero-basevertex-baseinstance",
1062 [&args](CLIParser &) { args.hlsl_support_nonzero_base = true; });
1063 cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; });
1064 cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; });
1065 cbs.add("--no-420pack-extension", [&args](CLIParser &) { args.use_420pack_extension = false; });
1066 cbs.add("--msl-capture-output", [&args](CLIParser &) { args.msl_capture_output_to_buffer = true; });
1067 cbs.add("--msl-swizzle-texture-samples", [&args](CLIParser &) { args.msl_swizzle_texture_samples = true; });
1068 cbs.add("--msl-ios", [&args](CLIParser &) { args.msl_ios = true; });
1069 cbs.add("--msl-pad-fragment-output", [&args](CLIParser &) { args.msl_pad_fragment_output = true; });
1070 cbs.add("--msl-domain-lower-left", [&args](CLIParser &) { args.msl_domain_lower_left = true; });
1071 cbs.add("--msl-argument-buffers", [&args](CLIParser &) { args.msl_argument_buffers = true; });
1072 cbs.add("--msl-discrete-descriptor-set",
1073 [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); });
1074 cbs.add("--msl-texture-buffer-native", [&args](CLIParser &) { args.msl_texture_buffer_native = true; });
1075 cbs.add("--msl-multiview", [&args](CLIParser &) { args.msl_multiview = true; });
1076 cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
1077 cbs.add("--rename-entry-point", [&args](CLIParser &parser) {
1078 auto old_name = parser.next_string();
1079 auto new_name = parser.next_string();
1080 auto model = stage_to_execution_model(parser.next_string());
1081 args.entry_point_rename.push_back({ old_name, new_name, move(model) });
1082 });
1083 cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); });
1084 cbs.add("--stage", [&args](CLIParser &parser) { args.entry_stage = parser.next_string(); });
1085 cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; });
1086 cbs.add("--set-hlsl-vertex-input-semantic", [&args](CLIParser &parser) {
1087 HLSLVertexAttributeRemap remap;
1088 remap.location = parser.next_uint();
1089 remap.semantic = parser.next_string();
1090 args.hlsl_attr_remap.push_back(move(remap));
1091 });
1092
1093 cbs.add("--remap", [&args](CLIParser &parser) {
1094 string src = parser.next_string();
1095 string dst = parser.next_string();
1096 uint32_t components = parser.next_uint();
1097 args.remaps.push_back({ move(src), move(dst), components });
1098 });
1099
1100 cbs.add("--remap-variable-type", [&args](CLIParser &parser) {
1101 string var_name = parser.next_string();
1102 string new_type = parser.next_string();
1103 args.variable_type_remaps.push_back({ move(var_name), move(new_type) });
1104 });
1105
1106 cbs.add("--rename-interface-variable", [&args](CLIParser &parser) {
1107 StorageClass cls = StorageClassMax;
1108 string clsStr = parser.next_string();
1109 if (clsStr == "in")
1110 cls = StorageClassInput;
1111 else if (clsStr == "out")
1112 cls = StorageClassOutput;
1113
1114 uint32_t loc = parser.next_uint();
1115 string var_name = parser.next_string();
1116 args.interface_variable_renames.push_back({ cls, loc, move(var_name) });
1117 });
1118
1119 cbs.add("--pls-in", [&args](CLIParser &parser) {
1120 auto fmt = pls_format(parser.next_string());
1121 auto name = parser.next_string();
1122 args.pls_in.push_back({ move(fmt), move(name) });
1123 });
1124 cbs.add("--pls-out", [&args](CLIParser &parser) {
1125 auto fmt = pls_format(parser.next_string());
1126 auto name = parser.next_string();
1127 args.pls_out.push_back({ move(fmt), move(name) });
1128 });
1129 cbs.add("--shader-model", [&args](CLIParser &parser) {
1130 args.shader_model = parser.next_uint();
1131 args.set_shader_model = true;
1132 });
1133 cbs.add("--msl-version", [&args](CLIParser &parser) {
1134 args.msl_version = parser.next_uint();
1135 args.set_msl_version = true;
1136 });
1137
1138 cbs.add("--remove-unused-variables", [&args](CLIParser &) { args.remove_unused = true; });
1139 cbs.add("--combined-samplers-inherit-bindings",
1140 [&args](CLIParser &) { args.combined_samplers_inherit_bindings = true; });
1141
1142 cbs.add("--no-support-nonzero-baseinstance", [&](CLIParser &) { args.support_nonzero_baseinstance = false; });
1143 cbs.add("--emit-line-directives", [&args](CLIParser &) { args.emit_line_directives = true; });
1144
1145 cbs.default_handler = [&args](const char *value) { args.input = value; };
1146 cbs.error_handler = [] { print_help(); };
1147
1148 CLIParser parser{ move(cbs), argc - 1, argv + 1 };
1149 if (!parser.parse())
1150 return EXIT_FAILURE;
1151 else if (parser.ended_state)
1152 return EXIT_SUCCESS;
1153
1154 if (!args.input)
1155 {
1156 fprintf(stderr, "Didn't specify input file.\n");
1157 print_help();
1158 return EXIT_FAILURE;
1159 }
1160
1161 auto spirv_file = read_spirv_file(args.input);
1162 if (spirv_file.empty())
1163 return EXIT_FAILURE;
1164
1165 // Special case reflection because it has little to do with the path followed by code-outputting compilers
1166 if (!args.reflect.empty())
1167 {
1168 Parser spirv_parser(move(spirv_file));
1169 spirv_parser.parse();
1170
1171 CompilerReflection compiler(move(spirv_parser.get_parsed_ir()));
1172 compiler.set_format(args.reflect);
1173 auto json = compiler.compile();
1174 if (args.output)
1175 write_string_to_file(args.output, json.c_str());
1176 else
1177 printf("%s", json.c_str());
1178 return EXIT_SUCCESS;
1179 }
1180
1181 string compiled_output;
1182
1183 if (args.iterations == 1)
1184 compiled_output = compile_iteration(args, move(spirv_file));
1185 else
1186 {
1187 for (unsigned i = 0; i < args.iterations; i++)
1188 compiled_output = compile_iteration(args, spirv_file);
1189 }
1190
1191 if (args.output)
1192 write_string_to_file(args.output, compiled_output.c_str());
1193 else
1194 printf("%s", compiled_output.c_str());
1195
1196 return EXIT_SUCCESS;
1197 }
1198
main(int argc,char * argv[])1199 int main(int argc, char *argv[])
1200 {
1201 #ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
1202 return main_inner(argc, argv);
1203 #else
1204 // Make sure we catch the exception or it just disappears into the aether on Windows.
1205 try
1206 {
1207 return main_inner(argc, argv);
1208 }
1209 catch (const std::exception &e)
1210 {
1211 fprintf(stderr, "SPIRV-Cross threw an exception: %s\n", e.what());
1212 return EXIT_FAILURE;
1213 }
1214 #endif
1215 }
1216