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