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