1 /*
2  * Copyright 2015-2019 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 #ifdef _MSC_VER
39 #pragma warning(disable : 4996)
40 #endif
41 
42 using namespace spv;
43 using namespace SPIRV_CROSS_NAMESPACE;
44 using namespace std;
45 
46 #ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
THROW(const char * str)47 static inline void THROW(const char *str)
48 {
49 	fprintf(stderr, "SPIRV-Cross will abort: %s\n", str);
50 	fflush(stderr);
51 	abort();
52 }
53 #else
54 #define THROW(x) throw runtime_error(x)
55 #endif
56 
57 struct CLIParser;
58 struct CLICallbacks
59 {
addCLICallbacks60 	void add(const char *cli, const function<void(CLIParser &)> &func)
61 	{
62 		callbacks[cli] = func;
63 	}
64 	unordered_map<string, function<void(CLIParser &)>> callbacks;
65 	function<void()> error_handler;
66 	function<void(const char *)> default_handler;
67 };
68 
69 struct CLIParser
70 {
CLIParserCLIParser71 	CLIParser(CLICallbacks cbs_, int argc_, char *argv_[])
72 	    : cbs(move(cbs_))
73 	    , argc(argc_)
74 	    , argv(argv_)
75 	{
76 	}
77 
parseCLIParser78 	bool parse()
79 	{
80 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
81 		try
82 #endif
83 		{
84 			while (argc && !ended_state)
85 			{
86 				const char *next = *argv++;
87 				argc--;
88 
89 				if (*next != '-' && cbs.default_handler)
90 				{
91 					cbs.default_handler(next);
92 				}
93 				else
94 				{
95 					auto itr = cbs.callbacks.find(next);
96 					if (itr == ::end(cbs.callbacks))
97 					{
98 						THROW("Invalid argument");
99 					}
100 
101 					itr->second(*this);
102 				}
103 			}
104 
105 			return true;
106 		}
107 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
108 		catch (...)
109 		{
110 			if (cbs.error_handler)
111 			{
112 				cbs.error_handler();
113 			}
114 			return false;
115 		}
116 #endif
117 	}
118 
endCLIParser119 	void end()
120 	{
121 		ended_state = true;
122 	}
123 
next_uintCLIParser124 	uint32_t next_uint()
125 	{
126 		if (!argc)
127 		{
128 			THROW("Tried to parse uint, but nothing left in arguments");
129 		}
130 
131 		uint64_t val = stoul(*argv);
132 		if (val > numeric_limits<uint32_t>::max())
133 		{
134 			THROW("next_uint() out of range");
135 		}
136 
137 		argc--;
138 		argv++;
139 
140 		return uint32_t(val);
141 	}
142 
next_doubleCLIParser143 	double next_double()
144 	{
145 		if (!argc)
146 		{
147 			THROW("Tried to parse double, but nothing left in arguments");
148 		}
149 
150 		double val = stod(*argv);
151 
152 		argc--;
153 		argv++;
154 
155 		return val;
156 	}
157 
158 	// Return a string only if it's not prefixed with `--`, otherwise return the default value
next_value_stringCLIParser159 	const char *next_value_string(const char *default_value)
160 	{
161 		if (!argc)
162 		{
163 			return default_value;
164 		}
165 
166 		if (0 == strncmp("--", *argv, 2))
167 		{
168 			return default_value;
169 		}
170 
171 		return next_string();
172 	}
173 
next_stringCLIParser174 	const char *next_string()
175 	{
176 		if (!argc)
177 		{
178 			THROW("Tried to parse string, but nothing left in arguments");
179 		}
180 
181 		const char *ret = *argv;
182 		argc--;
183 		argv++;
184 		return ret;
185 	}
186 
187 	CLICallbacks cbs;
188 	int argc;
189 	char **argv;
190 	bool ended_state = false;
191 };
192 
read_spirv_file(const char * path)193 static vector<uint32_t> read_spirv_file(const char *path)
194 {
195 	FILE *file = fopen(path, "rb");
196 	if (!file)
197 	{
198 		fprintf(stderr, "Failed to open SPIR-V file: %s\n", path);
199 		return {};
200 	}
201 
202 	fseek(file, 0, SEEK_END);
203 	long len = ftell(file) / sizeof(uint32_t);
204 	rewind(file);
205 
206 	vector<uint32_t> spirv(len);
207 	if (fread(spirv.data(), sizeof(uint32_t), len, file) != size_t(len))
208 		spirv.clear();
209 
210 	fclose(file);
211 	return spirv;
212 }
213 
write_string_to_file(const char * path,const char * string)214 static bool write_string_to_file(const char *path, const char *string)
215 {
216 	FILE *file = fopen(path, "w");
217 	if (!file)
218 	{
219 		fprintf(stderr, "Failed to write file: %s\n", path);
220 		return false;
221 	}
222 
223 	fprintf(file, "%s", string);
224 	fclose(file);
225 	return true;
226 }
227 
print_resources(const Compiler & compiler,const char * tag,const SmallVector<Resource> & resources)228 static void print_resources(const Compiler &compiler, const char *tag, const SmallVector<Resource> &resources)
229 {
230 	fprintf(stderr, "%s\n", tag);
231 	fprintf(stderr, "=============\n\n");
232 	bool print_ssbo = !strcmp(tag, "ssbos");
233 
234 	for (auto &res : resources)
235 	{
236 		auto &type = compiler.get_type(res.type_id);
237 
238 		if (print_ssbo && compiler.buffer_is_hlsl_counter_buffer(res.id))
239 			continue;
240 
241 		// If we don't have a name, use the fallback for the type instead of the variable
242 		// for SSBOs and UBOs since those are the only meaningful names to use externally.
243 		// Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
244 		bool is_push_constant = compiler.get_storage_class(res.id) == StorageClassPushConstant;
245 		bool is_block = compiler.get_decoration_bitset(type.self).get(DecorationBlock) ||
246 		                compiler.get_decoration_bitset(type.self).get(DecorationBufferBlock);
247 		bool is_sized_block = is_block && (compiler.get_storage_class(res.id) == StorageClassUniform ||
248 		                                   compiler.get_storage_class(res.id) == StorageClassUniformConstant);
249 		ID fallback_id = !is_push_constant && is_block ? ID(res.base_type_id) : ID(res.id);
250 
251 		uint32_t block_size = 0;
252 		uint32_t runtime_array_stride = 0;
253 		if (is_sized_block)
254 		{
255 			auto &base_type = compiler.get_type(res.base_type_id);
256 			block_size = uint32_t(compiler.get_declared_struct_size(base_type));
257 			runtime_array_stride = uint32_t(compiler.get_declared_struct_size_runtime_array(base_type, 1) -
258 			                                compiler.get_declared_struct_size_runtime_array(base_type, 0));
259 		}
260 
261 		Bitset mask;
262 		if (print_ssbo)
263 			mask = compiler.get_buffer_block_flags(res.id);
264 		else
265 			mask = compiler.get_decoration_bitset(res.id);
266 
267 		string array;
268 		for (auto arr : type.array)
269 			array = join("[", arr ? convert_to_string(arr) : "", "]") + array;
270 
271 		fprintf(stderr, " ID %03u : %s%s", uint32_t(res.id),
272 		        !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str(), array.c_str());
273 
274 		if (mask.get(DecorationLocation))
275 			fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation));
276 		if (mask.get(DecorationDescriptorSet))
277 			fprintf(stderr, " (Set : %u)", compiler.get_decoration(res.id, DecorationDescriptorSet));
278 		if (mask.get(DecorationBinding))
279 			fprintf(stderr, " (Binding : %u)", compiler.get_decoration(res.id, DecorationBinding));
280 		if (mask.get(DecorationInputAttachmentIndex))
281 			fprintf(stderr, " (Attachment : %u)", compiler.get_decoration(res.id, DecorationInputAttachmentIndex));
282 		if (mask.get(DecorationNonReadable))
283 			fprintf(stderr, " writeonly");
284 		if (mask.get(DecorationNonWritable))
285 			fprintf(stderr, " readonly");
286 		if (is_sized_block)
287 		{
288 			fprintf(stderr, " (BlockSize : %u bytes)", block_size);
289 			if (runtime_array_stride)
290 				fprintf(stderr, " (Unsized array stride: %u bytes)", runtime_array_stride);
291 		}
292 
293 		uint32_t counter_id = 0;
294 		if (print_ssbo && compiler.buffer_get_hlsl_counter_buffer(res.id, counter_id))
295 			fprintf(stderr, " (HLSL counter buffer ID: %u)", counter_id);
296 		fprintf(stderr, "\n");
297 	}
298 	fprintf(stderr, "=============\n\n");
299 }
300 
execution_model_to_str(spv::ExecutionModel model)301 static const char *execution_model_to_str(spv::ExecutionModel model)
302 {
303 	switch (model)
304 	{
305 	case spv::ExecutionModelVertex:
306 		return "vertex";
307 	case spv::ExecutionModelTessellationControl:
308 		return "tessellation control";
309 	case ExecutionModelTessellationEvaluation:
310 		return "tessellation evaluation";
311 	case ExecutionModelGeometry:
312 		return "geometry";
313 	case ExecutionModelFragment:
314 		return "fragment";
315 	case ExecutionModelGLCompute:
316 		return "compute";
317 	case ExecutionModelRayGenerationNV:
318 		return "raygenNV";
319 	case ExecutionModelIntersectionNV:
320 		return "intersectionNV";
321 	case ExecutionModelCallableNV:
322 		return "callableNV";
323 	case ExecutionModelAnyHitNV:
324 		return "anyhitNV";
325 	case ExecutionModelClosestHitNV:
326 		return "closesthitNV";
327 	case ExecutionModelMissNV:
328 		return "missNV";
329 	default:
330 		return "???";
331 	}
332 }
333 
print_resources(const Compiler & compiler,const ShaderResources & res)334 static void print_resources(const Compiler &compiler, const ShaderResources &res)
335 {
336 	auto &modes = compiler.get_execution_mode_bitset();
337 
338 	fprintf(stderr, "Entry points:\n");
339 	auto entry_points = compiler.get_entry_points_and_stages();
340 	for (auto &e : entry_points)
341 		fprintf(stderr, "  %s (%s)\n", e.name.c_str(), execution_model_to_str(e.execution_model));
342 	fprintf(stderr, "\n");
343 
344 	fprintf(stderr, "Execution modes:\n");
345 	modes.for_each_bit([&](uint32_t i) {
346 		auto mode = static_cast<ExecutionMode>(i);
347 		uint32_t arg0 = compiler.get_execution_mode_argument(mode, 0);
348 		uint32_t arg1 = compiler.get_execution_mode_argument(mode, 1);
349 		uint32_t arg2 = compiler.get_execution_mode_argument(mode, 2);
350 
351 		switch (static_cast<ExecutionMode>(i))
352 		{
353 		case ExecutionModeInvocations:
354 			fprintf(stderr, "  Invocations: %u\n", arg0);
355 			break;
356 
357 		case ExecutionModeLocalSize:
358 			fprintf(stderr, "  LocalSize: (%u, %u, %u)\n", arg0, arg1, arg2);
359 			break;
360 
361 		case ExecutionModeOutputVertices:
362 			fprintf(stderr, "  OutputVertices: %u\n", arg0);
363 			break;
364 
365 #define CHECK_MODE(m)                  \
366 	case ExecutionMode##m:             \
367 		fprintf(stderr, "  %s\n", #m); \
368 		break
369 			CHECK_MODE(SpacingEqual);
370 			CHECK_MODE(SpacingFractionalEven);
371 			CHECK_MODE(SpacingFractionalOdd);
372 			CHECK_MODE(VertexOrderCw);
373 			CHECK_MODE(VertexOrderCcw);
374 			CHECK_MODE(PixelCenterInteger);
375 			CHECK_MODE(OriginUpperLeft);
376 			CHECK_MODE(OriginLowerLeft);
377 			CHECK_MODE(EarlyFragmentTests);
378 			CHECK_MODE(PointMode);
379 			CHECK_MODE(Xfb);
380 			CHECK_MODE(DepthReplacing);
381 			CHECK_MODE(DepthGreater);
382 			CHECK_MODE(DepthLess);
383 			CHECK_MODE(DepthUnchanged);
384 			CHECK_MODE(LocalSizeHint);
385 			CHECK_MODE(InputPoints);
386 			CHECK_MODE(InputLines);
387 			CHECK_MODE(InputLinesAdjacency);
388 			CHECK_MODE(Triangles);
389 			CHECK_MODE(InputTrianglesAdjacency);
390 			CHECK_MODE(Quads);
391 			CHECK_MODE(Isolines);
392 			CHECK_MODE(OutputPoints);
393 			CHECK_MODE(OutputLineStrip);
394 			CHECK_MODE(OutputTriangleStrip);
395 			CHECK_MODE(VecTypeHint);
396 			CHECK_MODE(ContractionOff);
397 
398 		default:
399 			break;
400 		}
401 	});
402 	fprintf(stderr, "\n");
403 
404 	print_resources(compiler, "subpass inputs", res.subpass_inputs);
405 	print_resources(compiler, "inputs", res.stage_inputs);
406 	print_resources(compiler, "outputs", res.stage_outputs);
407 	print_resources(compiler, "textures", res.sampled_images);
408 	print_resources(compiler, "separate images", res.separate_images);
409 	print_resources(compiler, "separate samplers", res.separate_samplers);
410 	print_resources(compiler, "images", res.storage_images);
411 	print_resources(compiler, "ssbos", res.storage_buffers);
412 	print_resources(compiler, "ubos", res.uniform_buffers);
413 	print_resources(compiler, "push", res.push_constant_buffers);
414 	print_resources(compiler, "counters", res.atomic_counters);
415 	print_resources(compiler, "acceleration structures", res.acceleration_structures);
416 }
417 
print_push_constant_resources(const Compiler & compiler,const SmallVector<Resource> & res)418 static void print_push_constant_resources(const Compiler &compiler, const SmallVector<Resource> &res)
419 {
420 	for (auto &block : res)
421 	{
422 		auto ranges = compiler.get_active_buffer_ranges(block.id);
423 		fprintf(stderr, "Active members in buffer: %s\n",
424 		        !block.name.empty() ? block.name.c_str() : compiler.get_fallback_name(block.id).c_str());
425 
426 		fprintf(stderr, "==================\n\n");
427 		for (auto &range : ranges)
428 		{
429 			const auto &name = compiler.get_member_name(block.base_type_id, range.index);
430 
431 			fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index,
432 			        !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(),
433 			        unsigned(range.offset), unsigned(range.range));
434 		}
435 		fprintf(stderr, "==================\n\n");
436 	}
437 }
438 
print_spec_constants(const Compiler & compiler)439 static void print_spec_constants(const Compiler &compiler)
440 {
441 	auto spec_constants = compiler.get_specialization_constants();
442 	fprintf(stderr, "Specialization constants\n");
443 	fprintf(stderr, "==================\n\n");
444 	for (auto &c : spec_constants)
445 		fprintf(stderr, "ID: %u, Spec ID: %u\n", uint32_t(c.id), c.constant_id);
446 	fprintf(stderr, "==================\n\n");
447 }
448 
print_capabilities_and_extensions(const Compiler & compiler)449 static void print_capabilities_and_extensions(const Compiler &compiler)
450 {
451 	fprintf(stderr, "Capabilities\n");
452 	fprintf(stderr, "============\n");
453 	for (auto &capability : compiler.get_declared_capabilities())
454 		fprintf(stderr, "Capability: %u\n", static_cast<unsigned>(capability));
455 	fprintf(stderr, "============\n\n");
456 
457 	fprintf(stderr, "Extensions\n");
458 	fprintf(stderr, "============\n");
459 	for (auto &ext : compiler.get_declared_extensions())
460 		fprintf(stderr, "Extension: %s\n", ext.c_str());
461 	fprintf(stderr, "============\n\n");
462 }
463 
464 struct PLSArg
465 {
466 	PlsFormat format;
467 	string name;
468 };
469 
470 struct Remap
471 {
472 	string src_name;
473 	string dst_name;
474 	unsigned components;
475 };
476 
477 struct VariableTypeRemap
478 {
479 	string variable_name;
480 	string new_variable_type;
481 };
482 
483 struct InterfaceVariableRename
484 {
485 	StorageClass storageClass;
486 	uint32_t location;
487 	string variable_name;
488 };
489 
490 struct CLIArguments
491 {
492 	const char *input = nullptr;
493 	const char *output = nullptr;
494 	const char *cpp_interface_name = nullptr;
495 	uint32_t version = 0;
496 	uint32_t shader_model = 0;
497 	uint32_t msl_version = 0;
498 	bool es = false;
499 	bool set_version = false;
500 	bool set_shader_model = false;
501 	bool set_msl_version = false;
502 	bool set_es = false;
503 	bool dump_resources = false;
504 	bool force_temporary = false;
505 	bool flatten_ubo = false;
506 	bool fixup = false;
507 	bool yflip = false;
508 	bool sso = false;
509 	bool support_nonzero_baseinstance = true;
510 	bool msl_capture_output_to_buffer = false;
511 	bool msl_swizzle_texture_samples = false;
512 	bool msl_ios = false;
513 	bool msl_pad_fragment_output = false;
514 	bool msl_domain_lower_left = false;
515 	bool msl_argument_buffers = false;
516 	bool msl_texture_buffer_native = false;
517 	bool msl_framebuffer_fetch = false;
518 	bool msl_invariant_float_math = false;
519 	bool msl_emulate_cube_array = false;
520 	bool msl_multiview = false;
521 	bool msl_view_index_from_device_index = false;
522 	bool msl_dispatch_base = false;
523 	bool glsl_emit_push_constant_as_ubo = false;
524 	bool glsl_emit_ubo_as_plain_uniforms = false;
525 	bool vulkan_glsl_disable_ext_samplerless_texture_functions = false;
526 	bool emit_line_directives = false;
527 	SmallVector<uint32_t> msl_discrete_descriptor_sets;
528 	SmallVector<uint32_t> msl_device_argument_buffers;
529 	SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers;
530 	SmallVector<PLSArg> pls_in;
531 	SmallVector<PLSArg> pls_out;
532 	SmallVector<Remap> remaps;
533 	SmallVector<string> extensions;
534 	SmallVector<VariableTypeRemap> variable_type_remaps;
535 	SmallVector<InterfaceVariableRename> interface_variable_renames;
536 	SmallVector<HLSLVertexAttributeRemap> hlsl_attr_remap;
537 	string entry;
538 	string entry_stage;
539 
540 	struct Rename
541 	{
542 		string old_name;
543 		string new_name;
544 		ExecutionModel execution_model;
545 	};
546 	SmallVector<Rename> entry_point_rename;
547 
548 	uint32_t iterations = 1;
549 	bool cpp = false;
550 	string reflect;
551 	bool msl = false;
552 	bool hlsl = false;
553 	bool hlsl_compat = false;
554 	bool hlsl_support_nonzero_base = false;
555 	bool vulkan_semantics = false;
556 	bool flatten_multidimensional_arrays = false;
557 	bool use_420pack_extension = true;
558 	bool remove_unused = false;
559 	bool combined_samplers_inherit_bindings = false;
560 };
561 
print_version()562 static void print_version()
563 {
564 #ifdef HAVE_SPIRV_CROSS_GIT_VERSION
565 	fprintf(stderr, "%s\n", SPIRV_CROSS_GIT_REVISION);
566 #else
567 	fprintf(stderr, "Git revision unknown. Build with CMake to create timestamp and revision info.\n");
568 #endif
569 }
570 
print_help()571 static void print_help()
572 {
573 	print_version();
574 
575 	fprintf(stderr, "Usage: spirv-cross\n"
576 	                "\t[--output <output path>]\n"
577 	                "\t[SPIR-V file]\n"
578 	                "\t[--es]\n"
579 	                "\t[--no-es]\n"
580 	                "\t[--version <GLSL version>]\n"
581 	                "\t[--dump-resources]\n"
582 	                "\t[--help]\n"
583 	                "\t[--revision]\n"
584 	                "\t[--force-temporary]\n"
585 	                "\t[--vulkan-semantics]\n"
586 	                "\t[--flatten-ubo]\n"
587 	                "\t[--fixup-clipspace]\n"
588 	                "\t[--flip-vert-y]\n"
589 	                "\t[--iterations iter]\n"
590 	                "\t[--cpp]\n"
591 	                "\t[--cpp-interface-name <name>]\n"
592 	                "\t[--glsl-emit-push-constant-as-ubo]\n"
593 	                "\t[--glsl-emit-ubo-as-plain-uniforms]\n"
594 	                "\t[--vulkan-glsl-disable-ext-samplerless-texture-functions]\n"
595 	                "\t[--msl]\n"
596 	                "\t[--msl-version <MMmmpp>]\n"
597 	                "\t[--msl-capture-output]\n"
598 	                "\t[--msl-swizzle-texture-samples]\n"
599 	                "\t[--msl-ios]\n"
600 	                "\t[--msl-pad-fragment-output]\n"
601 	                "\t[--msl-domain-lower-left]\n"
602 	                "\t[--msl-argument-buffers]\n"
603 	                "\t[--msl-texture-buffer-native]\n"
604 	                "\t[--msl-framebuffer-fetch]\n"
605 	                "\t[--msl-emulate-cube-array]\n"
606 	                "\t[--msl-discrete-descriptor-set <index>]\n"
607 	                "\t[--msl-device-argument-buffer <index>]\n"
608 	                "\t[--msl-multiview]\n"
609 	                "\t[--msl-view-index-from-device-index]\n"
610 	                "\t[--msl-dispatch-base]\n"
611 	                "\t[--msl-dynamic-buffer <set index> <binding>]\n"
612 	                "\t[--hlsl]\n"
613 	                "\t[--reflect]\n"
614 	                "\t[--shader-model]\n"
615 	                "\t[--hlsl-enable-compat]\n"
616 	                "\t[--hlsl-support-nonzero-basevertex-baseinstance]\n"
617 	                "\t[--separate-shader-objects]\n"
618 	                "\t[--pls-in format input-name]\n"
619 	                "\t[--pls-out format output-name]\n"
620 	                "\t[--remap source_name target_name components]\n"
621 	                "\t[--extension ext]\n"
622 	                "\t[--entry name]\n"
623 	                "\t[--stage <stage (vert, frag, geom, tesc, tese comp)>]\n"
624 	                "\t[--remove-unused-variables]\n"
625 	                "\t[--flatten-multidimensional-arrays]\n"
626 	                "\t[--no-420pack-extension]\n"
627 	                "\t[--remap-variable-type <variable_name> <new_variable_type>]\n"
628 	                "\t[--rename-interface-variable <in|out> <location> <new_variable_name>]\n"
629 	                "\t[--set-hlsl-vertex-input-semantic <location> <semantic>]\n"
630 	                "\t[--rename-entry-point <old> <new> <stage>]\n"
631 	                "\t[--combined-samplers-inherit-bindings]\n"
632 	                "\t[--no-support-nonzero-baseinstance]\n"
633 	                "\t[--emit-line-directives]\n"
634 	                "\n");
635 }
636 
remap_generic(Compiler & compiler,const SmallVector<Resource> & resources,const Remap & remap)637 static bool remap_generic(Compiler &compiler, const SmallVector<Resource> &resources, const Remap &remap)
638 {
639 	auto itr =
640 	    find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; });
641 
642 	if (itr != end(resources))
643 	{
644 		compiler.set_remapped_variable_state(itr->id, true);
645 		compiler.set_name(itr->id, remap.dst_name);
646 		compiler.set_subpass_input_remapped_components(itr->id, remap.components);
647 		return true;
648 	}
649 	else
650 		return false;
651 }
652 
remap_pls(const SmallVector<PLSArg> & pls_variables,const SmallVector<Resource> & resources,const SmallVector<Resource> * secondary_resources)653 static vector<PlsRemap> remap_pls(const SmallVector<PLSArg> &pls_variables, const SmallVector<Resource> &resources,
654                                   const SmallVector<Resource> *secondary_resources)
655 {
656 	vector<PlsRemap> ret;
657 
658 	for (auto &pls : pls_variables)
659 	{
660 		bool found = false;
661 		for (auto &res : resources)
662 		{
663 			if (res.name == pls.name)
664 			{
665 				ret.push_back({ res.id, pls.format });
666 				found = true;
667 				break;
668 			}
669 		}
670 
671 		if (!found && secondary_resources)
672 		{
673 			for (auto &res : *secondary_resources)
674 			{
675 				if (res.name == pls.name)
676 				{
677 					ret.push_back({ res.id, pls.format });
678 					found = true;
679 					break;
680 				}
681 			}
682 		}
683 
684 		if (!found)
685 			fprintf(stderr, "Did not find stage input/output/target with name \"%s\".\n", pls.name.c_str());
686 	}
687 
688 	return ret;
689 }
690 
pls_format(const char * str)691 static PlsFormat pls_format(const char *str)
692 {
693 	if (!strcmp(str, "r11f_g11f_b10f"))
694 		return PlsR11FG11FB10F;
695 	else if (!strcmp(str, "r32f"))
696 		return PlsR32F;
697 	else if (!strcmp(str, "rg16f"))
698 		return PlsRG16F;
699 	else if (!strcmp(str, "rg16"))
700 		return PlsRG16;
701 	else if (!strcmp(str, "rgb10_a2"))
702 		return PlsRGB10A2;
703 	else if (!strcmp(str, "rgba8"))
704 		return PlsRGBA8;
705 	else if (!strcmp(str, "rgba8i"))
706 		return PlsRGBA8I;
707 	else if (!strcmp(str, "rgba8ui"))
708 		return PlsRGBA8UI;
709 	else if (!strcmp(str, "rg16i"))
710 		return PlsRG16I;
711 	else if (!strcmp(str, "rgb10_a2ui"))
712 		return PlsRGB10A2UI;
713 	else if (!strcmp(str, "rg16ui"))
714 		return PlsRG16UI;
715 	else if (!strcmp(str, "r32ui"))
716 		return PlsR32UI;
717 	else
718 		return PlsNone;
719 }
720 
stage_to_execution_model(const std::string & stage)721 static ExecutionModel stage_to_execution_model(const std::string &stage)
722 {
723 	if (stage == "vert")
724 		return ExecutionModelVertex;
725 	else if (stage == "frag")
726 		return ExecutionModelFragment;
727 	else if (stage == "comp")
728 		return ExecutionModelGLCompute;
729 	else if (stage == "tesc")
730 		return ExecutionModelTessellationControl;
731 	else if (stage == "tese")
732 		return ExecutionModelTessellationEvaluation;
733 	else if (stage == "geom")
734 		return ExecutionModelGeometry;
735 	else
736 		SPIRV_CROSS_THROW("Invalid stage.");
737 }
738 
compile_iteration(const CLIArguments & args,std::vector<uint32_t> spirv_file)739 static string compile_iteration(const CLIArguments &args, std::vector<uint32_t> spirv_file)
740 {
741 	Parser spirv_parser(move(spirv_file));
742 	spirv_parser.parse();
743 
744 	unique_ptr<CompilerGLSL> compiler;
745 	bool combined_image_samplers = false;
746 	bool build_dummy_sampler = false;
747 
748 	if (args.cpp)
749 	{
750 		compiler.reset(new CompilerCPP(move(spirv_parser.get_parsed_ir())));
751 		if (args.cpp_interface_name)
752 			static_cast<CompilerCPP *>(compiler.get())->set_interface_name(args.cpp_interface_name);
753 	}
754 	else if (args.msl)
755 	{
756 		compiler.reset(new CompilerMSL(move(spirv_parser.get_parsed_ir())));
757 
758 		auto *msl_comp = static_cast<CompilerMSL *>(compiler.get());
759 		auto msl_opts = msl_comp->get_msl_options();
760 		if (args.set_msl_version)
761 			msl_opts.msl_version = args.msl_version;
762 		msl_opts.capture_output_to_buffer = args.msl_capture_output_to_buffer;
763 		msl_opts.swizzle_texture_samples = args.msl_swizzle_texture_samples;
764 		msl_opts.invariant_float_math = args.msl_invariant_float_math;
765 		if (args.msl_ios)
766 		{
767 			msl_opts.platform = CompilerMSL::Options::iOS;
768 			msl_opts.ios_use_framebuffer_fetch_subpasses = args.msl_framebuffer_fetch;
769 			msl_opts.emulate_cube_array = args.msl_emulate_cube_array;
770 		}
771 		msl_opts.pad_fragment_output_components = args.msl_pad_fragment_output;
772 		msl_opts.tess_domain_origin_lower_left = args.msl_domain_lower_left;
773 		msl_opts.argument_buffers = args.msl_argument_buffers;
774 		msl_opts.texture_buffer_native = args.msl_texture_buffer_native;
775 		msl_opts.multiview = args.msl_multiview;
776 		msl_opts.view_index_from_device_index = args.msl_view_index_from_device_index;
777 		msl_opts.dispatch_base = args.msl_dispatch_base;
778 		msl_comp->set_msl_options(msl_opts);
779 		for (auto &v : args.msl_discrete_descriptor_sets)
780 			msl_comp->add_discrete_descriptor_set(v);
781 		for (auto &v : args.msl_device_argument_buffers)
782 			msl_comp->set_argument_buffer_device_address_space(v, true);
783 		uint32_t i = 0;
784 		for (auto &v : args.msl_dynamic_buffers)
785 			msl_comp->add_dynamic_buffer(v.first, v.second, i++);
786 	}
787 	else if (args.hlsl)
788 		compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir())));
789 	else
790 	{
791 		combined_image_samplers = !args.vulkan_semantics;
792 		if (!args.vulkan_semantics || args.vulkan_glsl_disable_ext_samplerless_texture_functions)
793 			build_dummy_sampler = true;
794 		compiler.reset(new CompilerGLSL(move(spirv_parser.get_parsed_ir())));
795 	}
796 
797 	if (!args.variable_type_remaps.empty())
798 	{
799 		auto remap_cb = [&](const SPIRType &, const string &name, string &out) -> void {
800 			for (const VariableTypeRemap &remap : args.variable_type_remaps)
801 				if (name == remap.variable_name)
802 					out = remap.new_variable_type;
803 		};
804 
805 		compiler->set_variable_type_remap_callback(move(remap_cb));
806 	}
807 
808 	for (auto &rename : args.entry_point_rename)
809 		compiler->rename_entry_point(rename.old_name, rename.new_name, rename.execution_model);
810 
811 	auto entry_points = compiler->get_entry_points_and_stages();
812 	auto entry_point = args.entry;
813 	ExecutionModel model = ExecutionModelMax;
814 
815 	if (!args.entry_stage.empty())
816 	{
817 		model = stage_to_execution_model(args.entry_stage);
818 		if (entry_point.empty())
819 		{
820 			// Just use the first entry point with this stage.
821 			for (auto &e : entry_points)
822 			{
823 				if (e.execution_model == model)
824 				{
825 					entry_point = e.name;
826 					break;
827 				}
828 			}
829 
830 			if (entry_point.empty())
831 			{
832 				fprintf(stderr, "Could not find an entry point with stage: %s\n", args.entry_stage.c_str());
833 				exit(EXIT_FAILURE);
834 			}
835 		}
836 		else
837 		{
838 			// Make sure both stage and name exists.
839 			bool exists = false;
840 			for (auto &e : entry_points)
841 			{
842 				if (e.execution_model == model && e.name == entry_point)
843 				{
844 					exists = true;
845 					break;
846 				}
847 			}
848 
849 			if (!exists)
850 			{
851 				fprintf(stderr, "Could not find an entry point %s with stage: %s\n", entry_point.c_str(),
852 				        args.entry_stage.c_str());
853 				exit(EXIT_FAILURE);
854 			}
855 		}
856 	}
857 	else if (!entry_point.empty())
858 	{
859 		// Make sure there is just one entry point with this name, or the stage
860 		// is ambiguous.
861 		uint32_t stage_count = 0;
862 		for (auto &e : entry_points)
863 		{
864 			if (e.name == entry_point)
865 			{
866 				stage_count++;
867 				model = e.execution_model;
868 			}
869 		}
870 
871 		if (stage_count == 0)
872 		{
873 			fprintf(stderr, "There is no entry point with name: %s\n", entry_point.c_str());
874 			exit(EXIT_FAILURE);
875 		}
876 		else if (stage_count > 1)
877 		{
878 			fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", entry_point.c_str());
879 			exit(EXIT_FAILURE);
880 		}
881 	}
882 
883 	if (!entry_point.empty())
884 		compiler->set_entry_point(entry_point, model);
885 
886 	if (!args.set_version && !compiler->get_common_options().version)
887 	{
888 		fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n");
889 		print_help();
890 		exit(EXIT_FAILURE);
891 	}
892 
893 	CompilerGLSL::Options opts = compiler->get_common_options();
894 	if (args.set_version)
895 		opts.version = args.version;
896 	if (args.set_es)
897 		opts.es = args.es;
898 	opts.force_temporary = args.force_temporary;
899 	opts.separate_shader_objects = args.sso;
900 	opts.flatten_multidimensional_arrays = args.flatten_multidimensional_arrays;
901 	opts.enable_420pack_extension = args.use_420pack_extension;
902 	opts.vulkan_semantics = args.vulkan_semantics;
903 	opts.vertex.fixup_clipspace = args.fixup;
904 	opts.vertex.flip_vert_y = args.yflip;
905 	opts.vertex.support_nonzero_base_instance = args.support_nonzero_baseinstance;
906 	opts.emit_push_constant_as_uniform_buffer = args.glsl_emit_push_constant_as_ubo;
907 	opts.emit_uniform_buffer_as_plain_uniforms = args.glsl_emit_ubo_as_plain_uniforms;
908 	opts.emit_line_directives = args.emit_line_directives;
909 	compiler->set_common_options(opts);
910 
911 	// Set HLSL specific options.
912 	if (args.hlsl)
913 	{
914 		auto *hlsl = static_cast<CompilerHLSL *>(compiler.get());
915 		auto hlsl_opts = hlsl->get_hlsl_options();
916 		if (args.set_shader_model)
917 		{
918 			if (args.shader_model < 30)
919 			{
920 				fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n");
921 				exit(EXIT_FAILURE);
922 			}
923 
924 			hlsl_opts.shader_model = args.shader_model;
925 		}
926 
927 		if (args.hlsl_compat)
928 		{
929 			// Enable all compat options.
930 			hlsl_opts.point_size_compat = true;
931 			hlsl_opts.point_coord_compat = true;
932 		}
933 
934 		if (hlsl_opts.shader_model <= 30)
935 		{
936 			combined_image_samplers = true;
937 			build_dummy_sampler = true;
938 		}
939 
940 		hlsl_opts.support_nonzero_base_vertex_base_instance = args.hlsl_support_nonzero_base;
941 		hlsl->set_hlsl_options(hlsl_opts);
942 	}
943 
944 	if (build_dummy_sampler)
945 	{
946 		uint32_t sampler = compiler->build_dummy_sampler_for_combined_images();
947 		if (sampler != 0)
948 		{
949 			// Set some defaults to make validation happy.
950 			compiler->set_decoration(sampler, DecorationDescriptorSet, 0);
951 			compiler->set_decoration(sampler, DecorationBinding, 0);
952 		}
953 	}
954 
955 	ShaderResources res;
956 	if (args.remove_unused)
957 	{
958 		auto active = compiler->get_active_interface_variables();
959 		res = compiler->get_shader_resources(active);
960 		compiler->set_enabled_interface_variables(move(active));
961 	}
962 	else
963 		res = compiler->get_shader_resources();
964 
965 	if (args.flatten_ubo)
966 	{
967 		for (auto &ubo : res.uniform_buffers)
968 			compiler->flatten_buffer_block(ubo.id);
969 		for (auto &ubo : res.push_constant_buffers)
970 			compiler->flatten_buffer_block(ubo.id);
971 	}
972 
973 	auto pls_inputs = remap_pls(args.pls_in, res.stage_inputs, &res.subpass_inputs);
974 	auto pls_outputs = remap_pls(args.pls_out, res.stage_outputs, nullptr);
975 	compiler->remap_pixel_local_storage(move(pls_inputs), move(pls_outputs));
976 
977 	for (auto &ext : args.extensions)
978 		compiler->require_extension(ext);
979 
980 	for (auto &remap : args.remaps)
981 	{
982 		if (remap_generic(*compiler, res.stage_inputs, remap))
983 			continue;
984 		if (remap_generic(*compiler, res.stage_outputs, remap))
985 			continue;
986 		if (remap_generic(*compiler, res.subpass_inputs, remap))
987 			continue;
988 	}
989 
990 	for (auto &rename : args.interface_variable_renames)
991 	{
992 		if (rename.storageClass == StorageClassInput)
993 			spirv_cross_util::rename_interface_variable(*compiler, res.stage_inputs, rename.location,
994 			                                            rename.variable_name);
995 		else if (rename.storageClass == StorageClassOutput)
996 			spirv_cross_util::rename_interface_variable(*compiler, res.stage_outputs, rename.location,
997 			                                            rename.variable_name);
998 		else
999 		{
1000 			fprintf(stderr, "error at --rename-interface-variable <in|out> ...\n");
1001 			exit(EXIT_FAILURE);
1002 		}
1003 	}
1004 
1005 	if (args.dump_resources)
1006 	{
1007 		print_resources(*compiler, res);
1008 		print_push_constant_resources(*compiler, res.push_constant_buffers);
1009 		print_spec_constants(*compiler);
1010 		print_capabilities_and_extensions(*compiler);
1011 	}
1012 
1013 	if (combined_image_samplers)
1014 	{
1015 		compiler->build_combined_image_samplers();
1016 		if (args.combined_samplers_inherit_bindings)
1017 			spirv_cross_util::inherit_combined_sampler_bindings(*compiler);
1018 
1019 		// Give the remapped combined samplers new names.
1020 		for (auto &remap : compiler->get_combined_image_samplers())
1021 		{
1022 			compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id),
1023 			                                           compiler->get_name(remap.sampler_id)));
1024 		}
1025 	}
1026 
1027 	if (args.hlsl)
1028 	{
1029 		auto *hlsl_compiler = static_cast<CompilerHLSL *>(compiler.get());
1030 		uint32_t new_builtin = hlsl_compiler->remap_num_workgroups_builtin();
1031 		if (new_builtin)
1032 		{
1033 			hlsl_compiler->set_decoration(new_builtin, DecorationDescriptorSet, 0);
1034 			hlsl_compiler->set_decoration(new_builtin, DecorationBinding, 0);
1035 		}
1036 	}
1037 
1038 	if (args.hlsl)
1039 	{
1040 		for (auto &remap : args.hlsl_attr_remap)
1041 			static_cast<CompilerHLSL *>(compiler.get())->add_vertex_attribute_remap(remap);
1042 	}
1043 
1044 	return compiler->compile();
1045 }
1046 
main_inner(int argc,char * argv[])1047 static int main_inner(int argc, char *argv[])
1048 {
1049 	CLIArguments args;
1050 	CLICallbacks cbs;
1051 
1052 	cbs.add("--help", [](CLIParser &parser) {
1053 		print_help();
1054 		parser.end();
1055 	});
1056 	cbs.add("--revision", [](CLIParser &parser) {
1057 		print_version();
1058 		parser.end();
1059 	});
1060 	cbs.add("--output", [&args](CLIParser &parser) { args.output = parser.next_string(); });
1061 	cbs.add("--es", [&args](CLIParser &) {
1062 		args.es = true;
1063 		args.set_es = true;
1064 	});
1065 	cbs.add("--no-es", [&args](CLIParser &) {
1066 		args.es = false;
1067 		args.set_es = true;
1068 	});
1069 	cbs.add("--version", [&args](CLIParser &parser) {
1070 		args.version = parser.next_uint();
1071 		args.set_version = true;
1072 	});
1073 	cbs.add("--dump-resources", [&args](CLIParser &) { args.dump_resources = true; });
1074 	cbs.add("--force-temporary", [&args](CLIParser &) { args.force_temporary = true; });
1075 	cbs.add("--flatten-ubo", [&args](CLIParser &) { args.flatten_ubo = true; });
1076 	cbs.add("--fixup-clipspace", [&args](CLIParser &) { args.fixup = true; });
1077 	cbs.add("--flip-vert-y", [&args](CLIParser &) { args.yflip = true; });
1078 	cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); });
1079 	cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; });
1080 	cbs.add("--reflect", [&args](CLIParser &parser) { args.reflect = parser.next_value_string("json"); });
1081 	cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); });
1082 	cbs.add("--metal", [&args](CLIParser &) { args.msl = true; }); // Legacy compatibility
1083 	cbs.add("--glsl-emit-push-constant-as-ubo", [&args](CLIParser &) { args.glsl_emit_push_constant_as_ubo = true; });
1084 	cbs.add("--glsl-emit-ubo-as-plain-uniforms", [&args](CLIParser &) { args.glsl_emit_ubo_as_plain_uniforms = true; });
1085 	cbs.add("--vulkan-glsl-disable-ext-samplerless-texture-functions",
1086 	        [&args](CLIParser &) { args.vulkan_glsl_disable_ext_samplerless_texture_functions = true; });
1087 	cbs.add("--msl", [&args](CLIParser &) { args.msl = true; });
1088 	cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; });
1089 	cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; });
1090 	cbs.add("--hlsl-support-nonzero-basevertex-baseinstance",
1091 	        [&args](CLIParser &) { args.hlsl_support_nonzero_base = true; });
1092 	cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; });
1093 	cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; });
1094 	cbs.add("--no-420pack-extension", [&args](CLIParser &) { args.use_420pack_extension = false; });
1095 	cbs.add("--msl-capture-output", [&args](CLIParser &) { args.msl_capture_output_to_buffer = true; });
1096 	cbs.add("--msl-swizzle-texture-samples", [&args](CLIParser &) { args.msl_swizzle_texture_samples = true; });
1097 	cbs.add("--msl-ios", [&args](CLIParser &) { args.msl_ios = true; });
1098 	cbs.add("--msl-pad-fragment-output", [&args](CLIParser &) { args.msl_pad_fragment_output = true; });
1099 	cbs.add("--msl-domain-lower-left", [&args](CLIParser &) { args.msl_domain_lower_left = true; });
1100 	cbs.add("--msl-argument-buffers", [&args](CLIParser &) { args.msl_argument_buffers = true; });
1101 	cbs.add("--msl-discrete-descriptor-set",
1102 	        [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); });
1103 	cbs.add("--msl-device-argument-buffer",
1104 	        [&args](CLIParser &parser) { args.msl_device_argument_buffers.push_back(parser.next_uint()); });
1105 	cbs.add("--msl-texture-buffer-native", [&args](CLIParser &) { args.msl_texture_buffer_native = true; });
1106 	cbs.add("--msl-framebuffer-fetch", [&args](CLIParser &) { args.msl_framebuffer_fetch = true; });
1107 	cbs.add("--msl-invariant-float-math", [&args](CLIParser &) { args.msl_invariant_float_math = true; });
1108 	cbs.add("--msl-emulate-cube-array", [&args](CLIParser &) { args.msl_emulate_cube_array = true; });
1109 	cbs.add("--msl-multiview", [&args](CLIParser &) { args.msl_multiview = true; });
1110 	cbs.add("--msl-view-index-from-device-index",
1111 	        [&args](CLIParser &) { args.msl_view_index_from_device_index = true; });
1112 	cbs.add("--msl-dispatch-base", [&args](CLIParser &) { args.msl_dispatch_base = true; });
1113 	cbs.add("--msl-dynamic-buffer", [&args](CLIParser &parser) {
1114 		args.msl_argument_buffers = true;
1115 		// Make sure next_uint() is called in-order.
1116 		uint32_t desc_set = parser.next_uint();
1117 		uint32_t binding = parser.next_uint();
1118 		args.msl_dynamic_buffers.push_back(make_pair(desc_set, binding));
1119 	});
1120 	cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
1121 	cbs.add("--rename-entry-point", [&args](CLIParser &parser) {
1122 		auto old_name = parser.next_string();
1123 		auto new_name = parser.next_string();
1124 		auto model = stage_to_execution_model(parser.next_string());
1125 		args.entry_point_rename.push_back({ old_name, new_name, move(model) });
1126 	});
1127 	cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); });
1128 	cbs.add("--stage", [&args](CLIParser &parser) { args.entry_stage = parser.next_string(); });
1129 	cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; });
1130 	cbs.add("--set-hlsl-vertex-input-semantic", [&args](CLIParser &parser) {
1131 		HLSLVertexAttributeRemap remap;
1132 		remap.location = parser.next_uint();
1133 		remap.semantic = parser.next_string();
1134 		args.hlsl_attr_remap.push_back(move(remap));
1135 	});
1136 
1137 	cbs.add("--remap", [&args](CLIParser &parser) {
1138 		string src = parser.next_string();
1139 		string dst = parser.next_string();
1140 		uint32_t components = parser.next_uint();
1141 		args.remaps.push_back({ move(src), move(dst), components });
1142 	});
1143 
1144 	cbs.add("--remap-variable-type", [&args](CLIParser &parser) {
1145 		string var_name = parser.next_string();
1146 		string new_type = parser.next_string();
1147 		args.variable_type_remaps.push_back({ move(var_name), move(new_type) });
1148 	});
1149 
1150 	cbs.add("--rename-interface-variable", [&args](CLIParser &parser) {
1151 		StorageClass cls = StorageClassMax;
1152 		string clsStr = parser.next_string();
1153 		if (clsStr == "in")
1154 			cls = StorageClassInput;
1155 		else if (clsStr == "out")
1156 			cls = StorageClassOutput;
1157 
1158 		uint32_t loc = parser.next_uint();
1159 		string var_name = parser.next_string();
1160 		args.interface_variable_renames.push_back({ cls, loc, move(var_name) });
1161 	});
1162 
1163 	cbs.add("--pls-in", [&args](CLIParser &parser) {
1164 		auto fmt = pls_format(parser.next_string());
1165 		auto name = parser.next_string();
1166 		args.pls_in.push_back({ move(fmt), move(name) });
1167 	});
1168 	cbs.add("--pls-out", [&args](CLIParser &parser) {
1169 		auto fmt = pls_format(parser.next_string());
1170 		auto name = parser.next_string();
1171 		args.pls_out.push_back({ move(fmt), move(name) });
1172 	});
1173 	cbs.add("--shader-model", [&args](CLIParser &parser) {
1174 		args.shader_model = parser.next_uint();
1175 		args.set_shader_model = true;
1176 	});
1177 	cbs.add("--msl-version", [&args](CLIParser &parser) {
1178 		args.msl_version = parser.next_uint();
1179 		args.set_msl_version = true;
1180 	});
1181 
1182 	cbs.add("--remove-unused-variables", [&args](CLIParser &) { args.remove_unused = true; });
1183 	cbs.add("--combined-samplers-inherit-bindings",
1184 	        [&args](CLIParser &) { args.combined_samplers_inherit_bindings = true; });
1185 
1186 	cbs.add("--no-support-nonzero-baseinstance", [&](CLIParser &) { args.support_nonzero_baseinstance = false; });
1187 	cbs.add("--emit-line-directives", [&args](CLIParser &) { args.emit_line_directives = true; });
1188 
1189 	cbs.default_handler = [&args](const char *value) { args.input = value; };
1190 	cbs.error_handler = [] { print_help(); };
1191 
1192 	CLIParser parser{ move(cbs), argc - 1, argv + 1 };
1193 	if (!parser.parse())
1194 		return EXIT_FAILURE;
1195 	else if (parser.ended_state)
1196 		return EXIT_SUCCESS;
1197 
1198 	if (!args.input)
1199 	{
1200 		fprintf(stderr, "Didn't specify input file.\n");
1201 		print_help();
1202 		return EXIT_FAILURE;
1203 	}
1204 
1205 	auto spirv_file = read_spirv_file(args.input);
1206 	if (spirv_file.empty())
1207 		return EXIT_FAILURE;
1208 
1209 	// Special case reflection because it has little to do with the path followed by code-outputting compilers
1210 	if (!args.reflect.empty())
1211 	{
1212 		Parser spirv_parser(move(spirv_file));
1213 		spirv_parser.parse();
1214 
1215 		CompilerReflection compiler(move(spirv_parser.get_parsed_ir()));
1216 		compiler.set_format(args.reflect);
1217 		auto json = compiler.compile();
1218 		if (args.output)
1219 			write_string_to_file(args.output, json.c_str());
1220 		else
1221 			printf("%s", json.c_str());
1222 		return EXIT_SUCCESS;
1223 	}
1224 
1225 	string compiled_output;
1226 
1227 	if (args.iterations == 1)
1228 		compiled_output = compile_iteration(args, move(spirv_file));
1229 	else
1230 	{
1231 		for (unsigned i = 0; i < args.iterations; i++)
1232 			compiled_output = compile_iteration(args, spirv_file);
1233 	}
1234 
1235 	if (args.output)
1236 		write_string_to_file(args.output, compiled_output.c_str());
1237 	else
1238 		printf("%s", compiled_output.c_str());
1239 
1240 	return EXIT_SUCCESS;
1241 }
1242 
main(int argc,char * argv[])1243 int main(int argc, char *argv[])
1244 {
1245 #ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
1246 	return main_inner(argc, argv);
1247 #else
1248 	// Make sure we catch the exception or it just disappears into the aether on Windows.
1249 	try
1250 	{
1251 		return main_inner(argc, argv);
1252 	}
1253 	catch (const std::exception &e)
1254 	{
1255 		fprintf(stderr, "SPIRV-Cross threw an exception: %s\n", e.what());
1256 		return EXIT_FAILURE;
1257 	}
1258 #endif
1259 }
1260