1 /*
2 * Copyright 2015-2020 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 using namespace spv;
39 using namespace SPIRV_CROSS_NAMESPACE;
40 using namespace std;
41
42 #ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
THROW(const char * str)43 static inline void THROW(const char *str)
44 {
45 fprintf(stderr, "SPIRV-Cross will abort: %s\n", str);
46 fflush(stderr);
47 abort();
48 }
49 #else
50 #define THROW(x) throw runtime_error(x)
51 #endif
52
53 struct CLIParser;
54 struct CLICallbacks
55 {
addCLICallbacks56 void add(const char *cli, const function<void(CLIParser &)> &func)
57 {
58 callbacks[cli] = func;
59 }
60 unordered_map<string, function<void(CLIParser &)>> callbacks;
61 function<void()> error_handler;
62 function<void(const char *)> default_handler;
63 };
64
65 struct CLIParser
66 {
CLIParserCLIParser67 CLIParser(CLICallbacks cbs_, int argc_, char *argv_[])
68 : cbs(move(cbs_))
69 , argc(argc_)
70 , argv(argv_)
71 {
72 }
73
parseCLIParser74 bool parse()
75 {
76 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
77 try
78 #endif
79 {
80 while (argc && !ended_state)
81 {
82 const char *next = *argv++;
83 argc--;
84
85 if (*next != '-' && cbs.default_handler)
86 {
87 cbs.default_handler(next);
88 }
89 else
90 {
91 auto itr = cbs.callbacks.find(next);
92 if (itr == ::end(cbs.callbacks))
93 {
94 THROW("Invalid argument");
95 }
96
97 itr->second(*this);
98 }
99 }
100
101 return true;
102 }
103 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
104 catch (...)
105 {
106 if (cbs.error_handler)
107 {
108 cbs.error_handler();
109 }
110 return false;
111 }
112 #endif
113 }
114
endCLIParser115 void end()
116 {
117 ended_state = true;
118 }
119
next_uintCLIParser120 uint32_t next_uint()
121 {
122 if (!argc)
123 {
124 THROW("Tried to parse uint, but nothing left in arguments");
125 }
126
127 uint64_t val = stoul(*argv);
128 if (val > numeric_limits<uint32_t>::max())
129 {
130 THROW("next_uint() out of range");
131 }
132
133 argc--;
134 argv++;
135
136 return uint32_t(val);
137 }
138
next_hex_uintCLIParser139 uint32_t next_hex_uint()
140 {
141 if (!argc)
142 {
143 THROW("Tried to parse uint, but nothing left in arguments");
144 }
145
146 uint64_t val = stoul(*argv, nullptr, 16);
147 if (val > numeric_limits<uint32_t>::max())
148 {
149 THROW("next_uint() out of range");
150 }
151
152 argc--;
153 argv++;
154
155 return uint32_t(val);
156 }
157
next_doubleCLIParser158 double next_double()
159 {
160 if (!argc)
161 {
162 THROW("Tried to parse double, but nothing left in arguments");
163 }
164
165 double val = stod(*argv);
166
167 argc--;
168 argv++;
169
170 return val;
171 }
172
173 // Return a string only if it's not prefixed with `--`, otherwise return the default value
next_value_stringCLIParser174 const char *next_value_string(const char *default_value)
175 {
176 if (!argc)
177 {
178 return default_value;
179 }
180
181 if (0 == strncmp("--", *argv, 2))
182 {
183 return default_value;
184 }
185
186 return next_string();
187 }
188
next_stringCLIParser189 const char *next_string()
190 {
191 if (!argc)
192 {
193 THROW("Tried to parse string, but nothing left in arguments");
194 }
195
196 const char *ret = *argv;
197 argc--;
198 argv++;
199 return ret;
200 }
201
202 CLICallbacks cbs;
203 int argc;
204 char **argv;
205 bool ended_state = false;
206 };
207
208 #if defined(__clang__) || defined(__GNUC__)
209 #pragma GCC diagnostic push
210 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
211 #elif defined(_MSC_VER)
212 #pragma warning(push)
213 #pragma warning(disable : 4996)
214 #endif
215
read_spirv_file(const char * path)216 static vector<uint32_t> read_spirv_file(const char *path)
217 {
218 FILE *file = fopen(path, "rb");
219 if (!file)
220 {
221 fprintf(stderr, "Failed to open SPIR-V file: %s\n", path);
222 return {};
223 }
224
225 fseek(file, 0, SEEK_END);
226 long len = ftell(file) / sizeof(uint32_t);
227 rewind(file);
228
229 vector<uint32_t> spirv(len);
230 if (fread(spirv.data(), sizeof(uint32_t), len, file) != size_t(len))
231 spirv.clear();
232
233 fclose(file);
234 return spirv;
235 }
236
write_string_to_file(const char * path,const char * string)237 static bool write_string_to_file(const char *path, const char *string)
238 {
239 FILE *file = fopen(path, "w");
240 if (!file)
241 {
242 fprintf(stderr, "Failed to write file: %s\n", path);
243 return false;
244 }
245
246 fprintf(file, "%s", string);
247 fclose(file);
248 return true;
249 }
250
251 #if defined(__clang__) || defined(__GNUC__)
252 #pragma GCC diagnostic pop
253 #elif defined(_MSC_VER)
254 #pragma warning(pop)
255 #endif
256
print_resources(const Compiler & compiler,const char * tag,const SmallVector<Resource> & resources)257 static void print_resources(const Compiler &compiler, const char *tag, const SmallVector<Resource> &resources)
258 {
259 fprintf(stderr, "%s\n", tag);
260 fprintf(stderr, "=============\n\n");
261 bool print_ssbo = !strcmp(tag, "ssbos");
262
263 for (auto &res : resources)
264 {
265 auto &type = compiler.get_type(res.type_id);
266
267 if (print_ssbo && compiler.buffer_is_hlsl_counter_buffer(res.id))
268 continue;
269
270 // If we don't have a name, use the fallback for the type instead of the variable
271 // for SSBOs and UBOs since those are the only meaningful names to use externally.
272 // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
273 bool is_push_constant = compiler.get_storage_class(res.id) == StorageClassPushConstant;
274 bool is_block = compiler.get_decoration_bitset(type.self).get(DecorationBlock) ||
275 compiler.get_decoration_bitset(type.self).get(DecorationBufferBlock);
276 bool is_sized_block = is_block && (compiler.get_storage_class(res.id) == StorageClassUniform ||
277 compiler.get_storage_class(res.id) == StorageClassUniformConstant);
278 ID fallback_id = !is_push_constant && is_block ? ID(res.base_type_id) : ID(res.id);
279
280 uint32_t block_size = 0;
281 uint32_t runtime_array_stride = 0;
282 if (is_sized_block)
283 {
284 auto &base_type = compiler.get_type(res.base_type_id);
285 block_size = uint32_t(compiler.get_declared_struct_size(base_type));
286 runtime_array_stride = uint32_t(compiler.get_declared_struct_size_runtime_array(base_type, 1) -
287 compiler.get_declared_struct_size_runtime_array(base_type, 0));
288 }
289
290 Bitset mask;
291 if (print_ssbo)
292 mask = compiler.get_buffer_block_flags(res.id);
293 else
294 mask = compiler.get_decoration_bitset(res.id);
295
296 string array;
297 for (auto arr : type.array)
298 array = join("[", arr ? convert_to_string(arr) : "", "]") + array;
299
300 fprintf(stderr, " ID %03u : %s%s", uint32_t(res.id),
301 !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str(), array.c_str());
302
303 if (mask.get(DecorationLocation))
304 fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation));
305 if (mask.get(DecorationDescriptorSet))
306 fprintf(stderr, " (Set : %u)", compiler.get_decoration(res.id, DecorationDescriptorSet));
307 if (mask.get(DecorationBinding))
308 fprintf(stderr, " (Binding : %u)", compiler.get_decoration(res.id, DecorationBinding));
309 if (static_cast<const CompilerGLSL &>(compiler).variable_is_depth_or_compare(res.id))
310 fprintf(stderr, " (comparison)");
311 if (mask.get(DecorationInputAttachmentIndex))
312 fprintf(stderr, " (Attachment : %u)", compiler.get_decoration(res.id, DecorationInputAttachmentIndex));
313 if (mask.get(DecorationNonReadable))
314 fprintf(stderr, " writeonly");
315 if (mask.get(DecorationNonWritable))
316 fprintf(stderr, " readonly");
317 if (is_sized_block)
318 {
319 fprintf(stderr, " (BlockSize : %u bytes)", block_size);
320 if (runtime_array_stride)
321 fprintf(stderr, " (Unsized array stride: %u bytes)", runtime_array_stride);
322 }
323
324 uint32_t counter_id = 0;
325 if (print_ssbo && compiler.buffer_get_hlsl_counter_buffer(res.id, counter_id))
326 fprintf(stderr, " (HLSL counter buffer ID: %u)", counter_id);
327 fprintf(stderr, "\n");
328 }
329 fprintf(stderr, "=============\n\n");
330 }
331
execution_model_to_str(spv::ExecutionModel model)332 static const char *execution_model_to_str(spv::ExecutionModel model)
333 {
334 switch (model)
335 {
336 case spv::ExecutionModelVertex:
337 return "vertex";
338 case spv::ExecutionModelTessellationControl:
339 return "tessellation control";
340 case ExecutionModelTessellationEvaluation:
341 return "tessellation evaluation";
342 case ExecutionModelGeometry:
343 return "geometry";
344 case ExecutionModelFragment:
345 return "fragment";
346 case ExecutionModelGLCompute:
347 return "compute";
348 case ExecutionModelRayGenerationNV:
349 return "raygenNV";
350 case ExecutionModelIntersectionNV:
351 return "intersectionNV";
352 case ExecutionModelCallableNV:
353 return "callableNV";
354 case ExecutionModelAnyHitNV:
355 return "anyhitNV";
356 case ExecutionModelClosestHitNV:
357 return "closesthitNV";
358 case ExecutionModelMissNV:
359 return "missNV";
360 default:
361 return "???";
362 }
363 }
364
print_resources(const Compiler & compiler,const ShaderResources & res)365 static void print_resources(const Compiler &compiler, const ShaderResources &res)
366 {
367 auto &modes = compiler.get_execution_mode_bitset();
368
369 fprintf(stderr, "Entry points:\n");
370 auto entry_points = compiler.get_entry_points_and_stages();
371 for (auto &e : entry_points)
372 fprintf(stderr, " %s (%s)\n", e.name.c_str(), execution_model_to_str(e.execution_model));
373 fprintf(stderr, "\n");
374
375 fprintf(stderr, "Execution modes:\n");
376 modes.for_each_bit([&](uint32_t i) {
377 auto mode = static_cast<ExecutionMode>(i);
378 uint32_t arg0 = compiler.get_execution_mode_argument(mode, 0);
379 uint32_t arg1 = compiler.get_execution_mode_argument(mode, 1);
380 uint32_t arg2 = compiler.get_execution_mode_argument(mode, 2);
381
382 switch (static_cast<ExecutionMode>(i))
383 {
384 case ExecutionModeInvocations:
385 fprintf(stderr, " Invocations: %u\n", arg0);
386 break;
387
388 case ExecutionModeLocalSize:
389 fprintf(stderr, " LocalSize: (%u, %u, %u)\n", arg0, arg1, arg2);
390 break;
391
392 case ExecutionModeOutputVertices:
393 fprintf(stderr, " OutputVertices: %u\n", arg0);
394 break;
395
396 #define CHECK_MODE(m) \
397 case ExecutionMode##m: \
398 fprintf(stderr, " %s\n", #m); \
399 break
400 CHECK_MODE(SpacingEqual);
401 CHECK_MODE(SpacingFractionalEven);
402 CHECK_MODE(SpacingFractionalOdd);
403 CHECK_MODE(VertexOrderCw);
404 CHECK_MODE(VertexOrderCcw);
405 CHECK_MODE(PixelCenterInteger);
406 CHECK_MODE(OriginUpperLeft);
407 CHECK_MODE(OriginLowerLeft);
408 CHECK_MODE(EarlyFragmentTests);
409 CHECK_MODE(PointMode);
410 CHECK_MODE(Xfb);
411 CHECK_MODE(DepthReplacing);
412 CHECK_MODE(DepthGreater);
413 CHECK_MODE(DepthLess);
414 CHECK_MODE(DepthUnchanged);
415 CHECK_MODE(LocalSizeHint);
416 CHECK_MODE(InputPoints);
417 CHECK_MODE(InputLines);
418 CHECK_MODE(InputLinesAdjacency);
419 CHECK_MODE(Triangles);
420 CHECK_MODE(InputTrianglesAdjacency);
421 CHECK_MODE(Quads);
422 CHECK_MODE(Isolines);
423 CHECK_MODE(OutputPoints);
424 CHECK_MODE(OutputLineStrip);
425 CHECK_MODE(OutputTriangleStrip);
426 CHECK_MODE(VecTypeHint);
427 CHECK_MODE(ContractionOff);
428
429 default:
430 break;
431 }
432 });
433 fprintf(stderr, "\n");
434
435 print_resources(compiler, "subpass inputs", res.subpass_inputs);
436 print_resources(compiler, "inputs", res.stage_inputs);
437 print_resources(compiler, "outputs", res.stage_outputs);
438 print_resources(compiler, "textures", res.sampled_images);
439 print_resources(compiler, "separate images", res.separate_images);
440 print_resources(compiler, "separate samplers", res.separate_samplers);
441 print_resources(compiler, "images", res.storage_images);
442 print_resources(compiler, "ssbos", res.storage_buffers);
443 print_resources(compiler, "ubos", res.uniform_buffers);
444 print_resources(compiler, "push", res.push_constant_buffers);
445 print_resources(compiler, "counters", res.atomic_counters);
446 print_resources(compiler, "acceleration structures", res.acceleration_structures);
447 }
448
print_push_constant_resources(const Compiler & compiler,const SmallVector<Resource> & res)449 static void print_push_constant_resources(const Compiler &compiler, const SmallVector<Resource> &res)
450 {
451 for (auto &block : res)
452 {
453 auto ranges = compiler.get_active_buffer_ranges(block.id);
454 fprintf(stderr, "Active members in buffer: %s\n",
455 !block.name.empty() ? block.name.c_str() : compiler.get_fallback_name(block.id).c_str());
456
457 fprintf(stderr, "==================\n\n");
458 for (auto &range : ranges)
459 {
460 const auto &name = compiler.get_member_name(block.base_type_id, range.index);
461
462 fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index,
463 !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(),
464 unsigned(range.offset), unsigned(range.range));
465 }
466 fprintf(stderr, "==================\n\n");
467 }
468 }
469
print_spec_constants(const Compiler & compiler)470 static void print_spec_constants(const Compiler &compiler)
471 {
472 auto spec_constants = compiler.get_specialization_constants();
473 fprintf(stderr, "Specialization constants\n");
474 fprintf(stderr, "==================\n\n");
475 for (auto &c : spec_constants)
476 fprintf(stderr, "ID: %u, Spec ID: %u\n", uint32_t(c.id), c.constant_id);
477 fprintf(stderr, "==================\n\n");
478 }
479
print_capabilities_and_extensions(const Compiler & compiler)480 static void print_capabilities_and_extensions(const Compiler &compiler)
481 {
482 fprintf(stderr, "Capabilities\n");
483 fprintf(stderr, "============\n");
484 for (auto &capability : compiler.get_declared_capabilities())
485 fprintf(stderr, "Capability: %u\n", static_cast<unsigned>(capability));
486 fprintf(stderr, "============\n\n");
487
488 fprintf(stderr, "Extensions\n");
489 fprintf(stderr, "============\n");
490 for (auto &ext : compiler.get_declared_extensions())
491 fprintf(stderr, "Extension: %s\n", ext.c_str());
492 fprintf(stderr, "============\n\n");
493 }
494
495 struct PLSArg
496 {
497 PlsFormat format;
498 string name;
499 };
500
501 struct Remap
502 {
503 string src_name;
504 string dst_name;
505 unsigned components;
506 };
507
508 struct VariableTypeRemap
509 {
510 string variable_name;
511 string new_variable_type;
512 };
513
514 struct InterfaceVariableRename
515 {
516 StorageClass storageClass;
517 uint32_t location;
518 string variable_name;
519 };
520
521 struct CLIArguments
522 {
523 const char *input = nullptr;
524 const char *output = nullptr;
525 const char *cpp_interface_name = nullptr;
526 uint32_t version = 0;
527 uint32_t shader_model = 0;
528 uint32_t msl_version = 0;
529 bool es = false;
530 bool set_version = false;
531 bool set_shader_model = false;
532 bool set_msl_version = false;
533 bool set_es = false;
534 bool dump_resources = false;
535 bool force_temporary = false;
536 bool flatten_ubo = false;
537 bool fixup = false;
538 bool yflip = false;
539 bool sso = false;
540 bool support_nonzero_baseinstance = true;
541 bool msl_capture_output_to_buffer = false;
542 bool msl_swizzle_texture_samples = false;
543 bool msl_ios = false;
544 bool msl_pad_fragment_output = false;
545 bool msl_domain_lower_left = false;
546 bool msl_argument_buffers = false;
547 bool msl_texture_buffer_native = false;
548 bool msl_framebuffer_fetch = false;
549 bool msl_invariant_float_math = false;
550 bool msl_emulate_cube_array = false;
551 bool msl_multiview = false;
552 bool msl_multiview_layered_rendering = true;
553 bool msl_view_index_from_device_index = false;
554 bool msl_dispatch_base = false;
555 bool msl_decoration_binding = false;
556 bool msl_force_active_argument_buffer_resources = false;
557 bool msl_force_native_arrays = false;
558 bool msl_enable_frag_depth_builtin = true;
559 bool msl_enable_frag_stencil_ref_builtin = true;
560 uint32_t msl_enable_frag_output_mask = 0xffffffff;
561 bool msl_enable_clip_distance_user_varying = true;
562 bool msl_multi_patch_workgroup = false;
563 bool msl_vertex_for_tessellation = false;
564 uint32_t msl_additional_fixed_sample_mask = 0xffffffff;
565 bool msl_arrayed_subpass_input = false;
566 bool glsl_emit_push_constant_as_ubo = false;
567 bool glsl_emit_ubo_as_plain_uniforms = false;
568 bool glsl_force_flattened_io_blocks = false;
569 SmallVector<pair<uint32_t, uint32_t>> glsl_ext_framebuffer_fetch;
570 bool vulkan_glsl_disable_ext_samplerless_texture_functions = false;
571 bool emit_line_directives = false;
572 bool enable_storage_image_qualifier_deduction = true;
573 bool force_zero_initialized_variables = false;
574 SmallVector<uint32_t> msl_discrete_descriptor_sets;
575 SmallVector<uint32_t> msl_device_argument_buffers;
576 SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers;
577 SmallVector<pair<uint32_t, uint32_t>> msl_inline_uniform_blocks;
578 SmallVector<MSLShaderInput> msl_shader_inputs;
579 SmallVector<PLSArg> pls_in;
580 SmallVector<PLSArg> pls_out;
581 SmallVector<Remap> remaps;
582 SmallVector<string> extensions;
583 SmallVector<VariableTypeRemap> variable_type_remaps;
584 SmallVector<InterfaceVariableRename> interface_variable_renames;
585 SmallVector<HLSLVertexAttributeRemap> hlsl_attr_remap;
586 string entry;
587 string entry_stage;
588
589 struct Rename
590 {
591 string old_name;
592 string new_name;
593 ExecutionModel execution_model;
594 };
595 SmallVector<Rename> entry_point_rename;
596
597 uint32_t iterations = 1;
598 bool cpp = false;
599 string reflect;
600 bool msl = false;
601 bool hlsl = false;
602 bool hlsl_compat = false;
603 bool hlsl_support_nonzero_base = false;
604 bool hlsl_force_storage_buffer_as_uav = false;
605 bool hlsl_nonwritable_uav_texture_as_srv = false;
606 bool hlsl_enable_16bit_types = false;
607 HLSLBindingFlags hlsl_binding_flags = 0;
608 bool vulkan_semantics = false;
609 bool flatten_multidimensional_arrays = false;
610 bool use_420pack_extension = true;
611 bool remove_unused = false;
612 bool combined_samplers_inherit_bindings = false;
613 };
614
print_version()615 static void print_version()
616 {
617 #ifdef HAVE_SPIRV_CROSS_GIT_VERSION
618 fprintf(stderr, "%s\n", SPIRV_CROSS_GIT_REVISION);
619 #else
620 fprintf(stderr, "Git revision unknown. Build with CMake to create timestamp and revision info.\n");
621 #endif
622 }
623
print_help_backend()624 static void print_help_backend()
625 {
626 // clang-format off
627 fprintf(stderr, "\nSelect backend:\n"
628 "\tBy default, OpenGL-style GLSL is the target, with #version and GLSL/ESSL information inherited from the SPIR-V module if present.\n"
629 "\t[--vulkan-semantics] or [-V]:\n\t\tEmit Vulkan GLSL instead of plain GLSL. Makes use of Vulkan-only features to match SPIR-V.\n"
630 "\t[--msl]:\n\t\tEmit Metal Shading Language (MSL).\n"
631 "\t[--hlsl]:\n\t\tEmit HLSL.\n"
632 "\t[--reflect]:\n\t\tEmit JSON reflection.\n"
633 "\t[--cpp]:\n\t\tDEPRECATED. Emits C++ code.\n"
634 );
635 // clang-format on
636 }
637
print_help_glsl()638 static void print_help_glsl()
639 {
640 // clang-format off
641 fprintf(stderr, "\nGLSL options:\n"
642 "\t[--es]:\n\t\tForce ESSL.\n"
643 "\t[--no-es]:\n\t\tForce desktop GLSL.\n"
644 "\t[--version <GLSL version>]:\n\t\tE.g. --version 450 will emit '#version 450' in shader.\n"
645 "\t\tCode generation will depend on the version used.\n"
646 "\t[--flatten-ubo]:\n\t\tEmit UBOs as plain uniform arrays which are suitable for use with glUniform4*v().\n"
647 "\t\tThis can be an optimization on GL implementations where this is faster or works around buggy driver implementations.\n"
648 "\t\tE.g.: uniform MyUBO { vec4 a; float b, c, d, e; }; will be emitted as uniform vec4 MyUBO[2];\n"
649 "\t\tCaveat: You cannot mix and match floating-point and integer in the same UBO with this option.\n"
650 "\t\tLegacy GLSL/ESSL (where this flattening makes sense) does not support bit-casting, which would have been the obvious workaround.\n"
651 "\t[--extension ext]:\n\t\tAdd #extension string of your choosing to GLSL output.\n"
652 "\t\tUseful if you use variable name remapping to something that requires an extension unknown to SPIRV-Cross.\n"
653 "\t[--remove-unused-variables]:\n\t\tDo not emit interface variables which are not statically accessed by the shader.\n"
654 "\t[--separate-shader-objects]:\n\t\tRedeclare gl_PerVertex blocks to be suitable for desktop GL separate shader objects.\n"
655 "\t[--glsl-emit-push-constant-as-ubo]:\n\t\tInstead of a plain uniform of struct for push constants, emit a UBO block instead.\n"
656 "\t[--glsl-emit-ubo-as-plain-uniforms]:\n\t\tInstead of emitting UBOs, emit them as plain uniform structs.\n"
657 "\t[--glsl-remap-ext-framebuffer-fetch input-attachment color-location]:\n\t\tRemaps an input attachment to use GL_EXT_shader_framebuffer_fetch.\n"
658 "\t\tgl_LastFragData[location] is read from. The attachment to read from must be declared as an output in the shader.\n"
659 "\t[--vulkan-glsl-disable-ext-samplerless-texture-functions]:\n\t\tDo not allow use of GL_EXT_samperless_texture_functions, even in Vulkan GLSL.\n"
660 "\t\tUse of texelFetch and similar might have to create dummy samplers to work around it.\n"
661 "\t[--combined-samplers-inherit-bindings]:\n\t\tInherit binding information from the textures when building combined image samplers from separate textures and samplers.\n"
662 "\t[--no-support-nonzero-baseinstance]:\n\t\tWhen using gl_InstanceIndex with desktop GL,\n"
663 "\t\tassume that base instance is always 0, and do not attempt to fix up gl_InstanceID to match Vulkan semantics.\n"
664 "\t[--pls-in format input-name]:\n\t\tRemaps a subpass input with name into a GL_EXT_pixel_local_storage input.\n"
665 "\t\tEntry in PLS block is ordered where first --pls-in marks the first entry. Can be called multiple times.\n"
666 "\t\tFormats allowed: r11f_g11f_b10f, r32f, rg16f, rg16, rgb10_a2, rgba8, rgba8i, rgba8ui, rg16i, rgb10_a2ui, rg16ui, r32ui.\n"
667 "\t\tRequires ESSL.\n"
668 "\t[--pls-out format output-name]:\n\t\tRemaps a color output with name into a GL_EXT_pixel_local_storage output.\n"
669 "\t\tEntry in PLS block is ordered where first --pls-output marks the first entry. Can be called multiple times.\n"
670 "\t\tFormats allowed: r11f_g11f_b10f, r32f, rg16f, rg16, rgb10_a2, rgba8, rgba8i, rgba8ui, rg16i, rgb10_a2ui, rg16ui, r32ui.\n"
671 "\t\tRequires ESSL.\n"
672 "\t[--remap source_name target_name components]:\n\t\tRemaps a variable to a different name with N components.\n"
673 "\t\tMain use case is to remap a subpass input to gl_LastFragDepthARM.\n"
674 "\t\tE.g.:\n"
675 "\t\tuniform subpassInput uDepth;\n"
676 "\t\t--remap uDepth gl_LastFragDepthARM 1 --extension GL_ARM_shader_framebuffer_fetch_depth_stencil\n"
677 "\t[--no-420pack-extension]:\n\t\tDo not make use of GL_ARB_shading_language_420pack in older GL targets to support layout(binding).\n"
678 "\t[--remap-variable-type <variable_name> <new_variable_type>]:\n\t\tRemaps a variable type based on name.\n"
679 "\t\tPrimary use case is supporting external samplers in ESSL for video rendering on Android where you could remap a texture to a YUV one.\n"
680 "\t[--glsl-force-flattened-io-blocks]:\n\t\tAlways flatten I/O blocks and structs.\n"
681 );
682 // clang-format on
683 }
684
print_help_hlsl()685 static void print_help_hlsl()
686 {
687 // clang-format off
688 fprintf(stderr, "\nHLSL options:\n"
689 "\t[--shader-model]:\n\t\tEnables a specific shader model, e.g. --shader-model 50 for SM 5.0.\n"
690 "\t[--hlsl-enable-compat]:\n\t\tAllow point size and point coord to be used, even if they won't work as expected.\n"
691 "\t\tPointSize is ignored, and PointCoord returns (0.5, 0.5).\n"
692 "\t[--hlsl-support-nonzero-basevertex-baseinstance]:\n\t\tSupport base vertex and base instance by emitting a special cbuffer declared as:\n"
693 "\t\tcbuffer SPIRV_Cross_VertexInfo { int SPIRV_Cross_BaseVertex; int SPIRV_Cross_BaseInstance; };\n"
694 "\t[--hlsl-auto-binding (push, cbv, srv, uav, sampler, all)]\n"
695 "\t\tDo not emit any : register(#) bindings for specific resource types, and rely on HLSL compiler to assign something.\n"
696 "\t[--hlsl-force-storage-buffer-as-uav]:\n\t\tAlways emit SSBOs as UAVs, even when marked as read-only.\n"
697 "\t\tNormally, SSBOs marked with NonWritable will be emitted as SRVs.\n"
698 "\t[--hlsl-nonwritable-uav-texture-as-srv]:\n\t\tEmit NonWritable storage images as SRV textures instead of UAV.\n"
699 "\t\tUsing this option messes with the type system. SPIRV-Cross cannot guarantee that this will work.\n"
700 "\t\tOne major problem area with this feature is function arguments, where we won't know if we're seeing a UAV or SRV.\n"
701 "\t\tShader must ensure that read/write state is consistent at all call sites.\n"
702 "\t[--set-hlsl-vertex-input-semantic <location> <semantic>]:\n\t\tEmits a specific vertex input semantic for a given location.\n"
703 "\t\tOtherwise, TEXCOORD# is used as semantics, where # is location.\n"
704 "\t[--hlsl-enable-16bit-types]:\n\t\tEnables native use of half/int16_t/uint16_t and ByteAddressBuffer interaction with these types. Requires SM 6.2.\n"
705 );
706 // clang-format on
707 }
708
print_help_msl()709 static void print_help_msl()
710 {
711 // clang-format off
712 fprintf(stderr, "\nMSL options:\n"
713 "\t[--msl-version <MMmmpp>]:\n\t\tUses a specific MSL version, e.g. --msl-version 20100 for MSL 2.1.\n"
714 "\t[--msl-capture-output]:\n\t\tWrites geometry varyings to a buffer instead of as stage-outputs.\n"
715 "\t[--msl-swizzle-texture-samples]:\n\t\tWorks around lack of support for VkImageView component swizzles.\n"
716 "\t\tThis has a massive impact on performance and bloat. Do not use this unless you are absolutely forced to.\n"
717 "\t\tTo use this feature, the API side must pass down swizzle buffers.\n"
718 "\t\tShould only be used by translation layers as a last resort.\n"
719 "\t\tRecent Metal versions do not require this workaround.\n"
720 "\t[--msl-ios]:\n\t\tTarget iOS Metal instead of macOS Metal.\n"
721 "\t[--msl-pad-fragment-output]:\n\t\tAlways emit color outputs as 4-component variables.\n"
722 "\t\tIn Metal, the fragment shader must emit at least as many components as the render target format.\n"
723 "\t[--msl-domain-lower-left]:\n\t\tUse a lower-left tessellation domain.\n"
724 "\t[--msl-argument-buffers]:\n\t\tEmit Indirect Argument buffers instead of plain bindings.\n"
725 "\t\tRequires MSL 2.0 to be enabled.\n"
726 "\t[--msl-texture-buffer-native]:\n\t\tEnable native support for texel buffers. Otherwise, it is emulated as a normal texture.\n"
727 "\t[--msl-framebuffer-fetch]:\n\t\tImplement subpass inputs with frame buffer fetch.\n"
728 "\t\tEmits [[color(N)]] inputs in fragment stage.\n"
729 "\t\tRequires iOS Metal.\n"
730 "\t[--msl-emulate-cube-array]:\n\t\tEmulate cube arrays with 2D array and manual math.\n"
731 "\t[--msl-discrete-descriptor-set <index>]:\n\t\tWhen using argument buffers, forces a specific descriptor set to be implemented without argument buffers.\n"
732 "\t\tUseful for implementing push descriptors in emulation layers.\n"
733 "\t\tCan be used multiple times for each descriptor set in question.\n"
734 "\t[--msl-device-argument-buffer <descriptor set index>]:\n\t\tUse device address space to hold indirect argument buffers instead of constant.\n"
735 "\t\tComes up when trying to support argument buffers which are larger than 64 KiB.\n"
736 "\t[--msl-multiview]:\n\t\tEnable SPV_KHR_multiview emulation.\n"
737 "\t[--msl-multiview-no-layered-rendering]:\n\t\tDon't set [[render_target_array_index]] in multiview shaders.\n"
738 "\t\tUseful for devices which don't support layered rendering. Only effective when --msl-multiview is enabled.\n"
739 "\t[--msl-view-index-from-device-index]:\n\t\tTreat the view index as the device index instead.\n"
740 "\t\tFor multi-GPU rendering.\n"
741 "\t[--msl-dispatch-base]:\n\t\tAdd support for vkCmdDispatchBase() or similar APIs.\n"
742 "\t\tOffsets the workgroup ID based on a buffer.\n"
743 "\t[--msl-dynamic-buffer <set index> <binding>]:\n\t\tMarks a buffer as having dynamic offset.\n"
744 "\t\tThe offset is applied in the shader with pointer arithmetic.\n"
745 "\t\tUseful for argument buffers where it is non-trivial to apply dynamic offset otherwise.\n"
746 "\t[--msl-inline-uniform-block <set index> <binding>]:\n\t\tIn argument buffers, mark an UBO as being an inline uniform block which is embedded into the argument buffer itself.\n"
747 "\t[--msl-decoration-binding]:\n\t\tUse SPIR-V bindings directly as MSL bindings.\n"
748 "\t\tThis does not work in the general case as there is no descriptor set support, and combined image samplers are split up.\n"
749 "\t\tHowever, if the shader author knows of binding limitations, this option will avoid the need for reflection on Metal side.\n"
750 "\t[--msl-force-active-argument-buffer-resources]:\n\t\tAlways emit resources which are part of argument buffers.\n"
751 "\t\tThis makes sure that similar shaders with same resource declarations can share the argument buffer as declaring an argument buffer implies an ABI.\n"
752 "\t[--msl-force-native-arrays]:\n\t\tRather than implementing array types as a templated value type ala std::array<T>, use plain, native arrays.\n"
753 "\t\tThis will lead to worse code-gen, but can work around driver bugs on certain driver revisions of certain Intel-based Macbooks where template arrays break.\n"
754 "\t[--msl-disable-frag-depth-builtin]:\n\t\tDisables FragDepth output. Useful if pipeline does not enable depth, as pipeline creation might otherwise fail.\n"
755 "\t[--msl-disable-frag-stencil-ref-builtin]:\n\t\tDisable FragStencilRef output. Useful if pipeline does not enable stencil output, as pipeline creation might otherwise fail.\n"
756 "\t[--msl-enable-frag-output-mask <mask>]:\n\t\tOnly selectively enable fragment outputs. Useful if pipeline does not enable fragment output for certain locations, as pipeline creation might otherwise fail.\n"
757 "\t[--msl-no-clip-distance-user-varying]:\n\t\tDo not emit user varyings to emulate gl_ClipDistance in fragment shaders.\n"
758 "\t[--msl-shader-input <index> <format> <size>]:\n\t\tSpecify the format of the shader input at <index>.\n"
759 "\t\t<format> can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, "
760 "or other-typed variable. <size> is the vector length of the variable, which must be greater than or equal to that declared in the shader.\n"
761 "\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n"
762 "\t[--msl-multi-patch-workgroup]:\n\t\tUse the new style of tessellation control processing, where multiple patches are processed per workgroup.\n"
763 "\t\tThis should increase throughput by ensuring all the GPU's SIMD lanes are occupied, but it is not compatible with the old style.\n"
764 "\t\tIn addition, this style also passes input variables in buffers directly instead of using vertex attribute processing.\n"
765 "\t\tIn a future version of SPIRV-Cross, this will become the default.\n"
766 "\t[--msl-vertex-for-tessellation]:\n\t\tWhen handling a vertex shader, marks it as one that will be used with a new-style tessellation control shader.\n"
767 "\t\tThe vertex shader is output to MSL as a compute kernel which outputs vertices to the buffer in the order they are received, rather than in index order as with --msl-capture-output normally.\n"
768 "\t[--msl-additional-fixed-sample-mask <mask>]:\n"
769 "\t\tSet an additional fixed sample mask. If the shader outputs a sample mask, then the final sample mask will be a bitwise AND of the two.\n"
770 "\t[--msl-arrayed-subpass-input]:\n\t\tAssume that images of dimension SubpassData have multiple layers. Layered input attachments are accessed relative to BuiltInLayer.\n"
771 "\t\tThis option has no effect if multiview is also enabled.\n");
772 // clang-format on
773 }
774
print_help_common()775 static void print_help_common()
776 {
777 // clang-format off
778 fprintf(stderr, "\nCommon options:\n"
779 "\t[--entry name]:\n\t\tUse a specific entry point. By default, the first entry point in the module is used.\n"
780 "\t[--stage <stage (vert, frag, geom, tesc, tese comp)>]:\n\t\tForces use of a certain shader stage.\n"
781 "\t\tCan disambiguate the entry point if more than one entry point exists with same name, but different stage.\n"
782 "\t[--emit-line-directives]:\n\t\tIf SPIR-V has OpLine directives, aim to emit those accurately in output code as well.\n"
783 "\t[--rename-entry-point <old> <new> <stage>]:\n\t\tRenames an entry point from what is declared in SPIR-V to code output.\n"
784 "\t\tMostly relevant for HLSL or MSL.\n"
785 "\t[--rename-interface-variable <in|out> <location> <new_variable_name>]:\n\t\tRename an interface variable based on location decoration.\n"
786 "\t[--force-zero-initialized-variables]:\n\t\tForces temporary variables to be initialized to zero.\n"
787 "\t\tCan be useful in environments where compilers do not allow potentially uninitialized variables.\n"
788 "\t\tThis usually comes up with Phi temporaries.\n"
789 "\t[--fixup-clipspace]:\n\t\tFixup Z clip-space at the end of a vertex shader. The behavior is backend-dependent.\n"
790 "\t\tGLSL: Rewrites [0, w] Z range (D3D/Metal/Vulkan) to GL-style [-w, w].\n"
791 "\t\tHLSL/MSL: Rewrites [-w, w] Z range (GL) to D3D/Metal/Vulkan-style [0, w].\n"
792 "\t[--flip-vert-y]:\n\t\tInverts gl_Position.y (or equivalent) at the end of a vertex shader. This is equivalent to using negative viewport height.\n"
793 );
794 // clang-format on
795 }
796
print_help_obscure()797 static void print_help_obscure()
798 {
799 // clang-format off
800 fprintf(stderr, "\nObscure options:\n"
801 "\tThese options are not meant to be used on a regular basis. They have some occasional uses in the test suite.\n"
802
803 "\t[--force-temporary]:\n\t\tAggressively emit temporary expressions instead of forwarding expressions. Very rarely used and under-tested.\n"
804 "\t[--revision]:\n\t\tPrints build timestamp and Git commit information (updated when cmake is configured).\n"
805 "\t[--iterations iter]:\n\t\tRecompiles the same shader over and over, benchmarking related.\n"
806 "\t[--disable-storage-image-qualifier-deduction]:\n\t\tIf storage images are received without any nonwritable or nonreadable information,\n"""
807 "\t\tdo not attempt to analyze usage, and always emit read/write state.\n"
808 "\t[--flatten-multidimensional-arrays]:\n\t\tDo not support multi-dimensional arrays and flatten them to one dimension.\n"
809 "\t[--cpp-interface-name <name>]:\n\t\tEmit a specific class name in C++ codegen.\n"
810 );
811 // clang-format on
812 }
813
print_help()814 static void print_help()
815 {
816 print_version();
817
818 // clang-format off
819 fprintf(stderr, "Usage: spirv-cross <...>\n"
820 "\nBasic:\n"
821 "\t[SPIR-V file]\n"
822 "\t[--output <output path>]: If not provided, prints output to stdout.\n"
823 "\t[--dump-resources]:\n\t\tPrints a basic reflection of the SPIR-V module along with other output.\n"
824 "\t[--help]:\n\t\tPrints this help message.\n"
825 );
826 // clang-format on
827
828 print_help_backend();
829 print_help_common();
830 print_help_glsl();
831 print_help_msl();
832 print_help_hlsl();
833 print_help_obscure();
834 }
835
remap_generic(Compiler & compiler,const SmallVector<Resource> & resources,const Remap & remap)836 static bool remap_generic(Compiler &compiler, const SmallVector<Resource> &resources, const Remap &remap)
837 {
838 auto itr =
839 find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; });
840
841 if (itr != end(resources))
842 {
843 compiler.set_remapped_variable_state(itr->id, true);
844 compiler.set_name(itr->id, remap.dst_name);
845 compiler.set_subpass_input_remapped_components(itr->id, remap.components);
846 return true;
847 }
848 else
849 return false;
850 }
851
remap_pls(const SmallVector<PLSArg> & pls_variables,const SmallVector<Resource> & resources,const SmallVector<Resource> * secondary_resources)852 static vector<PlsRemap> remap_pls(const SmallVector<PLSArg> &pls_variables, const SmallVector<Resource> &resources,
853 const SmallVector<Resource> *secondary_resources)
854 {
855 vector<PlsRemap> ret;
856
857 for (auto &pls : pls_variables)
858 {
859 bool found = false;
860 for (auto &res : resources)
861 {
862 if (res.name == pls.name)
863 {
864 ret.push_back({ res.id, pls.format });
865 found = true;
866 break;
867 }
868 }
869
870 if (!found && secondary_resources)
871 {
872 for (auto &res : *secondary_resources)
873 {
874 if (res.name == pls.name)
875 {
876 ret.push_back({ res.id, pls.format });
877 found = true;
878 break;
879 }
880 }
881 }
882
883 if (!found)
884 fprintf(stderr, "Did not find stage input/output/target with name \"%s\".\n", pls.name.c_str());
885 }
886
887 return ret;
888 }
889
pls_format(const char * str)890 static PlsFormat pls_format(const char *str)
891 {
892 if (!strcmp(str, "r11f_g11f_b10f"))
893 return PlsR11FG11FB10F;
894 else if (!strcmp(str, "r32f"))
895 return PlsR32F;
896 else if (!strcmp(str, "rg16f"))
897 return PlsRG16F;
898 else if (!strcmp(str, "rg16"))
899 return PlsRG16;
900 else if (!strcmp(str, "rgb10_a2"))
901 return PlsRGB10A2;
902 else if (!strcmp(str, "rgba8"))
903 return PlsRGBA8;
904 else if (!strcmp(str, "rgba8i"))
905 return PlsRGBA8I;
906 else if (!strcmp(str, "rgba8ui"))
907 return PlsRGBA8UI;
908 else if (!strcmp(str, "rg16i"))
909 return PlsRG16I;
910 else if (!strcmp(str, "rgb10_a2ui"))
911 return PlsRGB10A2UI;
912 else if (!strcmp(str, "rg16ui"))
913 return PlsRG16UI;
914 else if (!strcmp(str, "r32ui"))
915 return PlsR32UI;
916 else
917 return PlsNone;
918 }
919
stage_to_execution_model(const std::string & stage)920 static ExecutionModel stage_to_execution_model(const std::string &stage)
921 {
922 if (stage == "vert")
923 return ExecutionModelVertex;
924 else if (stage == "frag")
925 return ExecutionModelFragment;
926 else if (stage == "comp")
927 return ExecutionModelGLCompute;
928 else if (stage == "tesc")
929 return ExecutionModelTessellationControl;
930 else if (stage == "tese")
931 return ExecutionModelTessellationEvaluation;
932 else if (stage == "geom")
933 return ExecutionModelGeometry;
934 else
935 SPIRV_CROSS_THROW("Invalid stage.");
936 }
937
hlsl_resource_type_to_flag(const std::string & arg)938 static HLSLBindingFlags hlsl_resource_type_to_flag(const std::string &arg)
939 {
940 if (arg == "push")
941 return HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT;
942 else if (arg == "cbv")
943 return HLSL_BINDING_AUTO_CBV_BIT;
944 else if (arg == "srv")
945 return HLSL_BINDING_AUTO_SRV_BIT;
946 else if (arg == "uav")
947 return HLSL_BINDING_AUTO_UAV_BIT;
948 else if (arg == "sampler")
949 return HLSL_BINDING_AUTO_SAMPLER_BIT;
950 else if (arg == "all")
951 return HLSL_BINDING_AUTO_ALL;
952 else
953 {
954 fprintf(stderr, "Invalid resource type for --hlsl-auto-binding: %s\n", arg.c_str());
955 return 0;
956 }
957 }
958
compile_iteration(const CLIArguments & args,std::vector<uint32_t> spirv_file)959 static string compile_iteration(const CLIArguments &args, std::vector<uint32_t> spirv_file)
960 {
961 Parser spirv_parser(move(spirv_file));
962 spirv_parser.parse();
963
964 unique_ptr<CompilerGLSL> compiler;
965 bool combined_image_samplers = false;
966 bool build_dummy_sampler = false;
967
968 if (args.cpp)
969 {
970 compiler.reset(new CompilerCPP(move(spirv_parser.get_parsed_ir())));
971 if (args.cpp_interface_name)
972 static_cast<CompilerCPP *>(compiler.get())->set_interface_name(args.cpp_interface_name);
973 }
974 else if (args.msl)
975 {
976 compiler.reset(new CompilerMSL(move(spirv_parser.get_parsed_ir())));
977
978 auto *msl_comp = static_cast<CompilerMSL *>(compiler.get());
979 auto msl_opts = msl_comp->get_msl_options();
980 if (args.set_msl_version)
981 msl_opts.msl_version = args.msl_version;
982 msl_opts.capture_output_to_buffer = args.msl_capture_output_to_buffer;
983 msl_opts.swizzle_texture_samples = args.msl_swizzle_texture_samples;
984 msl_opts.invariant_float_math = args.msl_invariant_float_math;
985 if (args.msl_ios)
986 {
987 msl_opts.platform = CompilerMSL::Options::iOS;
988 msl_opts.ios_use_framebuffer_fetch_subpasses = args.msl_framebuffer_fetch;
989 msl_opts.emulate_cube_array = args.msl_emulate_cube_array;
990 }
991 msl_opts.pad_fragment_output_components = args.msl_pad_fragment_output;
992 msl_opts.tess_domain_origin_lower_left = args.msl_domain_lower_left;
993 msl_opts.argument_buffers = args.msl_argument_buffers;
994 msl_opts.texture_buffer_native = args.msl_texture_buffer_native;
995 msl_opts.multiview = args.msl_multiview;
996 msl_opts.multiview_layered_rendering = args.msl_multiview_layered_rendering;
997 msl_opts.view_index_from_device_index = args.msl_view_index_from_device_index;
998 msl_opts.dispatch_base = args.msl_dispatch_base;
999 msl_opts.enable_decoration_binding = args.msl_decoration_binding;
1000 msl_opts.force_active_argument_buffer_resources = args.msl_force_active_argument_buffer_resources;
1001 msl_opts.force_native_arrays = args.msl_force_native_arrays;
1002 msl_opts.enable_frag_depth_builtin = args.msl_enable_frag_depth_builtin;
1003 msl_opts.enable_frag_stencil_ref_builtin = args.msl_enable_frag_stencil_ref_builtin;
1004 msl_opts.enable_frag_output_mask = args.msl_enable_frag_output_mask;
1005 msl_opts.enable_clip_distance_user_varying = args.msl_enable_clip_distance_user_varying;
1006 msl_opts.multi_patch_workgroup = args.msl_multi_patch_workgroup;
1007 msl_opts.vertex_for_tessellation = args.msl_vertex_for_tessellation;
1008 msl_opts.additional_fixed_sample_mask = args.msl_additional_fixed_sample_mask;
1009 msl_opts.arrayed_subpass_input = args.msl_arrayed_subpass_input;
1010 msl_comp->set_msl_options(msl_opts);
1011 for (auto &v : args.msl_discrete_descriptor_sets)
1012 msl_comp->add_discrete_descriptor_set(v);
1013 for (auto &v : args.msl_device_argument_buffers)
1014 msl_comp->set_argument_buffer_device_address_space(v, true);
1015 uint32_t i = 0;
1016 for (auto &v : args.msl_dynamic_buffers)
1017 msl_comp->add_dynamic_buffer(v.first, v.second, i++);
1018 for (auto &v : args.msl_inline_uniform_blocks)
1019 msl_comp->add_inline_uniform_block(v.first, v.second);
1020 for (auto &v : args.msl_shader_inputs)
1021 msl_comp->add_msl_shader_input(v);
1022 }
1023 else if (args.hlsl)
1024 compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir())));
1025 else
1026 {
1027 combined_image_samplers = !args.vulkan_semantics;
1028 if (!args.vulkan_semantics || args.vulkan_glsl_disable_ext_samplerless_texture_functions)
1029 build_dummy_sampler = true;
1030 compiler.reset(new CompilerGLSL(move(spirv_parser.get_parsed_ir())));
1031 }
1032
1033 if (!args.variable_type_remaps.empty())
1034 {
1035 auto remap_cb = [&](const SPIRType &, const string &name, string &out) -> void {
1036 for (const VariableTypeRemap &remap : args.variable_type_remaps)
1037 if (name == remap.variable_name)
1038 out = remap.new_variable_type;
1039 };
1040
1041 compiler->set_variable_type_remap_callback(move(remap_cb));
1042 }
1043
1044 for (auto &rename : args.entry_point_rename)
1045 compiler->rename_entry_point(rename.old_name, rename.new_name, rename.execution_model);
1046
1047 auto entry_points = compiler->get_entry_points_and_stages();
1048 auto entry_point = args.entry;
1049 ExecutionModel model = ExecutionModelMax;
1050
1051 if (!args.entry_stage.empty())
1052 {
1053 model = stage_to_execution_model(args.entry_stage);
1054 if (entry_point.empty())
1055 {
1056 // Just use the first entry point with this stage.
1057 for (auto &e : entry_points)
1058 {
1059 if (e.execution_model == model)
1060 {
1061 entry_point = e.name;
1062 break;
1063 }
1064 }
1065
1066 if (entry_point.empty())
1067 {
1068 fprintf(stderr, "Could not find an entry point with stage: %s\n", args.entry_stage.c_str());
1069 exit(EXIT_FAILURE);
1070 }
1071 }
1072 else
1073 {
1074 // Make sure both stage and name exists.
1075 bool exists = false;
1076 for (auto &e : entry_points)
1077 {
1078 if (e.execution_model == model && e.name == entry_point)
1079 {
1080 exists = true;
1081 break;
1082 }
1083 }
1084
1085 if (!exists)
1086 {
1087 fprintf(stderr, "Could not find an entry point %s with stage: %s\n", entry_point.c_str(),
1088 args.entry_stage.c_str());
1089 exit(EXIT_FAILURE);
1090 }
1091 }
1092 }
1093 else if (!entry_point.empty())
1094 {
1095 // Make sure there is just one entry point with this name, or the stage
1096 // is ambiguous.
1097 uint32_t stage_count = 0;
1098 for (auto &e : entry_points)
1099 {
1100 if (e.name == entry_point)
1101 {
1102 stage_count++;
1103 model = e.execution_model;
1104 }
1105 }
1106
1107 if (stage_count == 0)
1108 {
1109 fprintf(stderr, "There is no entry point with name: %s\n", entry_point.c_str());
1110 exit(EXIT_FAILURE);
1111 }
1112 else if (stage_count > 1)
1113 {
1114 fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", entry_point.c_str());
1115 exit(EXIT_FAILURE);
1116 }
1117 }
1118
1119 if (!entry_point.empty())
1120 compiler->set_entry_point(entry_point, model);
1121
1122 if (!args.set_version && !compiler->get_common_options().version)
1123 {
1124 fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n");
1125 print_help();
1126 exit(EXIT_FAILURE);
1127 }
1128
1129 CompilerGLSL::Options opts = compiler->get_common_options();
1130 if (args.set_version)
1131 opts.version = args.version;
1132 if (args.set_es)
1133 opts.es = args.es;
1134 opts.force_temporary = args.force_temporary;
1135 opts.separate_shader_objects = args.sso;
1136 opts.flatten_multidimensional_arrays = args.flatten_multidimensional_arrays;
1137 opts.enable_420pack_extension = args.use_420pack_extension;
1138 opts.vulkan_semantics = args.vulkan_semantics;
1139 opts.vertex.fixup_clipspace = args.fixup;
1140 opts.vertex.flip_vert_y = args.yflip;
1141 opts.vertex.support_nonzero_base_instance = args.support_nonzero_baseinstance;
1142 opts.emit_push_constant_as_uniform_buffer = args.glsl_emit_push_constant_as_ubo;
1143 opts.emit_uniform_buffer_as_plain_uniforms = args.glsl_emit_ubo_as_plain_uniforms;
1144 opts.force_flattened_io_blocks = args.glsl_force_flattened_io_blocks;
1145 opts.emit_line_directives = args.emit_line_directives;
1146 opts.enable_storage_image_qualifier_deduction = args.enable_storage_image_qualifier_deduction;
1147 opts.force_zero_initialized_variables = args.force_zero_initialized_variables;
1148 compiler->set_common_options(opts);
1149
1150 for (auto &fetch : args.glsl_ext_framebuffer_fetch)
1151 compiler->remap_ext_framebuffer_fetch(fetch.first, fetch.second);
1152
1153 // Set HLSL specific options.
1154 if (args.hlsl)
1155 {
1156 auto *hlsl = static_cast<CompilerHLSL *>(compiler.get());
1157 auto hlsl_opts = hlsl->get_hlsl_options();
1158 if (args.set_shader_model)
1159 {
1160 if (args.shader_model < 30)
1161 {
1162 fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n");
1163 exit(EXIT_FAILURE);
1164 }
1165
1166 hlsl_opts.shader_model = args.shader_model;
1167 }
1168
1169 if (args.hlsl_compat)
1170 {
1171 // Enable all compat options.
1172 hlsl_opts.point_size_compat = true;
1173 hlsl_opts.point_coord_compat = true;
1174 }
1175
1176 if (hlsl_opts.shader_model <= 30)
1177 {
1178 combined_image_samplers = true;
1179 build_dummy_sampler = true;
1180 }
1181
1182 hlsl_opts.support_nonzero_base_vertex_base_instance = args.hlsl_support_nonzero_base;
1183 hlsl_opts.force_storage_buffer_as_uav = args.hlsl_force_storage_buffer_as_uav;
1184 hlsl_opts.nonwritable_uav_texture_as_srv = args.hlsl_nonwritable_uav_texture_as_srv;
1185 hlsl_opts.enable_16bit_types = args.hlsl_enable_16bit_types;
1186 hlsl->set_hlsl_options(hlsl_opts);
1187 hlsl->set_resource_binding_flags(args.hlsl_binding_flags);
1188 }
1189
1190 if (build_dummy_sampler)
1191 {
1192 uint32_t sampler = compiler->build_dummy_sampler_for_combined_images();
1193 if (sampler != 0)
1194 {
1195 // Set some defaults to make validation happy.
1196 compiler->set_decoration(sampler, DecorationDescriptorSet, 0);
1197 compiler->set_decoration(sampler, DecorationBinding, 0);
1198 }
1199 }
1200
1201 ShaderResources res;
1202 if (args.remove_unused)
1203 {
1204 auto active = compiler->get_active_interface_variables();
1205 res = compiler->get_shader_resources(active);
1206 compiler->set_enabled_interface_variables(move(active));
1207 }
1208 else
1209 res = compiler->get_shader_resources();
1210
1211 if (args.flatten_ubo)
1212 {
1213 for (auto &ubo : res.uniform_buffers)
1214 compiler->flatten_buffer_block(ubo.id);
1215 for (auto &ubo : res.push_constant_buffers)
1216 compiler->flatten_buffer_block(ubo.id);
1217 }
1218
1219 auto pls_inputs = remap_pls(args.pls_in, res.stage_inputs, &res.subpass_inputs);
1220 auto pls_outputs = remap_pls(args.pls_out, res.stage_outputs, nullptr);
1221 compiler->remap_pixel_local_storage(move(pls_inputs), move(pls_outputs));
1222
1223 for (auto &ext : args.extensions)
1224 compiler->require_extension(ext);
1225
1226 for (auto &remap : args.remaps)
1227 {
1228 if (remap_generic(*compiler, res.stage_inputs, remap))
1229 continue;
1230 if (remap_generic(*compiler, res.stage_outputs, remap))
1231 continue;
1232 if (remap_generic(*compiler, res.subpass_inputs, remap))
1233 continue;
1234 }
1235
1236 for (auto &rename : args.interface_variable_renames)
1237 {
1238 if (rename.storageClass == StorageClassInput)
1239 spirv_cross_util::rename_interface_variable(*compiler, res.stage_inputs, rename.location,
1240 rename.variable_name);
1241 else if (rename.storageClass == StorageClassOutput)
1242 spirv_cross_util::rename_interface_variable(*compiler, res.stage_outputs, rename.location,
1243 rename.variable_name);
1244 else
1245 {
1246 fprintf(stderr, "error at --rename-interface-variable <in|out> ...\n");
1247 exit(EXIT_FAILURE);
1248 }
1249 }
1250
1251 if (combined_image_samplers)
1252 {
1253 compiler->build_combined_image_samplers();
1254 if (args.combined_samplers_inherit_bindings)
1255 spirv_cross_util::inherit_combined_sampler_bindings(*compiler);
1256
1257 // Give the remapped combined samplers new names.
1258 for (auto &remap : compiler->get_combined_image_samplers())
1259 {
1260 compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id),
1261 compiler->get_name(remap.sampler_id)));
1262 }
1263 }
1264
1265 if (args.hlsl)
1266 {
1267 auto *hlsl_compiler = static_cast<CompilerHLSL *>(compiler.get());
1268 uint32_t new_builtin = hlsl_compiler->remap_num_workgroups_builtin();
1269 if (new_builtin)
1270 {
1271 hlsl_compiler->set_decoration(new_builtin, DecorationDescriptorSet, 0);
1272 hlsl_compiler->set_decoration(new_builtin, DecorationBinding, 0);
1273 }
1274 }
1275
1276 if (args.hlsl)
1277 {
1278 for (auto &remap : args.hlsl_attr_remap)
1279 static_cast<CompilerHLSL *>(compiler.get())->add_vertex_attribute_remap(remap);
1280 }
1281
1282 auto ret = compiler->compile();
1283
1284 if (args.dump_resources)
1285 {
1286 print_resources(*compiler, res);
1287 print_push_constant_resources(*compiler, res.push_constant_buffers);
1288 print_spec_constants(*compiler);
1289 print_capabilities_and_extensions(*compiler);
1290 }
1291
1292 return ret;
1293 }
1294
main_inner(int argc,char * argv[])1295 static int main_inner(int argc, char *argv[])
1296 {
1297 CLIArguments args;
1298 CLICallbacks cbs;
1299
1300 cbs.add("--help", [](CLIParser &parser) {
1301 print_help();
1302 parser.end();
1303 });
1304 cbs.add("--revision", [](CLIParser &parser) {
1305 print_version();
1306 parser.end();
1307 });
1308 cbs.add("--output", [&args](CLIParser &parser) { args.output = parser.next_string(); });
1309 cbs.add("--es", [&args](CLIParser &) {
1310 args.es = true;
1311 args.set_es = true;
1312 });
1313 cbs.add("--no-es", [&args](CLIParser &) {
1314 args.es = false;
1315 args.set_es = true;
1316 });
1317 cbs.add("--version", [&args](CLIParser &parser) {
1318 args.version = parser.next_uint();
1319 args.set_version = true;
1320 });
1321 cbs.add("--dump-resources", [&args](CLIParser &) { args.dump_resources = true; });
1322 cbs.add("--force-temporary", [&args](CLIParser &) { args.force_temporary = true; });
1323 cbs.add("--flatten-ubo", [&args](CLIParser &) { args.flatten_ubo = true; });
1324 cbs.add("--fixup-clipspace", [&args](CLIParser &) { args.fixup = true; });
1325 cbs.add("--flip-vert-y", [&args](CLIParser &) { args.yflip = true; });
1326 cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); });
1327 cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; });
1328 cbs.add("--reflect", [&args](CLIParser &parser) { args.reflect = parser.next_value_string("json"); });
1329 cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); });
1330 cbs.add("--metal", [&args](CLIParser &) { args.msl = true; }); // Legacy compatibility
1331 cbs.add("--glsl-emit-push-constant-as-ubo", [&args](CLIParser &) { args.glsl_emit_push_constant_as_ubo = true; });
1332 cbs.add("--glsl-emit-ubo-as-plain-uniforms", [&args](CLIParser &) { args.glsl_emit_ubo_as_plain_uniforms = true; });
1333 cbs.add("--glsl-force-flattened-io-blocks", [&args](CLIParser &) { args.glsl_force_flattened_io_blocks = true; });
1334 cbs.add("--glsl-remap-ext-framebuffer-fetch", [&args](CLIParser &parser) {
1335 uint32_t input_index = parser.next_uint();
1336 uint32_t color_attachment = parser.next_uint();
1337 args.glsl_ext_framebuffer_fetch.push_back({ input_index, color_attachment });
1338 });
1339 cbs.add("--vulkan-glsl-disable-ext-samplerless-texture-functions",
1340 [&args](CLIParser &) { args.vulkan_glsl_disable_ext_samplerless_texture_functions = true; });
1341 cbs.add("--disable-storage-image-qualifier-deduction",
1342 [&args](CLIParser &) { args.enable_storage_image_qualifier_deduction = false; });
1343 cbs.add("--force-zero-initialized-variables",
1344 [&args](CLIParser &) { args.force_zero_initialized_variables = true; });
1345 cbs.add("--msl", [&args](CLIParser &) { args.msl = true; });
1346 cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; });
1347 cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; });
1348 cbs.add("--hlsl-support-nonzero-basevertex-baseinstance",
1349 [&args](CLIParser &) { args.hlsl_support_nonzero_base = true; });
1350 cbs.add("--hlsl-auto-binding", [&args](CLIParser &parser) {
1351 args.hlsl_binding_flags |= hlsl_resource_type_to_flag(parser.next_string());
1352 });
1353 cbs.add("--hlsl-force-storage-buffer-as-uav",
1354 [&args](CLIParser &) { args.hlsl_force_storage_buffer_as_uav = true; });
1355 cbs.add("--hlsl-nonwritable-uav-texture-as-srv",
1356 [&args](CLIParser &) { args.hlsl_nonwritable_uav_texture_as_srv = true; });
1357 cbs.add("--hlsl-enable-16bit-types", [&args](CLIParser &) { args.hlsl_enable_16bit_types = true; });
1358 cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; });
1359 cbs.add("-V", [&args](CLIParser &) { args.vulkan_semantics = true; });
1360 cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; });
1361 cbs.add("--no-420pack-extension", [&args](CLIParser &) { args.use_420pack_extension = false; });
1362 cbs.add("--msl-capture-output", [&args](CLIParser &) { args.msl_capture_output_to_buffer = true; });
1363 cbs.add("--msl-swizzle-texture-samples", [&args](CLIParser &) { args.msl_swizzle_texture_samples = true; });
1364 cbs.add("--msl-ios", [&args](CLIParser &) { args.msl_ios = true; });
1365 cbs.add("--msl-pad-fragment-output", [&args](CLIParser &) { args.msl_pad_fragment_output = true; });
1366 cbs.add("--msl-domain-lower-left", [&args](CLIParser &) { args.msl_domain_lower_left = true; });
1367 cbs.add("--msl-argument-buffers", [&args](CLIParser &) { args.msl_argument_buffers = true; });
1368 cbs.add("--msl-discrete-descriptor-set",
1369 [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); });
1370 cbs.add("--msl-device-argument-buffer",
1371 [&args](CLIParser &parser) { args.msl_device_argument_buffers.push_back(parser.next_uint()); });
1372 cbs.add("--msl-texture-buffer-native", [&args](CLIParser &) { args.msl_texture_buffer_native = true; });
1373 cbs.add("--msl-framebuffer-fetch", [&args](CLIParser &) { args.msl_framebuffer_fetch = true; });
1374 cbs.add("--msl-invariant-float-math", [&args](CLIParser &) { args.msl_invariant_float_math = true; });
1375 cbs.add("--msl-emulate-cube-array", [&args](CLIParser &) { args.msl_emulate_cube_array = true; });
1376 cbs.add("--msl-multiview", [&args](CLIParser &) { args.msl_multiview = true; });
1377 cbs.add("--msl-multiview-no-layered-rendering",
1378 [&args](CLIParser &) { args.msl_multiview_layered_rendering = false; });
1379 cbs.add("--msl-view-index-from-device-index",
1380 [&args](CLIParser &) { args.msl_view_index_from_device_index = true; });
1381 cbs.add("--msl-dispatch-base", [&args](CLIParser &) { args.msl_dispatch_base = true; });
1382 cbs.add("--msl-dynamic-buffer", [&args](CLIParser &parser) {
1383 args.msl_argument_buffers = true;
1384 // Make sure next_uint() is called in-order.
1385 uint32_t desc_set = parser.next_uint();
1386 uint32_t binding = parser.next_uint();
1387 args.msl_dynamic_buffers.push_back(make_pair(desc_set, binding));
1388 });
1389 cbs.add("--msl-decoration-binding", [&args](CLIParser &) { args.msl_decoration_binding = true; });
1390 cbs.add("--msl-force-active-argument-buffer-resources",
1391 [&args](CLIParser &) { args.msl_force_active_argument_buffer_resources = true; });
1392 cbs.add("--msl-inline-uniform-block", [&args](CLIParser &parser) {
1393 args.msl_argument_buffers = true;
1394 // Make sure next_uint() is called in-order.
1395 uint32_t desc_set = parser.next_uint();
1396 uint32_t binding = parser.next_uint();
1397 args.msl_inline_uniform_blocks.push_back(make_pair(desc_set, binding));
1398 });
1399 cbs.add("--msl-force-native-arrays", [&args](CLIParser &) { args.msl_force_native_arrays = true; });
1400 cbs.add("--msl-disable-frag-depth-builtin", [&args](CLIParser &) { args.msl_enable_frag_depth_builtin = false; });
1401 cbs.add("--msl-disable-frag-stencil-ref-builtin",
1402 [&args](CLIParser &) { args.msl_enable_frag_stencil_ref_builtin = false; });
1403 cbs.add("--msl-enable-frag-output-mask",
1404 [&args](CLIParser &parser) { args.msl_enable_frag_output_mask = parser.next_hex_uint(); });
1405 cbs.add("--msl-no-clip-distance-user-varying",
1406 [&args](CLIParser &) { args.msl_enable_clip_distance_user_varying = false; });
1407 cbs.add("--msl-shader-input", [&args](CLIParser &parser) {
1408 MSLShaderInput input;
1409 // Make sure next_uint() is called in-order.
1410 input.location = parser.next_uint();
1411 const char *format = parser.next_value_string("other");
1412 if (strcmp(format, "any32") == 0)
1413 input.format = MSL_SHADER_INPUT_FORMAT_ANY32;
1414 else if (strcmp(format, "any16") == 0)
1415 input.format = MSL_SHADER_INPUT_FORMAT_ANY16;
1416 else if (strcmp(format, "u16") == 0)
1417 input.format = MSL_SHADER_INPUT_FORMAT_UINT16;
1418 else if (strcmp(format, "u8") == 0)
1419 input.format = MSL_SHADER_INPUT_FORMAT_UINT8;
1420 else
1421 input.format = MSL_SHADER_INPUT_FORMAT_OTHER;
1422 input.vecsize = parser.next_uint();
1423 args.msl_shader_inputs.push_back(input);
1424 });
1425 cbs.add("--msl-multi-patch-workgroup", [&args](CLIParser &) { args.msl_multi_patch_workgroup = true; });
1426 cbs.add("--msl-vertex-for-tessellation", [&args](CLIParser &) { args.msl_vertex_for_tessellation = true; });
1427 cbs.add("--msl-additional-fixed-sample-mask",
1428 [&args](CLIParser &parser) { args.msl_additional_fixed_sample_mask = parser.next_hex_uint(); });
1429 cbs.add("--msl-arrayed-subpass-input", [&args](CLIParser &) { args.msl_arrayed_subpass_input = true; });
1430 cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
1431 cbs.add("--rename-entry-point", [&args](CLIParser &parser) {
1432 auto old_name = parser.next_string();
1433 auto new_name = parser.next_string();
1434 auto model = stage_to_execution_model(parser.next_string());
1435 args.entry_point_rename.push_back({ old_name, new_name, move(model) });
1436 });
1437 cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); });
1438 cbs.add("--stage", [&args](CLIParser &parser) { args.entry_stage = parser.next_string(); });
1439 cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; });
1440 cbs.add("--set-hlsl-vertex-input-semantic", [&args](CLIParser &parser) {
1441 HLSLVertexAttributeRemap remap;
1442 remap.location = parser.next_uint();
1443 remap.semantic = parser.next_string();
1444 args.hlsl_attr_remap.push_back(move(remap));
1445 });
1446
1447 cbs.add("--remap", [&args](CLIParser &parser) {
1448 string src = parser.next_string();
1449 string dst = parser.next_string();
1450 uint32_t components = parser.next_uint();
1451 args.remaps.push_back({ move(src), move(dst), components });
1452 });
1453
1454 cbs.add("--remap-variable-type", [&args](CLIParser &parser) {
1455 string var_name = parser.next_string();
1456 string new_type = parser.next_string();
1457 args.variable_type_remaps.push_back({ move(var_name), move(new_type) });
1458 });
1459
1460 cbs.add("--rename-interface-variable", [&args](CLIParser &parser) {
1461 StorageClass cls = StorageClassMax;
1462 string clsStr = parser.next_string();
1463 if (clsStr == "in")
1464 cls = StorageClassInput;
1465 else if (clsStr == "out")
1466 cls = StorageClassOutput;
1467
1468 uint32_t loc = parser.next_uint();
1469 string var_name = parser.next_string();
1470 args.interface_variable_renames.push_back({ cls, loc, move(var_name) });
1471 });
1472
1473 cbs.add("--pls-in", [&args](CLIParser &parser) {
1474 auto fmt = pls_format(parser.next_string());
1475 auto name = parser.next_string();
1476 args.pls_in.push_back({ move(fmt), move(name) });
1477 });
1478 cbs.add("--pls-out", [&args](CLIParser &parser) {
1479 auto fmt = pls_format(parser.next_string());
1480 auto name = parser.next_string();
1481 args.pls_out.push_back({ move(fmt), move(name) });
1482 });
1483 cbs.add("--shader-model", [&args](CLIParser &parser) {
1484 args.shader_model = parser.next_uint();
1485 args.set_shader_model = true;
1486 });
1487 cbs.add("--msl-version", [&args](CLIParser &parser) {
1488 args.msl_version = parser.next_uint();
1489 args.set_msl_version = true;
1490 });
1491
1492 cbs.add("--remove-unused-variables", [&args](CLIParser &) { args.remove_unused = true; });
1493 cbs.add("--combined-samplers-inherit-bindings",
1494 [&args](CLIParser &) { args.combined_samplers_inherit_bindings = true; });
1495
1496 cbs.add("--no-support-nonzero-baseinstance", [&](CLIParser &) { args.support_nonzero_baseinstance = false; });
1497 cbs.add("--emit-line-directives", [&args](CLIParser &) { args.emit_line_directives = true; });
1498
1499 cbs.default_handler = [&args](const char *value) { args.input = value; };
1500 cbs.error_handler = [] { print_help(); };
1501
1502 CLIParser parser{ move(cbs), argc - 1, argv + 1 };
1503 if (!parser.parse())
1504 return EXIT_FAILURE;
1505 else if (parser.ended_state)
1506 return EXIT_SUCCESS;
1507
1508 if (!args.input)
1509 {
1510 fprintf(stderr, "Didn't specify input file.\n");
1511 print_help();
1512 return EXIT_FAILURE;
1513 }
1514
1515 auto spirv_file = read_spirv_file(args.input);
1516 if (spirv_file.empty())
1517 return EXIT_FAILURE;
1518
1519 // Special case reflection because it has little to do with the path followed by code-outputting compilers
1520 if (!args.reflect.empty())
1521 {
1522 Parser spirv_parser(move(spirv_file));
1523 spirv_parser.parse();
1524
1525 CompilerReflection compiler(move(spirv_parser.get_parsed_ir()));
1526 compiler.set_format(args.reflect);
1527 auto json = compiler.compile();
1528 if (args.output)
1529 write_string_to_file(args.output, json.c_str());
1530 else
1531 printf("%s", json.c_str());
1532 return EXIT_SUCCESS;
1533 }
1534
1535 string compiled_output;
1536
1537 if (args.iterations == 1)
1538 compiled_output = compile_iteration(args, move(spirv_file));
1539 else
1540 {
1541 for (unsigned i = 0; i < args.iterations; i++)
1542 compiled_output = compile_iteration(args, spirv_file);
1543 }
1544
1545 if (args.output)
1546 write_string_to_file(args.output, compiled_output.c_str());
1547 else
1548 printf("%s", compiled_output.c_str());
1549
1550 return EXIT_SUCCESS;
1551 }
1552
main(int argc,char * argv[])1553 int main(int argc, char *argv[])
1554 {
1555 #ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
1556 return main_inner(argc, argv);
1557 #else
1558 // Make sure we catch the exception or it just disappears into the aether on Windows.
1559 try
1560 {
1561 return main_inner(argc, argv);
1562 }
1563 catch (const std::exception &e)
1564 {
1565 fprintf(stderr, "SPIRV-Cross threw an exception: %s\n", e.what());
1566 return EXIT_FAILURE;
1567 }
1568 #endif
1569 }
1570