GLSL/HLSL: Add legacy handling for int vertex attributes
[KhronosGroup/SPIRV-Cross.git] / main.cpp
blob3605a54a209c2a723038e4d315d94028e590084e
1 /*
2 * Copyright 2015-2021 Arm Limited
3 * SPDX-License-Identifier: Apache-2.0 OR MIT
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>.
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>
41 #ifdef _WIN32
42 #include <io.h>
43 #include <fcntl.h>
44 #endif
46 #ifdef HAVE_SPIRV_CROSS_GIT_VERSION
47 #include "gitversion.h"
48 #endif
50 using namespace spv;
51 using namespace SPIRV_CROSS_NAMESPACE;
52 using namespace std;
54 #ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
55 static inline void THROW(const char *str)
57 fprintf(stderr, "SPIRV-Cross will abort: %s\n", str);
58 fflush(stderr);
59 abort();
61 #else
62 #define THROW(x) throw runtime_error(x)
63 #endif
65 struct CLIParser;
66 struct CLICallbacks
68 void add(const char *cli, const function<void(CLIParser &)> &func)
70 callbacks[cli] = func;
72 unordered_map<string, function<void(CLIParser &)>> callbacks;
73 function<void()> error_handler;
74 function<void(const char *)> default_handler;
77 struct CLIParser
79 CLIParser(CLICallbacks cbs_, int argc_, char *argv_[])
80 : cbs(std::move(cbs_))
81 , argc(argc_)
82 , argv(argv_)
86 bool parse()
88 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
89 try
90 #endif
92 while (argc && !ended_state)
94 const char *next = *argv++;
95 argc--;
97 if (*next != '-' && cbs.default_handler)
99 cbs.default_handler(next);
101 else
103 auto itr = cbs.callbacks.find(next);
104 if (itr == ::end(cbs.callbacks))
106 THROW("Invalid argument");
109 itr->second(*this);
113 return true;
115 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
116 catch (...)
118 if (cbs.error_handler)
120 cbs.error_handler();
122 return false;
124 #endif
127 void end()
129 ended_state = true;
132 uint32_t next_uint()
134 if (!argc)
136 THROW("Tried to parse uint, but nothing left in arguments");
139 uint64_t val = stoul(*argv);
140 if (val > numeric_limits<uint32_t>::max())
142 THROW("next_uint() out of range");
145 argc--;
146 argv++;
148 return uint32_t(val);
151 uint32_t next_hex_uint()
153 if (!argc)
155 THROW("Tried to parse uint, but nothing left in arguments");
158 uint64_t val = stoul(*argv, nullptr, 16);
159 if (val > numeric_limits<uint32_t>::max())
161 THROW("next_uint() out of range");
164 argc--;
165 argv++;
167 return uint32_t(val);
170 double next_double()
172 if (!argc)
174 THROW("Tried to parse double, but nothing left in arguments");
177 double val = stod(*argv);
179 argc--;
180 argv++;
182 return val;
185 // Return a string only if it's not prefixed with `--`, otherwise return the default value
186 const char *next_value_string(const char *default_value)
188 if (!argc)
190 return default_value;
193 if (0 == strncmp("--", *argv, 2))
195 return default_value;
198 return next_string();
201 const char *next_string()
203 if (!argc)
205 THROW("Tried to parse string, but nothing left in arguments");
208 const char *ret = *argv;
209 argc--;
210 argv++;
211 return ret;
214 CLICallbacks cbs;
215 int argc;
216 char **argv;
217 bool ended_state = false;
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
228 static vector<uint32_t> read_spirv_file_stdin()
230 #ifdef _WIN32
231 setmode(fileno(stdin), O_BINARY);
232 #endif
234 vector<uint32_t> buffer;
235 uint32_t tmp[256];
236 size_t ret;
238 while ((ret = fread(tmp, sizeof(uint32_t), 256, stdin)))
239 buffer.insert(buffer.end(), tmp, tmp + ret);
241 return buffer;
244 static vector<uint32_t> read_spirv_file(const char *path)
246 if (path[0] == '-' && path[1] == '\0')
247 return read_spirv_file_stdin();
249 FILE *file = fopen(path, "rb");
250 if (!file)
252 fprintf(stderr, "Failed to open SPIR-V file: %s\n", path);
253 return {};
256 fseek(file, 0, SEEK_END);
257 long len = ftell(file) / sizeof(uint32_t);
258 rewind(file);
260 vector<uint32_t> spirv(len);
261 if (fread(spirv.data(), sizeof(uint32_t), len, file) != size_t(len))
262 spirv.clear();
264 fclose(file);
265 return spirv;
268 static bool write_string_to_file(const char *path, const char *string)
270 FILE *file = fopen(path, "w");
271 if (!file)
273 fprintf(stderr, "Failed to write file: %s\n", path);
274 return false;
277 fprintf(file, "%s", string);
278 fclose(file);
279 return true;
282 #if defined(__clang__) || defined(__GNUC__)
283 #pragma GCC diagnostic pop
284 #elif defined(_MSC_VER)
285 #pragma warning(pop)
286 #endif
288 static void print_resources(const Compiler &compiler, spv::StorageClass storage,
289 const SmallVector<BuiltInResource> &resources)
291 fprintf(stderr, "%s\n", storage == StorageClassInput ? "builtin inputs" : "builtin outputs");
292 fprintf(stderr, "=============\n\n");
293 for (auto &res : resources)
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)
300 case SPIRType::Float: basetype = "float"; break;
301 case SPIRType::Int: basetype = "int"; break;
302 case SPIRType::UInt: basetype = "uint"; break;
303 default: break;
306 uint32_t array_size = 0;
307 bool array_size_literal = false;
308 if (!type.array.empty())
310 array_size = type.array.front();
311 array_size_literal = type.array_size_literal.front();
314 string type_str = basetype;
315 if (type.vecsize > 1)
316 type_str += std::to_string(type.vecsize);
318 if (array_size)
320 if (array_size_literal)
321 type_str += join("[", array_size, "]");
322 else
323 type_str += join("[", array_size, " (spec constant ID)]");
326 string builtin_str;
327 switch (res.builtin)
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);
338 fprintf(stderr, "Builtin %s (%s) (active: %s).\n", builtin_str.c_str(), type_str.c_str(), active ? "yes" : "no");
340 fprintf(stderr, "=============\n\n");
343 static void print_resources(const Compiler &compiler, const char *tag, const SmallVector<Resource> &resources)
345 fprintf(stderr, "%s\n", tag);
346 fprintf(stderr, "=============\n\n");
347 bool print_ssbo = !strcmp(tag, "ssbos");
349 for (auto &res : resources)
351 auto &type = compiler.get_type(res.type_id);
353 if (print_ssbo && compiler.buffer_is_hlsl_counter_buffer(res.id))
354 continue;
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);
366 uint32_t block_size = 0;
367 uint32_t runtime_array_stride = 0;
368 if (is_sized_block)
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));
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);
382 string array;
383 for (auto arr : type.array)
384 array = join("[", arr ? convert_to_string(arr) : "", "]") + array;
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());
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 (mask.get(DecorationRestrict))
404 fprintf(stderr, " restrict");
405 if (mask.get(DecorationCoherent))
406 fprintf(stderr, " coherent");
407 if (mask.get(DecorationVolatile))
408 fprintf(stderr, " volatile");
409 if (is_sized_block)
411 fprintf(stderr, " (BlockSize : %u bytes)", block_size);
412 if (runtime_array_stride)
413 fprintf(stderr, " (Unsized array stride: %u bytes)", runtime_array_stride);
416 uint32_t counter_id = 0;
417 if (print_ssbo && compiler.buffer_get_hlsl_counter_buffer(res.id, counter_id))
418 fprintf(stderr, " (HLSL counter buffer ID: %u)", counter_id);
419 fprintf(stderr, "\n");
421 fprintf(stderr, "=============\n\n");
424 static const char *execution_model_to_str(spv::ExecutionModel model)
426 switch (model)
428 case spv::ExecutionModelVertex:
429 return "vertex";
430 case spv::ExecutionModelTessellationControl:
431 return "tessellation control";
432 case ExecutionModelTessellationEvaluation:
433 return "tessellation evaluation";
434 case ExecutionModelGeometry:
435 return "geometry";
436 case ExecutionModelFragment:
437 return "fragment";
438 case ExecutionModelGLCompute:
439 return "compute";
440 case ExecutionModelRayGenerationNV:
441 return "raygenNV";
442 case ExecutionModelIntersectionNV:
443 return "intersectionNV";
444 case ExecutionModelCallableNV:
445 return "callableNV";
446 case ExecutionModelAnyHitNV:
447 return "anyhitNV";
448 case ExecutionModelClosestHitNV:
449 return "closesthitNV";
450 case ExecutionModelMissNV:
451 return "missNV";
452 default:
453 return "???";
457 static void print_resources(const Compiler &compiler, const ShaderResources &res)
459 auto &modes = compiler.get_execution_mode_bitset();
461 fprintf(stderr, "Entry points:\n");
462 auto entry_points = compiler.get_entry_points_and_stages();
463 for (auto &e : entry_points)
464 fprintf(stderr, " %s (%s)\n", e.name.c_str(), execution_model_to_str(e.execution_model));
465 fprintf(stderr, "\n");
467 fprintf(stderr, "Execution modes:\n");
468 modes.for_each_bit([&](uint32_t i) {
469 auto mode = static_cast<ExecutionMode>(i);
470 uint32_t arg0 = compiler.get_execution_mode_argument(mode, 0);
471 uint32_t arg1 = compiler.get_execution_mode_argument(mode, 1);
472 uint32_t arg2 = compiler.get_execution_mode_argument(mode, 2);
474 switch (static_cast<ExecutionMode>(i))
476 case ExecutionModeInvocations:
477 fprintf(stderr, " Invocations: %u\n", arg0);
478 break;
480 case ExecutionModeLocalSize:
481 fprintf(stderr, " LocalSize: (%u, %u, %u)\n", arg0, arg1, arg2);
482 break;
484 case ExecutionModeOutputVertices:
485 fprintf(stderr, " OutputVertices: %u\n", arg0);
486 break;
488 #define CHECK_MODE(m) \
489 case ExecutionMode##m: \
490 fprintf(stderr, " %s\n", #m); \
491 break
492 CHECK_MODE(SpacingEqual);
493 CHECK_MODE(SpacingFractionalEven);
494 CHECK_MODE(SpacingFractionalOdd);
495 CHECK_MODE(VertexOrderCw);
496 CHECK_MODE(VertexOrderCcw);
497 CHECK_MODE(PixelCenterInteger);
498 CHECK_MODE(OriginUpperLeft);
499 CHECK_MODE(OriginLowerLeft);
500 CHECK_MODE(EarlyFragmentTests);
501 CHECK_MODE(PointMode);
502 CHECK_MODE(Xfb);
503 CHECK_MODE(DepthReplacing);
504 CHECK_MODE(DepthGreater);
505 CHECK_MODE(DepthLess);
506 CHECK_MODE(DepthUnchanged);
507 CHECK_MODE(LocalSizeHint);
508 CHECK_MODE(InputPoints);
509 CHECK_MODE(InputLines);
510 CHECK_MODE(InputLinesAdjacency);
511 CHECK_MODE(Triangles);
512 CHECK_MODE(InputTrianglesAdjacency);
513 CHECK_MODE(Quads);
514 CHECK_MODE(Isolines);
515 CHECK_MODE(OutputPoints);
516 CHECK_MODE(OutputLineStrip);
517 CHECK_MODE(OutputTriangleStrip);
518 CHECK_MODE(VecTypeHint);
519 CHECK_MODE(ContractionOff);
521 default:
522 break;
525 fprintf(stderr, "\n");
527 print_resources(compiler, "subpass inputs", res.subpass_inputs);
528 print_resources(compiler, "inputs", res.stage_inputs);
529 print_resources(compiler, "outputs", res.stage_outputs);
530 print_resources(compiler, "textures", res.sampled_images);
531 print_resources(compiler, "separate images", res.separate_images);
532 print_resources(compiler, "separate samplers", res.separate_samplers);
533 print_resources(compiler, "images", res.storage_images);
534 print_resources(compiler, "ssbos", res.storage_buffers);
535 print_resources(compiler, "ubos", res.uniform_buffers);
536 print_resources(compiler, "push", res.push_constant_buffers);
537 print_resources(compiler, "counters", res.atomic_counters);
538 print_resources(compiler, "acceleration structures", res.acceleration_structures);
539 print_resources(compiler, "record buffers", res.shader_record_buffers);
540 print_resources(compiler, spv::StorageClassInput, res.builtin_inputs);
541 print_resources(compiler, spv::StorageClassOutput, res.builtin_outputs);
544 static void print_push_constant_resources(const Compiler &compiler, const SmallVector<Resource> &res)
546 for (auto &block : res)
548 auto ranges = compiler.get_active_buffer_ranges(block.id);
549 fprintf(stderr, "Active members in buffer: %s\n",
550 !block.name.empty() ? block.name.c_str() : compiler.get_fallback_name(block.id).c_str());
552 fprintf(stderr, "==================\n\n");
553 for (auto &range : ranges)
555 const auto &name = compiler.get_member_name(block.base_type_id, range.index);
557 fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index,
558 !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(),
559 unsigned(range.offset), unsigned(range.range));
561 fprintf(stderr, "==================\n\n");
565 static void print_spec_constants(const Compiler &compiler)
567 auto spec_constants = compiler.get_specialization_constants();
568 fprintf(stderr, "Specialization constants\n");
569 fprintf(stderr, "==================\n\n");
570 for (auto &c : spec_constants)
571 fprintf(stderr, "ID: %u, Spec ID: %u\n", uint32_t(c.id), c.constant_id);
572 fprintf(stderr, "==================\n\n");
575 static void print_capabilities_and_extensions(const Compiler &compiler)
577 fprintf(stderr, "Capabilities\n");
578 fprintf(stderr, "============\n");
579 for (auto &capability : compiler.get_declared_capabilities())
580 fprintf(stderr, "Capability: %u\n", static_cast<unsigned>(capability));
581 fprintf(stderr, "============\n\n");
583 fprintf(stderr, "Extensions\n");
584 fprintf(stderr, "============\n");
585 for (auto &ext : compiler.get_declared_extensions())
586 fprintf(stderr, "Extension: %s\n", ext.c_str());
587 fprintf(stderr, "============\n\n");
590 struct PLSArg
592 PlsFormat format;
593 string name;
596 struct Remap
598 string src_name;
599 string dst_name;
600 unsigned components;
603 struct VariableTypeRemap
605 string variable_name;
606 string new_variable_type;
609 struct InterfaceVariableRename
611 StorageClass storageClass;
612 uint32_t location;
613 string variable_name;
616 struct HLSLVertexAttributeRemapNamed
618 std::string name;
619 std::string semantic;
622 struct CLIArguments
624 const char *input = nullptr;
625 const char *output = nullptr;
626 const char *cpp_interface_name = nullptr;
627 uint32_t version = 0;
628 uint32_t shader_model = 0;
629 uint32_t msl_version = 0;
630 bool es = false;
631 bool set_version = false;
632 bool set_shader_model = false;
633 bool set_msl_version = false;
634 bool set_es = false;
635 bool dump_resources = false;
636 bool force_temporary = false;
637 bool flatten_ubo = false;
638 bool fixup = false;
639 bool yflip = false;
640 bool sso = false;
641 bool support_nonzero_baseinstance = true;
642 bool msl_capture_output_to_buffer = false;
643 bool msl_swizzle_texture_samples = false;
644 bool msl_ios = false;
645 bool msl_pad_fragment_output = false;
646 bool msl_domain_lower_left = false;
647 bool msl_argument_buffers = false;
648 uint32_t msl_argument_buffers_tier = 0; // Tier 1
649 bool msl_texture_buffer_native = false;
650 bool msl_framebuffer_fetch = false;
651 bool msl_invariant_float_math = false;
652 bool msl_emulate_cube_array = false;
653 bool msl_multiview = false;
654 bool msl_multiview_layered_rendering = true;
655 bool msl_view_index_from_device_index = false;
656 bool msl_dispatch_base = false;
657 bool msl_decoration_binding = false;
658 bool msl_force_active_argument_buffer_resources = false;
659 bool msl_force_native_arrays = false;
660 bool msl_enable_frag_depth_builtin = true;
661 bool msl_enable_frag_stencil_ref_builtin = true;
662 uint32_t msl_enable_frag_output_mask = 0xffffffff;
663 bool msl_enable_clip_distance_user_varying = true;
664 bool msl_raw_buffer_tese_input = false;
665 bool msl_multi_patch_workgroup = false;
666 bool msl_vertex_for_tessellation = false;
667 uint32_t msl_additional_fixed_sample_mask = 0xffffffff;
668 bool msl_arrayed_subpass_input = false;
669 uint32_t msl_r32ui_linear_texture_alignment = 4;
670 uint32_t msl_r32ui_alignment_constant_id = 65535;
671 bool msl_texture_1d_as_2d = false;
672 bool msl_ios_use_simdgroup_functions = false;
673 bool msl_emulate_subgroups = false;
674 uint32_t msl_fixed_subgroup_size = 0;
675 bool msl_force_sample_rate_shading = false;
676 bool msl_manual_helper_invocation_updates = true;
677 bool msl_check_discarded_frag_stores = false;
678 const char *msl_combined_sampler_suffix = nullptr;
679 bool glsl_emit_push_constant_as_ubo = false;
680 bool glsl_emit_ubo_as_plain_uniforms = false;
681 bool glsl_force_flattened_io_blocks = false;
682 uint32_t glsl_ovr_multiview_view_count = 0;
683 SmallVector<pair<uint32_t, uint32_t>> glsl_ext_framebuffer_fetch;
684 bool glsl_ext_framebuffer_fetch_noncoherent = false;
685 bool vulkan_glsl_disable_ext_samplerless_texture_functions = false;
686 bool emit_line_directives = false;
687 bool enable_storage_image_qualifier_deduction = true;
688 bool force_zero_initialized_variables = false;
689 bool relax_nan_checks = false;
690 uint32_t force_recompile_max_debug_iterations = 3;
691 SmallVector<uint32_t> msl_discrete_descriptor_sets;
692 SmallVector<uint32_t> msl_device_argument_buffers;
693 SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers;
694 SmallVector<pair<uint32_t, uint32_t>> msl_inline_uniform_blocks;
695 SmallVector<MSLShaderInterfaceVariable> msl_shader_inputs;
696 SmallVector<MSLShaderInterfaceVariable> msl_shader_outputs;
697 SmallVector<PLSArg> pls_in;
698 SmallVector<PLSArg> pls_out;
699 SmallVector<Remap> remaps;
700 SmallVector<string> extensions;
701 SmallVector<VariableTypeRemap> variable_type_remaps;
702 SmallVector<InterfaceVariableRename> interface_variable_renames;
703 SmallVector<HLSLVertexAttributeRemap> hlsl_attr_remap;
704 SmallVector<HLSLVertexAttributeRemapNamed> hlsl_attr_remap_named;
705 SmallVector<std::pair<uint32_t, uint32_t>> masked_stage_outputs;
706 SmallVector<BuiltIn> masked_stage_builtins;
707 string entry;
708 string entry_stage;
710 struct Rename
712 string old_name;
713 string new_name;
714 ExecutionModel execution_model;
716 SmallVector<Rename> entry_point_rename;
718 uint32_t iterations = 1;
719 bool cpp = false;
720 string reflect;
721 bool msl = false;
722 bool hlsl = false;
723 bool hlsl_compat = false;
725 bool hlsl_support_nonzero_base = false;
726 bool hlsl_base_vertex_index_explicit_binding = false;
727 uint32_t hlsl_base_vertex_index_register_index = 0;
728 uint32_t hlsl_base_vertex_index_register_space = 0;
730 bool hlsl_force_storage_buffer_as_uav = false;
731 bool hlsl_nonwritable_uav_texture_as_srv = false;
732 bool hlsl_enable_16bit_types = false;
733 bool hlsl_flatten_matrix_vertex_input_semantics = false;
734 HLSLBindingFlags hlsl_binding_flags = 0;
735 bool vulkan_semantics = false;
736 bool flatten_multidimensional_arrays = false;
737 bool use_420pack_extension = true;
738 bool remove_unused = false;
739 bool combined_samplers_inherit_bindings = false;
742 static void print_version()
744 #ifdef HAVE_SPIRV_CROSS_GIT_VERSION
745 fprintf(stderr, "%s\n", SPIRV_CROSS_GIT_REVISION);
746 #else
747 fprintf(stderr, "Git revision unknown. Build with CMake to create timestamp and revision info.\n");
748 #endif
751 static void print_help_backend()
753 // clang-format off
754 fprintf(stderr, "\nSelect backend:\n"
755 "\tBy default, OpenGL-style GLSL is the target, with #version and GLSL/ESSL information inherited from the SPIR-V module if present.\n"
756 "\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"
757 "\t[--msl]:\n\t\tEmit Metal Shading Language (MSL).\n"
758 "\t[--hlsl]:\n\t\tEmit HLSL.\n"
759 "\t[--reflect]:\n\t\tEmit JSON reflection.\n"
760 "\t[--cpp]:\n\t\tDEPRECATED. Emits C++ code.\n"
762 // clang-format on
765 static void print_help_glsl()
767 // clang-format off
768 fprintf(stderr, "\nGLSL options:\n"
769 "\t[--es]:\n\t\tForce ESSL.\n"
770 "\t[--no-es]:\n\t\tForce desktop GLSL.\n"
771 "\t[--version <GLSL version>]:\n\t\tE.g. --version 450 will emit '#version 450' in shader.\n"
772 "\t\tCode generation will depend on the version used.\n"
773 "\t[--flatten-ubo]:\n\t\tEmit UBOs as plain uniform arrays which are suitable for use with glUniform4*v().\n"
774 "\t\tThis can be an optimization on GL implementations where this is faster or works around buggy driver implementations.\n"
775 "\t\tE.g.: uniform MyUBO { vec4 a; float b, c, d, e; }; will be emitted as uniform vec4 MyUBO[2];\n"
776 "\t\tCaveat: You cannot mix and match floating-point and integer in the same UBO with this option.\n"
777 "\t\tLegacy GLSL/ESSL (where this flattening makes sense) does not support bit-casting, which would have been the obvious workaround.\n"
778 "\t[--extension ext]:\n\t\tAdd #extension string of your choosing to GLSL output.\n"
779 "\t\tUseful if you use variable name remapping to something that requires an extension unknown to SPIRV-Cross.\n"
780 "\t[--remove-unused-variables]:\n\t\tDo not emit interface variables which are not statically accessed by the shader.\n"
781 "\t[--separate-shader-objects]:\n\t\tRedeclare gl_PerVertex blocks to be suitable for desktop GL separate shader objects.\n"
782 "\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"
783 "\t[--glsl-emit-ubo-as-plain-uniforms]:\n\t\tInstead of emitting UBOs, emit them as plain uniform structs.\n"
784 "\t[--glsl-remap-ext-framebuffer-fetch input-attachment color-location]:\n\t\tRemaps an input attachment to use GL_EXT_shader_framebuffer_fetch.\n"
785 "\t\tgl_LastFragData[location] is read from. The attachment to read from must be declared as an output in the shader.\n"
786 "\t[--glsl-ext-framebuffer-fetch-noncoherent]:\n\t\tUses noncoherent qualifier for framebuffer fetch.\n"
787 "\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"
788 "\t\tUse of texelFetch and similar might have to create dummy samplers to work around it.\n"
789 "\t[--combined-samplers-inherit-bindings]:\n\t\tInherit binding information from the textures when building combined image samplers from separate textures and samplers.\n"
790 "\t[--no-support-nonzero-baseinstance]:\n\t\tWhen using gl_InstanceIndex with desktop GL,\n"
791 "\t\tassume that base instance is always 0, and do not attempt to fix up gl_InstanceID to match Vulkan semantics.\n"
792 "\t[--pls-in format input-name]:\n\t\tRemaps a subpass input with name into a GL_EXT_pixel_local_storage input.\n"
793 "\t\tEntry in PLS block is ordered where first --pls-in marks the first entry. Can be called multiple times.\n"
794 "\t\tFormats allowed: r11f_g11f_b10f, r32f, rg16f, rg16, rgb10_a2, rgba8, rgba8i, rgba8ui, rg16i, rgb10_a2ui, rg16ui, r32ui.\n"
795 "\t\tRequires ESSL.\n"
796 "\t[--pls-out format output-name]:\n\t\tRemaps a color output with name into a GL_EXT_pixel_local_storage output.\n"
797 "\t\tEntry in PLS block is ordered where first --pls-output marks the first entry. Can be called multiple times.\n"
798 "\t\tFormats allowed: r11f_g11f_b10f, r32f, rg16f, rg16, rgb10_a2, rgba8, rgba8i, rgba8ui, rg16i, rgb10_a2ui, rg16ui, r32ui.\n"
799 "\t\tRequires ESSL.\n"
800 "\t[--remap source_name target_name components]:\n\t\tRemaps a variable to a different name with N components.\n"
801 "\t\tMain use case is to remap a subpass input to gl_LastFragDepthARM.\n"
802 "\t\tE.g.:\n"
803 "\t\tuniform subpassInput uDepth;\n"
804 "\t\t--remap uDepth gl_LastFragDepthARM 1 --extension GL_ARM_shader_framebuffer_fetch_depth_stencil\n"
805 "\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"
806 "\t[--remap-variable-type <variable_name> <new_variable_type>]:\n\t\tRemaps a variable type based on name.\n"
807 "\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"
808 "\t[--glsl-force-flattened-io-blocks]:\n\t\tAlways flatten I/O blocks and structs.\n"
809 "\t[--glsl-ovr-multiview-view-count count]:\n\t\tIn GL_OVR_multiview2, specify layout(num_views).\n"
811 // clang-format on
814 static void print_help_hlsl()
816 // clang-format off
817 fprintf(stderr, "\nHLSL options:\n"
818 "\t[--shader-model]:\n\t\tEnables a specific shader model, e.g. --shader-model 50 for SM 5.0.\n"
819 "\t[--flatten-ubo]:\n\t\tEmit UBOs as plain uniform arrays.\n"
820 "\t\tE.g.: uniform MyUBO { vec4 a; float b, c, d, e; }; will be emitted as uniform float4 MyUBO[2];\n"
821 "\t\tCaveat: You cannot mix and match floating-point and integer in the same UBO with this option.\n"
822 "\t[--hlsl-enable-compat]:\n\t\tAllow point size and point coord to be used, even if they won't work as expected.\n"
823 "\t\tPointSize is ignored, and PointCoord returns (0.5, 0.5).\n"
824 "\t[--hlsl-support-nonzero-basevertex-baseinstance]:\n\t\tSupport base vertex and base instance by emitting a special cbuffer declared as:\n"
825 "\t\tcbuffer SPIRV_Cross_VertexInfo { int SPIRV_Cross_BaseVertex; int SPIRV_Cross_BaseInstance; };\n"
826 "\t[--hlsl-basevertex-baseinstance-binding <register index> <register space>]:\n\t\tAssign a fixed binding to SPIRV_Cross_VertexInfo.\n"
827 "\t[--hlsl-auto-binding (push, cbv, srv, uav, sampler, all)]\n"
828 "\t\tDo not emit any : register(#) bindings for specific resource types, and rely on HLSL compiler to assign something.\n"
829 "\t[--hlsl-force-storage-buffer-as-uav]:\n\t\tAlways emit SSBOs as UAVs, even when marked as read-only.\n"
830 "\t\tNormally, SSBOs marked with NonWritable will be emitted as SRVs.\n"
831 "\t[--hlsl-nonwritable-uav-texture-as-srv]:\n\t\tEmit NonWritable storage images as SRV textures instead of UAV.\n"
832 "\t\tUsing this option messes with the type system. SPIRV-Cross cannot guarantee that this will work.\n"
833 "\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"
834 "\t\tShader must ensure that read/write state is consistent at all call sites.\n"
835 "\t[--set-hlsl-vertex-input-semantic <location> <semantic>]:\n\t\tEmits a specific vertex input semantic for a given location.\n"
836 "\t\tOtherwise, TEXCOORD# is used as semantics, where # is location.\n"
837 "\t[--set-hlsl-named-vertex-input-semantic <name> <semantic>]:\n\t\tEmits a specific vertex input semantic for a given name.\n"
838 "\t\tOpName reflection information must be intact.\n"
839 "\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"
840 "\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"
842 // clang-format on
845 static void print_help_msl()
847 // clang-format off
848 fprintf(stderr, "\nMSL options:\n"
849 "\t[--msl-version <MMmmpp>]:\n\t\tUses a specific MSL version, e.g. --msl-version 20100 for MSL 2.1.\n"
850 "\t[--msl-capture-output]:\n\t\tWrites geometry varyings to a buffer instead of as stage-outputs.\n"
851 "\t[--msl-swizzle-texture-samples]:\n\t\tWorks around lack of support for VkImageView component swizzles.\n"
852 "\t\tThis has a massive impact on performance and bloat. Do not use this unless you are absolutely forced to.\n"
853 "\t\tTo use this feature, the API side must pass down swizzle buffers.\n"
854 "\t\tShould only be used by translation layers as a last resort.\n"
855 "\t\tRecent Metal versions do not require this workaround.\n"
856 "\t[--msl-ios]:\n\t\tTarget iOS Metal instead of macOS Metal.\n"
857 "\t[--msl-pad-fragment-output]:\n\t\tAlways emit color outputs as 4-component variables.\n"
858 "\t\tIn Metal, the fragment shader must emit at least as many components as the render target format.\n"
859 "\t[--msl-domain-lower-left]:\n\t\tUse a lower-left tessellation domain.\n"
860 "\t[--msl-argument-buffers]:\n\t\tEmit Metal argument buffers instead of discrete resource bindings.\n"
861 "\t\tRequires MSL 2.0 to be enabled.\n"
862 "\t[--msl-argument-buffers-tier]:\n\t\tWhen using Metal argument buffers, indicate the Metal argument buffer tier level supported by the Metal platform.\n"
863 "\t\tUses same values as Metal MTLArgumentBuffersTier enumeration (0 = Tier1, 1 = Tier2).\n"
864 "\t\tSetting this value also enables msl-argument-buffers.\n"
865 "\t[--msl-texture-buffer-native]:\n\t\tEnable native support for texel buffers. Otherwise, it is emulated as a normal texture.\n"
866 "\t[--msl-framebuffer-fetch]:\n\t\tImplement subpass inputs with frame buffer fetch.\n"
867 "\t\tEmits [[color(N)]] inputs in fragment stage.\n"
868 "\t\tRequires an Apple GPU.\n"
869 "\t[--msl-emulate-cube-array]:\n\t\tEmulate cube arrays with 2D array and manual math.\n"
870 "\t[--msl-discrete-descriptor-set <index>]:\n\t\tWhen using argument buffers, forces a specific descriptor set to be implemented without argument buffers.\n"
871 "\t\tUseful for implementing push descriptors in emulation layers.\n"
872 "\t\tCan be used multiple times for each descriptor set in question.\n"
873 "\t[--msl-device-argument-buffer <descriptor set index>]:\n\t\tUse device address space to hold indirect argument buffers instead of constant.\n"
874 "\t\tComes up when trying to support argument buffers which are larger than 64 KiB.\n"
875 "\t[--msl-multiview]:\n\t\tEnable SPV_KHR_multiview emulation.\n"
876 "\t[--msl-multiview-no-layered-rendering]:\n\t\tDon't set [[render_target_array_index]] in multiview shaders.\n"
877 "\t\tUseful for devices which don't support layered rendering. Only effective when --msl-multiview is enabled.\n"
878 "\t[--msl-view-index-from-device-index]:\n\t\tTreat the view index as the device index instead.\n"
879 "\t\tFor multi-GPU rendering.\n"
880 "\t[--msl-dispatch-base]:\n\t\tAdd support for vkCmdDispatchBase() or similar APIs.\n"
881 "\t\tOffsets the workgroup ID based on a buffer.\n"
882 "\t[--msl-dynamic-buffer <set index> <binding>]:\n\t\tMarks a buffer as having dynamic offset.\n"
883 "\t\tThe offset is applied in the shader with pointer arithmetic.\n"
884 "\t\tUseful for argument buffers where it is non-trivial to apply dynamic offset otherwise.\n"
885 "\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"
886 "\t[--msl-decoration-binding]:\n\t\tUse SPIR-V bindings directly as MSL bindings.\n"
887 "\t\tThis does not work in the general case as there is no descriptor set support, and combined image samplers are split up.\n"
888 "\t\tHowever, if the shader author knows of binding limitations, this option will avoid the need for reflection on Metal side.\n"
889 "\t[--msl-force-active-argument-buffer-resources]:\n\t\tAlways emit resources which are part of argument buffers.\n"
890 "\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"
891 "\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"
892 "\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"
893 "\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"
894 "\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"
895 "\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"
896 "\t[--msl-no-clip-distance-user-varying]:\n\t\tDo not emit user varyings to emulate gl_ClipDistance in fragment shaders.\n"
897 "\t[--msl-add-shader-input <index> <format> <size> <rate>]:\n\t\tSpecify the format of the shader input at <index>.\n"
898 "\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, "
899 "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. <rate> can be 'vertex', "
900 "'primitive', or 'patch' to indicate a per-vertex, per-primitive, or per-patch variable.\n"
901 "\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n"
902 "\t[--msl-add-shader-output <index> <format> <size> <rate>]:\n\t\tSpecify the format of the shader output at <index>.\n"
903 "\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, "
904 "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. <rate> can be 'vertex', "
905 "'primitive', or 'patch' to indicate a per-vertex, per-primitive, or per-patch variable.\n"
906 "\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n"
907 "\t[--msl-shader-input <index> <format> <size>]:\n\t\tSpecify the format of the shader input at <index>.\n"
908 "\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, "
909 "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."
910 "\t\tEquivalent to --msl-add-shader-input with a rate of 'vertex'.\n"
911 "\t[--msl-shader-output <index> <format> <size>]:\n\t\tSpecify the format of the shader output at <index>.\n"
912 "\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, "
913 "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."
914 "\t\tEquivalent to --msl-add-shader-output with a rate of 'vertex'.\n"
915 "\t[--msl-raw-buffer-tese-input]:\n\t\tUse raw buffers for tessellation evaluation input.\n"
916 "\t\tThis allows the use of nested structures and arrays.\n"
917 "\t\tIn a future version of SPIRV-Cross, this will become the default.\n"
918 "\t[--msl-multi-patch-workgroup]:\n\t\tUse the new style of tessellation control processing, where multiple patches are processed per workgroup.\n"
919 "\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"
920 "\t\tIn addition, this style also passes input variables in buffers directly instead of using vertex attribute processing.\n"
921 "\t\tIn a future version of SPIRV-Cross, this will become the default.\n"
922 "\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"
923 "\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"
924 "\t[--msl-additional-fixed-sample-mask <mask>]:\n"
925 "\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"
926 "\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"
927 "\t\tThis option has no effect if multiview is also enabled.\n"
928 "\t[--msl-r32ui-linear-texture-align <alignment>]:\n\t\tThe required alignment of linear textures of format MTLPixelFormatR32Uint.\n"
929 "\t\tThis is used to align the row stride for atomic accesses to such images.\n"
930 "\t[--msl-r32ui-linear-texture-align-constant-id <id>]:\n\t\tThe function constant ID to use for the linear texture alignment.\n"
931 "\t\tOn MSL 1.2 or later, you can override the alignment by setting this function constant.\n"
932 "\t[--msl-texture-1d-as-2d]:\n\t\tEmit Image variables of dimension Dim1D as texture2d.\n"
933 "\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"
934 "\t[--msl-ios-use-simdgroup-functions]:\n\t\tUse simd_*() functions for subgroup ops instead of quad_*().\n"
935 "\t\tRecent Apple GPUs support SIMD-groups larger than a quad. Use this option to take advantage of this support.\n"
936 "\t[--msl-emulate-subgroups]:\n\t\tAssume subgroups of size 1.\n"
937 "\t\tIntended for Vulkan Portability implementations where Metal support for SIMD-groups is insufficient for true subgroups.\n"
938 "\t[--msl-fixed-subgroup-size <size>]:\n\t\tAssign a constant <size> to the SubgroupSize builtin.\n"
939 "\t\tIntended for Vulkan Portability implementations where VK_EXT_subgroup_size_control is not supported or disabled.\n"
940 "\t\tIf 0, assume variable subgroup size as actually exposed by Metal.\n"
941 "\t[--msl-force-sample-rate-shading]:\n\t\tForce fragment shaders to run per sample.\n"
942 "\t\tThis adds a [[sample_id]] parameter if none is already present.\n"
943 "\t[--msl-no-manual-helper-invocation-updates]:\n\t\tDo not manually update the HelperInvocation builtin when a fragment is discarded.\n"
944 "\t\tSome Metal devices have a bug where simd_is_helper_thread() does not return true\n"
945 "\t\tafter the fragment is discarded. This behavior is required by Vulkan and SPIR-V, however.\n"
946 "\t[--msl-check-discarded-frag-stores]:\n\t\tAdd additional checks to resource stores in a fragment shader.\n"
947 "\t\tSome Metal devices have a bug where stores to resources from a fragment shader\n"
948 "\t\tcontinue to execute, even when the fragment is discarded. These checks\n"
949 "\t\tprevent these stores from executing.\n"
950 "\t[--msl-combined-sampler-suffix <suffix>]:\n\t\tUses a custom suffix for combined samplers.\n");
951 // clang-format on
954 static void print_help_common()
956 // clang-format off
957 fprintf(stderr, "\nCommon options:\n"
958 "\t[--entry name]:\n\t\tUse a specific entry point. By default, the first entry point in the module is used.\n"
959 "\t[--stage <stage (vert, frag, geom, tesc, tese, comp)>]:\n\t\tForces use of a certain shader stage.\n"
960 "\t\tCan disambiguate the entry point if more than one entry point exists with same name, but different stage.\n"
961 "\t[--emit-line-directives]:\n\t\tIf SPIR-V has OpLine directives, aim to emit those accurately in output code as well.\n"
962 "\t[--rename-entry-point <old> <new> <stage>]:\n\t\tRenames an entry point from what is declared in SPIR-V to code output.\n"
963 "\t\tMostly relevant for HLSL or MSL.\n"
964 "\t[--rename-interface-variable <in|out> <location> <new_variable_name>]:\n\t\tRename an interface variable based on location decoration.\n"
965 "\t[--force-zero-initialized-variables]:\n\t\tForces temporary variables to be initialized to zero.\n"
966 "\t\tCan be useful in environments where compilers do not allow potentially uninitialized variables.\n"
967 "\t\tThis usually comes up with Phi temporaries.\n"
968 "\t[--fixup-clipspace]:\n\t\tFixup Z clip-space at the end of a vertex shader. The behavior is backend-dependent.\n"
969 "\t\tGLSL: Rewrites [0, w] Z range (D3D/Metal/Vulkan) to GL-style [-w, w].\n"
970 "\t\tHLSL/MSL: Rewrites [-w, w] Z range (GL) to D3D/Metal/Vulkan-style [0, w].\n"
971 "\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"
972 "\t[--mask-stage-output-location <location> <component>]:\n"
973 "\t\tIf a stage output variable with matching location and component is active, optimize away the variable if applicable.\n"
974 "\t[--mask-stage-output-builtin <Position|PointSize|ClipDistance|CullDistance>]:\n"
975 "\t\tIf a stage output variable with matching builtin is active, "
976 "optimize away the variable if it can affect cross-stage linking correctness.\n"
977 "\t[--relax-nan-checks]:\n\t\tRelax NaN checks for N{Clamp,Min,Max} and ordered vs. unordered compare instructions.\n"
979 // clang-format on
982 static void print_help_obscure()
984 // clang-format off
985 fprintf(stderr, "\nObscure options:\n"
986 "\tThese options are not meant to be used on a regular basis. They have some occasional uses in the test suite.\n"
988 "\t[--force-temporary]:\n\t\tAggressively emit temporary expressions instead of forwarding expressions. Very rarely used and under-tested.\n"
989 "\t[--revision]:\n\t\tPrints build timestamp and Git commit information (updated when cmake is configured).\n"
990 "\t[--iterations iter]:\n\t\tRecompiles the same shader over and over, benchmarking related.\n"
991 "\t[--disable-storage-image-qualifier-deduction]:\n\t\tIf storage images are received without any nonwritable or nonreadable information,\n"""
992 "\t\tdo not attempt to analyze usage, and always emit read/write state.\n"
993 "\t[--flatten-multidimensional-arrays]:\n\t\tDo not support multi-dimensional arrays and flatten them to one dimension.\n"
994 "\t[--cpp-interface-name <name>]:\n\t\tEmit a specific class name in C++ codegen.\n"
995 "\t[--force-recompile-max-debug-iterations <count>]:\n\t\tAllow compilation loop to run for N loops.\n"
996 "\t\tCan be used to triage workarounds, but should not be used as a crutch, since it masks an implementation bug.\n"
998 // clang-format on
1001 static void print_help()
1003 print_version();
1005 // clang-format off
1006 fprintf(stderr, "Usage: spirv-cross <...>\n"
1007 "\nBasic:\n"
1008 "\t[SPIR-V file] (- is stdin)\n"
1009 "\t[--output <output path>]: If not provided, prints output to stdout.\n"
1010 "\t[--dump-resources]:\n\t\tPrints a basic reflection of the SPIR-V module along with other output.\n"
1011 "\t[--help]:\n\t\tPrints this help message.\n"
1013 // clang-format on
1015 print_help_backend();
1016 print_help_common();
1017 print_help_glsl();
1018 print_help_msl();
1019 print_help_hlsl();
1020 print_help_obscure();
1023 static bool remap_generic(Compiler &compiler, const SmallVector<Resource> &resources, const Remap &remap)
1025 auto itr =
1026 find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; });
1028 if (itr != end(resources))
1030 compiler.set_remapped_variable_state(itr->id, true);
1031 compiler.set_name(itr->id, remap.dst_name);
1032 compiler.set_subpass_input_remapped_components(itr->id, remap.components);
1033 return true;
1035 else
1036 return false;
1039 static vector<PlsRemap> remap_pls(const SmallVector<PLSArg> &pls_variables, const SmallVector<Resource> &resources,
1040 const SmallVector<Resource> *secondary_resources)
1042 vector<PlsRemap> ret;
1044 for (auto &pls : pls_variables)
1046 bool found = false;
1047 for (auto &res : resources)
1049 if (res.name == pls.name)
1051 ret.push_back({ res.id, pls.format });
1052 found = true;
1053 break;
1057 if (!found && secondary_resources)
1059 for (auto &res : *secondary_resources)
1061 if (res.name == pls.name)
1063 ret.push_back({ res.id, pls.format });
1064 found = true;
1065 break;
1070 if (!found)
1071 fprintf(stderr, "Did not find stage input/output/target with name \"%s\".\n", pls.name.c_str());
1074 return ret;
1077 static PlsFormat pls_format(const char *str)
1079 if (!strcmp(str, "r11f_g11f_b10f"))
1080 return PlsR11FG11FB10F;
1081 else if (!strcmp(str, "r32f"))
1082 return PlsR32F;
1083 else if (!strcmp(str, "rg16f"))
1084 return PlsRG16F;
1085 else if (!strcmp(str, "rg16"))
1086 return PlsRG16;
1087 else if (!strcmp(str, "rgb10_a2"))
1088 return PlsRGB10A2;
1089 else if (!strcmp(str, "rgba8"))
1090 return PlsRGBA8;
1091 else if (!strcmp(str, "rgba8i"))
1092 return PlsRGBA8I;
1093 else if (!strcmp(str, "rgba8ui"))
1094 return PlsRGBA8UI;
1095 else if (!strcmp(str, "rg16i"))
1096 return PlsRG16I;
1097 else if (!strcmp(str, "rgb10_a2ui"))
1098 return PlsRGB10A2UI;
1099 else if (!strcmp(str, "rg16ui"))
1100 return PlsRG16UI;
1101 else if (!strcmp(str, "r32ui"))
1102 return PlsR32UI;
1103 else
1104 return PlsNone;
1107 static ExecutionModel stage_to_execution_model(const std::string &stage)
1109 if (stage == "vert")
1110 return ExecutionModelVertex;
1111 else if (stage == "frag")
1112 return ExecutionModelFragment;
1113 else if (stage == "comp")
1114 return ExecutionModelGLCompute;
1115 else if (stage == "tesc")
1116 return ExecutionModelTessellationControl;
1117 else if (stage == "tese")
1118 return ExecutionModelTessellationEvaluation;
1119 else if (stage == "geom")
1120 return ExecutionModelGeometry;
1121 else if (stage == "rgen")
1122 return ExecutionModelRayGenerationKHR;
1123 else if (stage == "rint")
1124 return ExecutionModelIntersectionKHR;
1125 else if (stage == "rahit")
1126 return ExecutionModelAnyHitKHR;
1127 else if (stage == "rchit")
1128 return ExecutionModelClosestHitKHR;
1129 else if (stage == "rmiss")
1130 return ExecutionModelMissKHR;
1131 else if (stage == "rcall")
1132 return ExecutionModelCallableKHR;
1133 else if (stage == "mesh")
1134 return spv::ExecutionModelMeshEXT;
1135 else if (stage == "task")
1136 return spv::ExecutionModelTaskEXT;
1137 else
1138 SPIRV_CROSS_THROW("Invalid stage.");
1141 static HLSLBindingFlags hlsl_resource_type_to_flag(const std::string &arg)
1143 if (arg == "push")
1144 return HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT;
1145 else if (arg == "cbv")
1146 return HLSL_BINDING_AUTO_CBV_BIT;
1147 else if (arg == "srv")
1148 return HLSL_BINDING_AUTO_SRV_BIT;
1149 else if (arg == "uav")
1150 return HLSL_BINDING_AUTO_UAV_BIT;
1151 else if (arg == "sampler")
1152 return HLSL_BINDING_AUTO_SAMPLER_BIT;
1153 else if (arg == "all")
1154 return HLSL_BINDING_AUTO_ALL;
1155 else
1157 fprintf(stderr, "Invalid resource type for --hlsl-auto-binding: %s\n", arg.c_str());
1158 return 0;
1162 static string compile_iteration(const CLIArguments &args, std::vector<uint32_t> spirv_file)
1164 Parser spirv_parser(std::move(spirv_file));
1165 spirv_parser.parse();
1167 unique_ptr<CompilerGLSL> compiler;
1168 bool combined_image_samplers = false;
1169 bool build_dummy_sampler = false;
1171 if (args.cpp)
1173 compiler.reset(new CompilerCPP(std::move(spirv_parser.get_parsed_ir())));
1174 if (args.cpp_interface_name)
1175 static_cast<CompilerCPP *>(compiler.get())->set_interface_name(args.cpp_interface_name);
1177 else if (args.msl)
1179 compiler.reset(new CompilerMSL(std::move(spirv_parser.get_parsed_ir())));
1181 auto *msl_comp = static_cast<CompilerMSL *>(compiler.get());
1182 auto msl_opts = msl_comp->get_msl_options();
1183 if (args.set_msl_version)
1184 msl_opts.msl_version = args.msl_version;
1185 msl_opts.capture_output_to_buffer = args.msl_capture_output_to_buffer;
1186 msl_opts.swizzle_texture_samples = args.msl_swizzle_texture_samples;
1187 msl_opts.invariant_float_math = args.msl_invariant_float_math;
1188 if (args.msl_ios)
1190 msl_opts.platform = CompilerMSL::Options::iOS;
1191 msl_opts.emulate_cube_array = args.msl_emulate_cube_array;
1193 msl_opts.use_framebuffer_fetch_subpasses = args.msl_framebuffer_fetch;
1194 msl_opts.pad_fragment_output_components = args.msl_pad_fragment_output;
1195 msl_opts.tess_domain_origin_lower_left = args.msl_domain_lower_left;
1196 msl_opts.argument_buffers = args.msl_argument_buffers;
1197 msl_opts.argument_buffers_tier = static_cast<CompilerMSL::Options::ArgumentBuffersTier>(args.msl_argument_buffers_tier);
1198 msl_opts.texture_buffer_native = args.msl_texture_buffer_native;
1199 msl_opts.multiview = args.msl_multiview;
1200 msl_opts.multiview_layered_rendering = args.msl_multiview_layered_rendering;
1201 msl_opts.view_index_from_device_index = args.msl_view_index_from_device_index;
1202 msl_opts.dispatch_base = args.msl_dispatch_base;
1203 msl_opts.enable_decoration_binding = args.msl_decoration_binding;
1204 msl_opts.force_active_argument_buffer_resources = args.msl_force_active_argument_buffer_resources;
1205 msl_opts.force_native_arrays = args.msl_force_native_arrays;
1206 msl_opts.enable_frag_depth_builtin = args.msl_enable_frag_depth_builtin;
1207 msl_opts.enable_frag_stencil_ref_builtin = args.msl_enable_frag_stencil_ref_builtin;
1208 msl_opts.enable_frag_output_mask = args.msl_enable_frag_output_mask;
1209 msl_opts.enable_clip_distance_user_varying = args.msl_enable_clip_distance_user_varying;
1210 msl_opts.raw_buffer_tese_input = args.msl_raw_buffer_tese_input;
1211 msl_opts.multi_patch_workgroup = args.msl_multi_patch_workgroup;
1212 msl_opts.vertex_for_tessellation = args.msl_vertex_for_tessellation;
1213 msl_opts.additional_fixed_sample_mask = args.msl_additional_fixed_sample_mask;
1214 msl_opts.arrayed_subpass_input = args.msl_arrayed_subpass_input;
1215 msl_opts.r32ui_linear_texture_alignment = args.msl_r32ui_linear_texture_alignment;
1216 msl_opts.r32ui_alignment_constant_id = args.msl_r32ui_alignment_constant_id;
1217 msl_opts.texture_1D_as_2D = args.msl_texture_1d_as_2d;
1218 msl_opts.ios_use_simdgroup_functions = args.msl_ios_use_simdgroup_functions;
1219 msl_opts.emulate_subgroups = args.msl_emulate_subgroups;
1220 msl_opts.fixed_subgroup_size = args.msl_fixed_subgroup_size;
1221 msl_opts.force_sample_rate_shading = args.msl_force_sample_rate_shading;
1222 msl_opts.manual_helper_invocation_updates = args.msl_manual_helper_invocation_updates;
1223 msl_opts.check_discarded_frag_stores = args.msl_check_discarded_frag_stores;
1224 msl_opts.ios_support_base_vertex_instance = true;
1225 msl_comp->set_msl_options(msl_opts);
1226 for (auto &v : args.msl_discrete_descriptor_sets)
1227 msl_comp->add_discrete_descriptor_set(v);
1228 for (auto &v : args.msl_device_argument_buffers)
1229 msl_comp->set_argument_buffer_device_address_space(v, true);
1230 uint32_t i = 0;
1231 for (auto &v : args.msl_dynamic_buffers)
1232 msl_comp->add_dynamic_buffer(v.first, v.second, i++);
1233 for (auto &v : args.msl_inline_uniform_blocks)
1234 msl_comp->add_inline_uniform_block(v.first, v.second);
1235 for (auto &v : args.msl_shader_inputs)
1236 msl_comp->add_msl_shader_input(v);
1237 for (auto &v : args.msl_shader_outputs)
1238 msl_comp->add_msl_shader_output(v);
1239 if (args.msl_combined_sampler_suffix)
1240 msl_comp->set_combined_sampler_suffix(args.msl_combined_sampler_suffix);
1242 else if (args.hlsl)
1243 compiler.reset(new CompilerHLSL(std::move(spirv_parser.get_parsed_ir())));
1244 else
1246 combined_image_samplers = !args.vulkan_semantics;
1247 if (!args.vulkan_semantics || args.vulkan_glsl_disable_ext_samplerless_texture_functions)
1248 build_dummy_sampler = true;
1249 compiler.reset(new CompilerGLSL(std::move(spirv_parser.get_parsed_ir())));
1252 if (!args.variable_type_remaps.empty())
1254 auto remap_cb = [&](const SPIRType &, const string &name, string &out) -> void {
1255 for (const VariableTypeRemap &remap : args.variable_type_remaps)
1256 if (name == remap.variable_name)
1257 out = remap.new_variable_type;
1260 compiler->set_variable_type_remap_callback(std::move(remap_cb));
1263 for (auto &masked : args.masked_stage_outputs)
1264 compiler->mask_stage_output_by_location(masked.first, masked.second);
1265 for (auto &masked : args.masked_stage_builtins)
1266 compiler->mask_stage_output_by_builtin(masked);
1268 for (auto &rename : args.entry_point_rename)
1269 compiler->rename_entry_point(rename.old_name, rename.new_name, rename.execution_model);
1271 auto entry_points = compiler->get_entry_points_and_stages();
1272 auto entry_point = args.entry;
1273 ExecutionModel model = ExecutionModelMax;
1275 if (!args.entry_stage.empty())
1277 model = stage_to_execution_model(args.entry_stage);
1278 if (entry_point.empty())
1280 // Just use the first entry point with this stage.
1281 for (auto &e : entry_points)
1283 if (e.execution_model == model)
1285 entry_point = e.name;
1286 break;
1290 if (entry_point.empty())
1292 fprintf(stderr, "Could not find an entry point with stage: %s\n", args.entry_stage.c_str());
1293 exit(EXIT_FAILURE);
1296 else
1298 // Make sure both stage and name exists.
1299 bool exists = false;
1300 for (auto &e : entry_points)
1302 if (e.execution_model == model && e.name == entry_point)
1304 exists = true;
1305 break;
1309 if (!exists)
1311 fprintf(stderr, "Could not find an entry point %s with stage: %s\n", entry_point.c_str(),
1312 args.entry_stage.c_str());
1313 exit(EXIT_FAILURE);
1317 else if (!entry_point.empty())
1319 // Make sure there is just one entry point with this name, or the stage
1320 // is ambiguous.
1321 uint32_t stage_count = 0;
1322 for (auto &e : entry_points)
1324 if (e.name == entry_point)
1326 stage_count++;
1327 model = e.execution_model;
1331 if (stage_count == 0)
1333 fprintf(stderr, "There is no entry point with name: %s\n", entry_point.c_str());
1334 exit(EXIT_FAILURE);
1336 else if (stage_count > 1)
1338 fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", entry_point.c_str());
1339 exit(EXIT_FAILURE);
1343 if (!entry_point.empty())
1344 compiler->set_entry_point(entry_point, model);
1346 if (!args.set_version && !compiler->get_common_options().version)
1348 fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n");
1349 print_help();
1350 exit(EXIT_FAILURE);
1353 CompilerGLSL::Options opts = compiler->get_common_options();
1354 if (args.set_version)
1355 opts.version = args.version;
1356 if (args.set_es)
1357 opts.es = args.es;
1358 opts.force_temporary = args.force_temporary;
1359 opts.separate_shader_objects = args.sso;
1360 opts.flatten_multidimensional_arrays = args.flatten_multidimensional_arrays;
1361 opts.enable_420pack_extension = args.use_420pack_extension;
1362 opts.vulkan_semantics = args.vulkan_semantics;
1363 opts.vertex.fixup_clipspace = args.fixup;
1364 opts.vertex.flip_vert_y = args.yflip;
1365 opts.vertex.support_nonzero_base_instance = args.support_nonzero_baseinstance;
1366 opts.emit_push_constant_as_uniform_buffer = args.glsl_emit_push_constant_as_ubo;
1367 opts.emit_uniform_buffer_as_plain_uniforms = args.glsl_emit_ubo_as_plain_uniforms;
1368 opts.force_flattened_io_blocks = args.glsl_force_flattened_io_blocks;
1369 opts.ovr_multiview_view_count = args.glsl_ovr_multiview_view_count;
1370 opts.emit_line_directives = args.emit_line_directives;
1371 opts.enable_storage_image_qualifier_deduction = args.enable_storage_image_qualifier_deduction;
1372 opts.force_zero_initialized_variables = args.force_zero_initialized_variables;
1373 opts.relax_nan_checks = args.relax_nan_checks;
1374 opts.force_recompile_max_debug_iterations = args.force_recompile_max_debug_iterations;
1375 compiler->set_common_options(opts);
1377 for (auto &fetch : args.glsl_ext_framebuffer_fetch)
1378 compiler->remap_ext_framebuffer_fetch(fetch.first, fetch.second, !args.glsl_ext_framebuffer_fetch_noncoherent);
1380 // Set HLSL specific options.
1381 if (args.hlsl)
1383 auto *hlsl = static_cast<CompilerHLSL *>(compiler.get());
1384 auto hlsl_opts = hlsl->get_hlsl_options();
1385 if (args.set_shader_model)
1387 if (args.shader_model < 30)
1389 fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n");
1390 exit(EXIT_FAILURE);
1393 hlsl_opts.shader_model = args.shader_model;
1396 if (args.hlsl_compat)
1398 // Enable all compat options.
1399 hlsl_opts.point_size_compat = true;
1400 hlsl_opts.point_coord_compat = true;
1403 if (hlsl_opts.shader_model <= 30)
1405 combined_image_samplers = true;
1406 build_dummy_sampler = true;
1409 // If we're explicitly renaming, we probably want that name to be output.
1410 if (!args.entry_point_rename.empty())
1411 hlsl_opts.use_entry_point_name = true;
1413 hlsl_opts.support_nonzero_base_vertex_base_instance = args.hlsl_support_nonzero_base;
1414 hlsl_opts.force_storage_buffer_as_uav = args.hlsl_force_storage_buffer_as_uav;
1415 hlsl_opts.nonwritable_uav_texture_as_srv = args.hlsl_nonwritable_uav_texture_as_srv;
1416 hlsl_opts.enable_16bit_types = args.hlsl_enable_16bit_types;
1417 hlsl_opts.flatten_matrix_vertex_input_semantics = args.hlsl_flatten_matrix_vertex_input_semantics;
1418 hlsl->set_hlsl_options(hlsl_opts);
1419 hlsl->set_resource_binding_flags(args.hlsl_binding_flags);
1420 if (args.hlsl_base_vertex_index_explicit_binding)
1422 hlsl->set_hlsl_aux_buffer_binding(HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE,
1423 args.hlsl_base_vertex_index_register_index,
1424 args.hlsl_base_vertex_index_register_space);
1428 if (build_dummy_sampler)
1430 uint32_t sampler = compiler->build_dummy_sampler_for_combined_images();
1431 if (sampler != 0)
1433 // Set some defaults to make validation happy.
1434 compiler->set_decoration(sampler, DecorationDescriptorSet, 0);
1435 compiler->set_decoration(sampler, DecorationBinding, 0);
1439 ShaderResources res;
1440 if (args.remove_unused)
1442 auto active = compiler->get_active_interface_variables();
1443 res = compiler->get_shader_resources(active);
1444 compiler->set_enabled_interface_variables(std::move(active));
1446 else
1447 res = compiler->get_shader_resources();
1449 if (args.flatten_ubo)
1451 for (auto &ubo : res.uniform_buffers)
1452 compiler->flatten_buffer_block(ubo.id);
1453 for (auto &ubo : res.push_constant_buffers)
1454 compiler->flatten_buffer_block(ubo.id);
1457 auto pls_inputs = remap_pls(args.pls_in, res.stage_inputs, &res.subpass_inputs);
1458 auto pls_outputs = remap_pls(args.pls_out, res.stage_outputs, nullptr);
1459 compiler->remap_pixel_local_storage(std::move(pls_inputs), std::move(pls_outputs));
1461 for (auto &ext : args.extensions)
1462 compiler->require_extension(ext);
1464 for (auto &remap : args.remaps)
1466 if (remap_generic(*compiler, res.stage_inputs, remap))
1467 continue;
1468 if (remap_generic(*compiler, res.stage_outputs, remap))
1469 continue;
1470 if (remap_generic(*compiler, res.subpass_inputs, remap))
1471 continue;
1474 for (auto &rename : args.interface_variable_renames)
1476 if (rename.storageClass == StorageClassInput)
1477 spirv_cross_util::rename_interface_variable(*compiler, res.stage_inputs, rename.location,
1478 rename.variable_name);
1479 else if (rename.storageClass == StorageClassOutput)
1480 spirv_cross_util::rename_interface_variable(*compiler, res.stage_outputs, rename.location,
1481 rename.variable_name);
1482 else
1484 fprintf(stderr, "error at --rename-interface-variable <in|out> ...\n");
1485 exit(EXIT_FAILURE);
1489 if (combined_image_samplers)
1491 compiler->build_combined_image_samplers();
1492 if (args.combined_samplers_inherit_bindings)
1493 spirv_cross_util::inherit_combined_sampler_bindings(*compiler);
1495 // Give the remapped combined samplers new names.
1496 for (auto &remap : compiler->get_combined_image_samplers())
1498 compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id),
1499 compiler->get_name(remap.sampler_id)));
1503 if (args.hlsl)
1505 auto *hlsl_compiler = static_cast<CompilerHLSL *>(compiler.get());
1506 hlsl_compiler->remap_num_workgroups_builtin();
1509 if (args.hlsl)
1511 for (auto &remap : args.hlsl_attr_remap)
1512 static_cast<CompilerHLSL *>(compiler.get())->add_vertex_attribute_remap(remap);
1514 for (auto &named_remap : args.hlsl_attr_remap_named)
1516 auto itr = std::find_if(res.stage_inputs.begin(), res.stage_inputs.end(), [&](const Resource &input_res) {
1517 return input_res.name == named_remap.name;
1520 if (itr != res.stage_inputs.end())
1522 HLSLVertexAttributeRemap remap = {
1523 compiler->get_decoration(itr->id, DecorationLocation),
1524 named_remap.semantic,
1526 static_cast<CompilerHLSL *>(compiler.get())->add_vertex_attribute_remap(remap);
1531 auto ret = compiler->compile();
1533 if (args.dump_resources)
1535 compiler->update_active_builtins();
1536 print_resources(*compiler, res);
1537 print_push_constant_resources(*compiler, res.push_constant_buffers);
1538 print_spec_constants(*compiler);
1539 print_capabilities_and_extensions(*compiler);
1542 return ret;
1545 static int main_inner(int argc, char *argv[])
1547 CLIArguments args;
1548 CLICallbacks cbs;
1550 cbs.add("--help", [](CLIParser &parser) {
1551 print_help();
1552 parser.end();
1554 cbs.add("--revision", [](CLIParser &parser) {
1555 print_version();
1556 parser.end();
1558 cbs.add("--output", [&args](CLIParser &parser) { args.output = parser.next_string(); });
1559 cbs.add("--es", [&args](CLIParser &) {
1560 args.es = true;
1561 args.set_es = true;
1563 cbs.add("--no-es", [&args](CLIParser &) {
1564 args.es = false;
1565 args.set_es = true;
1567 cbs.add("--version", [&args](CLIParser &parser) {
1568 args.version = parser.next_uint();
1569 args.set_version = true;
1571 cbs.add("--dump-resources", [&args](CLIParser &) { args.dump_resources = true; });
1572 cbs.add("--force-temporary", [&args](CLIParser &) { args.force_temporary = true; });
1573 cbs.add("--flatten-ubo", [&args](CLIParser &) { args.flatten_ubo = true; });
1574 cbs.add("--fixup-clipspace", [&args](CLIParser &) { args.fixup = true; });
1575 cbs.add("--flip-vert-y", [&args](CLIParser &) { args.yflip = true; });
1576 cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); });
1577 cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; });
1578 cbs.add("--reflect", [&args](CLIParser &parser) { args.reflect = parser.next_value_string("json"); });
1579 cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); });
1580 cbs.add("--metal", [&args](CLIParser &) { args.msl = true; }); // Legacy compatibility
1581 cbs.add("--glsl-emit-push-constant-as-ubo", [&args](CLIParser &) { args.glsl_emit_push_constant_as_ubo = true; });
1582 cbs.add("--glsl-emit-ubo-as-plain-uniforms", [&args](CLIParser &) { args.glsl_emit_ubo_as_plain_uniforms = true; });
1583 cbs.add("--glsl-force-flattened-io-blocks", [&args](CLIParser &) { args.glsl_force_flattened_io_blocks = true; });
1584 cbs.add("--glsl-ovr-multiview-view-count", [&args](CLIParser &parser) { args.glsl_ovr_multiview_view_count = parser.next_uint(); });
1585 cbs.add("--glsl-remap-ext-framebuffer-fetch", [&args](CLIParser &parser) {
1586 uint32_t input_index = parser.next_uint();
1587 uint32_t color_attachment = parser.next_uint();
1588 args.glsl_ext_framebuffer_fetch.push_back({ input_index, color_attachment });
1590 cbs.add("--glsl-ext-framebuffer-fetch-noncoherent", [&args](CLIParser &) {
1591 args.glsl_ext_framebuffer_fetch_noncoherent = true;
1593 cbs.add("--vulkan-glsl-disable-ext-samplerless-texture-functions",
1594 [&args](CLIParser &) { args.vulkan_glsl_disable_ext_samplerless_texture_functions = true; });
1595 cbs.add("--disable-storage-image-qualifier-deduction",
1596 [&args](CLIParser &) { args.enable_storage_image_qualifier_deduction = false; });
1597 cbs.add("--force-zero-initialized-variables",
1598 [&args](CLIParser &) { args.force_zero_initialized_variables = true; });
1599 cbs.add("--msl", [&args](CLIParser &) { args.msl = true; });
1600 cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; });
1601 cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; });
1602 cbs.add("--hlsl-support-nonzero-basevertex-baseinstance",
1603 [&args](CLIParser &) { args.hlsl_support_nonzero_base = true; });
1604 cbs.add("--hlsl-basevertex-baseinstance-binding", [&args](CLIParser &parser) {
1605 args.hlsl_base_vertex_index_explicit_binding = true;
1606 args.hlsl_base_vertex_index_register_index = parser.next_uint();
1607 args.hlsl_base_vertex_index_register_space = parser.next_uint();
1609 cbs.add("--hlsl-auto-binding", [&args](CLIParser &parser) {
1610 args.hlsl_binding_flags |= hlsl_resource_type_to_flag(parser.next_string());
1612 cbs.add("--hlsl-force-storage-buffer-as-uav",
1613 [&args](CLIParser &) { args.hlsl_force_storage_buffer_as_uav = true; });
1614 cbs.add("--hlsl-nonwritable-uav-texture-as-srv",
1615 [&args](CLIParser &) { args.hlsl_nonwritable_uav_texture_as_srv = true; });
1616 cbs.add("--hlsl-enable-16bit-types", [&args](CLIParser &) { args.hlsl_enable_16bit_types = true; });
1617 cbs.add("--hlsl-flatten-matrix-vertex-input-semantics",
1618 [&args](CLIParser &) { args.hlsl_flatten_matrix_vertex_input_semantics = true; });
1619 cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; });
1620 cbs.add("-V", [&args](CLIParser &) { args.vulkan_semantics = true; });
1621 cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; });
1622 cbs.add("--no-420pack-extension", [&args](CLIParser &) { args.use_420pack_extension = false; });
1623 cbs.add("--msl-capture-output", [&args](CLIParser &) { args.msl_capture_output_to_buffer = true; });
1624 cbs.add("--msl-swizzle-texture-samples", [&args](CLIParser &) { args.msl_swizzle_texture_samples = true; });
1625 cbs.add("--msl-ios", [&args](CLIParser &) { args.msl_ios = true; });
1626 cbs.add("--msl-pad-fragment-output", [&args](CLIParser &) { args.msl_pad_fragment_output = true; });
1627 cbs.add("--msl-domain-lower-left", [&args](CLIParser &) { args.msl_domain_lower_left = true; });
1628 cbs.add("--msl-argument-buffers", [&args](CLIParser &) { args.msl_argument_buffers = true; });
1629 cbs.add("--msl-argument-buffer-tier", [&args](CLIParser &parser) {
1630 args.msl_argument_buffers_tier = parser.next_uint();
1631 args.msl_argument_buffers = true;
1633 cbs.add("--msl-discrete-descriptor-set",
1634 [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); });
1635 cbs.add("--msl-device-argument-buffer",
1636 [&args](CLIParser &parser) { args.msl_device_argument_buffers.push_back(parser.next_uint()); });
1637 cbs.add("--msl-texture-buffer-native", [&args](CLIParser &) { args.msl_texture_buffer_native = true; });
1638 cbs.add("--msl-framebuffer-fetch", [&args](CLIParser &) { args.msl_framebuffer_fetch = true; });
1639 cbs.add("--msl-invariant-float-math", [&args](CLIParser &) { args.msl_invariant_float_math = true; });
1640 cbs.add("--msl-emulate-cube-array", [&args](CLIParser &) { args.msl_emulate_cube_array = true; });
1641 cbs.add("--msl-multiview", [&args](CLIParser &) { args.msl_multiview = true; });
1642 cbs.add("--msl-multiview-no-layered-rendering",
1643 [&args](CLIParser &) { args.msl_multiview_layered_rendering = false; });
1644 cbs.add("--msl-view-index-from-device-index",
1645 [&args](CLIParser &) { args.msl_view_index_from_device_index = true; });
1646 cbs.add("--msl-dispatch-base", [&args](CLIParser &) { args.msl_dispatch_base = true; });
1647 cbs.add("--msl-dynamic-buffer", [&args](CLIParser &parser) {
1648 args.msl_argument_buffers = true;
1649 // Make sure next_uint() is called in-order.
1650 uint32_t desc_set = parser.next_uint();
1651 uint32_t binding = parser.next_uint();
1652 args.msl_dynamic_buffers.push_back(make_pair(desc_set, binding));
1654 cbs.add("--msl-decoration-binding", [&args](CLIParser &) { args.msl_decoration_binding = true; });
1655 cbs.add("--msl-force-active-argument-buffer-resources",
1656 [&args](CLIParser &) { args.msl_force_active_argument_buffer_resources = true; });
1657 cbs.add("--msl-inline-uniform-block", [&args](CLIParser &parser) {
1658 args.msl_argument_buffers = true;
1659 // Make sure next_uint() is called in-order.
1660 uint32_t desc_set = parser.next_uint();
1661 uint32_t binding = parser.next_uint();
1662 args.msl_inline_uniform_blocks.push_back(make_pair(desc_set, binding));
1664 cbs.add("--msl-force-native-arrays", [&args](CLIParser &) { args.msl_force_native_arrays = true; });
1665 cbs.add("--msl-disable-frag-depth-builtin", [&args](CLIParser &) { args.msl_enable_frag_depth_builtin = false; });
1666 cbs.add("--msl-disable-frag-stencil-ref-builtin",
1667 [&args](CLIParser &) { args.msl_enable_frag_stencil_ref_builtin = false; });
1668 cbs.add("--msl-enable-frag-output-mask",
1669 [&args](CLIParser &parser) { args.msl_enable_frag_output_mask = parser.next_hex_uint(); });
1670 cbs.add("--msl-no-clip-distance-user-varying",
1671 [&args](CLIParser &) { args.msl_enable_clip_distance_user_varying = false; });
1672 cbs.add("--msl-add-shader-input", [&args](CLIParser &parser) {
1673 MSLShaderInterfaceVariable input;
1674 // Make sure next_uint() is called in-order.
1675 input.location = parser.next_uint();
1676 const char *format = parser.next_value_string("other");
1677 if (strcmp(format, "any32") == 0)
1678 input.format = MSL_SHADER_VARIABLE_FORMAT_ANY32;
1679 else if (strcmp(format, "any16") == 0)
1680 input.format = MSL_SHADER_VARIABLE_FORMAT_ANY16;
1681 else if (strcmp(format, "u16") == 0)
1682 input.format = MSL_SHADER_VARIABLE_FORMAT_UINT16;
1683 else if (strcmp(format, "u8") == 0)
1684 input.format = MSL_SHADER_VARIABLE_FORMAT_UINT8;
1685 else
1686 input.format = MSL_SHADER_VARIABLE_FORMAT_OTHER;
1687 input.vecsize = parser.next_uint();
1688 const char *rate = parser.next_value_string("vertex");
1689 if (strcmp(rate, "primitive") == 0)
1690 input.rate = MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE;
1691 else if (strcmp(rate, "patch") == 0)
1692 input.rate = MSL_SHADER_VARIABLE_RATE_PER_PATCH;
1693 else
1694 input.rate = MSL_SHADER_VARIABLE_RATE_PER_VERTEX;
1695 args.msl_shader_inputs.push_back(input);
1697 cbs.add("--msl-add-shader-output", [&args](CLIParser &parser) {
1698 MSLShaderInterfaceVariable output;
1699 // Make sure next_uint() is called in-order.
1700 output.location = parser.next_uint();
1701 const char *format = parser.next_value_string("other");
1702 if (strcmp(format, "any32") == 0)
1703 output.format = MSL_SHADER_VARIABLE_FORMAT_ANY32;
1704 else if (strcmp(format, "any16") == 0)
1705 output.format = MSL_SHADER_VARIABLE_FORMAT_ANY16;
1706 else if (strcmp(format, "u16") == 0)
1707 output.format = MSL_SHADER_VARIABLE_FORMAT_UINT16;
1708 else if (strcmp(format, "u8") == 0)
1709 output.format = MSL_SHADER_VARIABLE_FORMAT_UINT8;
1710 else
1711 output.format = MSL_SHADER_VARIABLE_FORMAT_OTHER;
1712 output.vecsize = parser.next_uint();
1713 const char *rate = parser.next_value_string("vertex");
1714 if (strcmp(rate, "primitive") == 0)
1715 output.rate = MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE;
1716 else if (strcmp(rate, "patch") == 0)
1717 output.rate = MSL_SHADER_VARIABLE_RATE_PER_PATCH;
1718 else
1719 output.rate = MSL_SHADER_VARIABLE_RATE_PER_VERTEX;
1720 args.msl_shader_outputs.push_back(output);
1722 cbs.add("--msl-shader-input", [&args](CLIParser &parser) {
1723 MSLShaderInterfaceVariable input;
1724 // Make sure next_uint() is called in-order.
1725 input.location = parser.next_uint();
1726 const char *format = parser.next_value_string("other");
1727 if (strcmp(format, "any32") == 0)
1728 input.format = MSL_SHADER_VARIABLE_FORMAT_ANY32;
1729 else if (strcmp(format, "any16") == 0)
1730 input.format = MSL_SHADER_VARIABLE_FORMAT_ANY16;
1731 else if (strcmp(format, "u16") == 0)
1732 input.format = MSL_SHADER_VARIABLE_FORMAT_UINT16;
1733 else if (strcmp(format, "u8") == 0)
1734 input.format = MSL_SHADER_VARIABLE_FORMAT_UINT8;
1735 else
1736 input.format = MSL_SHADER_VARIABLE_FORMAT_OTHER;
1737 input.vecsize = parser.next_uint();
1738 args.msl_shader_inputs.push_back(input);
1740 cbs.add("--msl-shader-output", [&args](CLIParser &parser) {
1741 MSLShaderInterfaceVariable output;
1742 // Make sure next_uint() is called in-order.
1743 output.location = parser.next_uint();
1744 const char *format = parser.next_value_string("other");
1745 if (strcmp(format, "any32") == 0)
1746 output.format = MSL_SHADER_VARIABLE_FORMAT_ANY32;
1747 else if (strcmp(format, "any16") == 0)
1748 output.format = MSL_SHADER_VARIABLE_FORMAT_ANY16;
1749 else if (strcmp(format, "u16") == 0)
1750 output.format = MSL_SHADER_VARIABLE_FORMAT_UINT16;
1751 else if (strcmp(format, "u8") == 0)
1752 output.format = MSL_SHADER_VARIABLE_FORMAT_UINT8;
1753 else
1754 output.format = MSL_SHADER_VARIABLE_FORMAT_OTHER;
1755 output.vecsize = parser.next_uint();
1756 args.msl_shader_outputs.push_back(output);
1758 cbs.add("--msl-raw-buffer-tese-input", [&args](CLIParser &) { args.msl_raw_buffer_tese_input = true; });
1759 cbs.add("--msl-multi-patch-workgroup", [&args](CLIParser &) { args.msl_multi_patch_workgroup = true; });
1760 cbs.add("--msl-vertex-for-tessellation", [&args](CLIParser &) { args.msl_vertex_for_tessellation = true; });
1761 cbs.add("--msl-additional-fixed-sample-mask",
1762 [&args](CLIParser &parser) { args.msl_additional_fixed_sample_mask = parser.next_hex_uint(); });
1763 cbs.add("--msl-arrayed-subpass-input", [&args](CLIParser &) { args.msl_arrayed_subpass_input = true; });
1764 cbs.add("--msl-r32ui-linear-texture-align",
1765 [&args](CLIParser &parser) { args.msl_r32ui_linear_texture_alignment = parser.next_uint(); });
1766 cbs.add("--msl-r32ui-linear-texture-align-constant-id",
1767 [&args](CLIParser &parser) { args.msl_r32ui_alignment_constant_id = parser.next_uint(); });
1768 cbs.add("--msl-texture-1d-as-2d", [&args](CLIParser &) { args.msl_texture_1d_as_2d = true; });
1769 cbs.add("--msl-ios-use-simdgroup-functions", [&args](CLIParser &) { args.msl_ios_use_simdgroup_functions = true; });
1770 cbs.add("--msl-emulate-subgroups", [&args](CLIParser &) { args.msl_emulate_subgroups = true; });
1771 cbs.add("--msl-fixed-subgroup-size",
1772 [&args](CLIParser &parser) { args.msl_fixed_subgroup_size = parser.next_uint(); });
1773 cbs.add("--msl-force-sample-rate-shading", [&args](CLIParser &) { args.msl_force_sample_rate_shading = true; });
1774 cbs.add("--msl-no-manual-helper-invocation-updates",
1775 [&args](CLIParser &) { args.msl_manual_helper_invocation_updates = false; });
1776 cbs.add("--msl-check-discarded-frag-stores", [&args](CLIParser &) { args.msl_check_discarded_frag_stores = true; });
1777 cbs.add("--msl-combined-sampler-suffix", [&args](CLIParser &parser) {
1778 args.msl_combined_sampler_suffix = parser.next_string();
1780 cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
1781 cbs.add("--rename-entry-point", [&args](CLIParser &parser) {
1782 auto old_name = parser.next_string();
1783 auto new_name = parser.next_string();
1784 auto model = stage_to_execution_model(parser.next_string());
1785 args.entry_point_rename.push_back({ old_name, new_name, std::move(model) });
1787 cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); });
1788 cbs.add("--stage", [&args](CLIParser &parser) { args.entry_stage = parser.next_string(); });
1789 cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; });
1790 cbs.add("--set-hlsl-vertex-input-semantic", [&args](CLIParser &parser) {
1791 HLSLVertexAttributeRemap remap;
1792 remap.location = parser.next_uint();
1793 remap.semantic = parser.next_string();
1794 args.hlsl_attr_remap.push_back(std::move(remap));
1796 cbs.add("--set-hlsl-named-vertex-input-semantic", [&args](CLIParser &parser) {
1797 HLSLVertexAttributeRemapNamed remap;
1798 remap.name = parser.next_string();
1799 remap.semantic = parser.next_string();
1800 args.hlsl_attr_remap_named.push_back(std::move(remap));
1803 cbs.add("--remap", [&args](CLIParser &parser) {
1804 string src = parser.next_string();
1805 string dst = parser.next_string();
1806 uint32_t components = parser.next_uint();
1807 args.remaps.push_back({ std::move(src), std::move(dst), components });
1810 cbs.add("--remap-variable-type", [&args](CLIParser &parser) {
1811 string var_name = parser.next_string();
1812 string new_type = parser.next_string();
1813 args.variable_type_remaps.push_back({ std::move(var_name), std::move(new_type) });
1816 cbs.add("--rename-interface-variable", [&args](CLIParser &parser) {
1817 StorageClass cls = StorageClassMax;
1818 string clsStr = parser.next_string();
1819 if (clsStr == "in")
1820 cls = StorageClassInput;
1821 else if (clsStr == "out")
1822 cls = StorageClassOutput;
1824 uint32_t loc = parser.next_uint();
1825 string var_name = parser.next_string();
1826 args.interface_variable_renames.push_back({ cls, loc, std::move(var_name) });
1829 cbs.add("--pls-in", [&args](CLIParser &parser) {
1830 auto fmt = pls_format(parser.next_string());
1831 auto name = parser.next_string();
1832 args.pls_in.push_back({ std::move(fmt), std::move(name) });
1834 cbs.add("--pls-out", [&args](CLIParser &parser) {
1835 auto fmt = pls_format(parser.next_string());
1836 auto name = parser.next_string();
1837 args.pls_out.push_back({ std::move(fmt), std::move(name) });
1839 cbs.add("--shader-model", [&args](CLIParser &parser) {
1840 args.shader_model = parser.next_uint();
1841 args.set_shader_model = true;
1843 cbs.add("--msl-version", [&args](CLIParser &parser) {
1844 args.msl_version = parser.next_uint();
1845 args.set_msl_version = true;
1848 cbs.add("--remove-unused-variables", [&args](CLIParser &) { args.remove_unused = true; });
1849 cbs.add("--combined-samplers-inherit-bindings",
1850 [&args](CLIParser &) { args.combined_samplers_inherit_bindings = true; });
1852 cbs.add("--no-support-nonzero-baseinstance", [&](CLIParser &) { args.support_nonzero_baseinstance = false; });
1853 cbs.add("--emit-line-directives", [&args](CLIParser &) { args.emit_line_directives = true; });
1855 cbs.add("--mask-stage-output-location", [&](CLIParser &parser) {
1856 uint32_t location = parser.next_uint();
1857 uint32_t component = parser.next_uint();
1858 args.masked_stage_outputs.push_back({ location, component });
1861 cbs.add("--mask-stage-output-builtin", [&](CLIParser &parser) {
1862 BuiltIn masked_builtin = BuiltInMax;
1863 std::string builtin = parser.next_string();
1864 if (builtin == "Position")
1865 masked_builtin = BuiltInPosition;
1866 else if (builtin == "PointSize")
1867 masked_builtin = BuiltInPointSize;
1868 else if (builtin == "CullDistance")
1869 masked_builtin = BuiltInCullDistance;
1870 else if (builtin == "ClipDistance")
1871 masked_builtin = BuiltInClipDistance;
1872 else
1874 print_help();
1875 exit(EXIT_FAILURE);
1877 args.masked_stage_builtins.push_back(masked_builtin);
1880 cbs.add("--force-recompile-max-debug-iterations", [&](CLIParser &parser) {
1881 args.force_recompile_max_debug_iterations = parser.next_uint();
1884 cbs.add("--relax-nan-checks", [&](CLIParser &) { args.relax_nan_checks = true; });
1886 cbs.default_handler = [&args](const char *value) { args.input = value; };
1887 cbs.add("-", [&args](CLIParser &) { args.input = "-"; });
1888 cbs.error_handler = [] { print_help(); };
1890 CLIParser parser{ std::move(cbs), argc - 1, argv + 1 };
1891 if (!parser.parse())
1892 return EXIT_FAILURE;
1893 else if (parser.ended_state)
1894 return EXIT_SUCCESS;
1896 if (!args.input)
1898 fprintf(stderr, "Didn't specify input file.\n");
1899 print_help();
1900 return EXIT_FAILURE;
1903 auto spirv_file = read_spirv_file(args.input);
1904 if (spirv_file.empty())
1905 return EXIT_FAILURE;
1907 // Special case reflection because it has little to do with the path followed by code-outputting compilers
1908 if (!args.reflect.empty())
1910 Parser spirv_parser(std::move(spirv_file));
1911 spirv_parser.parse();
1913 CompilerReflection compiler(std::move(spirv_parser.get_parsed_ir()));
1914 compiler.set_format(args.reflect);
1915 auto json = compiler.compile();
1916 if (args.output)
1917 write_string_to_file(args.output, json.c_str());
1918 else
1919 printf("%s", json.c_str());
1920 return EXIT_SUCCESS;
1923 string compiled_output;
1925 if (args.iterations == 1)
1926 compiled_output = compile_iteration(args, std::move(spirv_file));
1927 else
1929 for (unsigned i = 0; i < args.iterations; i++)
1930 compiled_output = compile_iteration(args, spirv_file);
1933 if (args.output)
1934 write_string_to_file(args.output, compiled_output.c_str());
1935 else
1936 printf("%s", compiled_output.c_str());
1938 return EXIT_SUCCESS;
1941 int main(int argc, char *argv[])
1943 #ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
1944 return main_inner(argc, argv);
1945 #else
1946 // Make sure we catch the exception or it just disappears into the aether on Windows.
1949 return main_inner(argc, argv);
1951 catch (const std::exception &e)
1953 fprintf(stderr, "SPIRV-Cross threw an exception: %s\n", e.what());
1954 return EXIT_FAILURE;
1956 #endif