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