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