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