blob: 9539dc726f89e9276aaebcc21e17a13104197097 [file] [log] [blame]
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001/*
Hans-Kristian Arntzen18c37bc2017-01-28 09:00:40 +01002 * Copyright 2015-2017 ARM Limited
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01003 *
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
Hans-Kristian Arntzen147e53a2016-04-04 09:36:04 +020017#include "spirv_cpp.hpp"
Robert Konrad216a6812017-01-25 17:30:52 +010018#include "spirv_hlsl.hpp"
Hans-Kristian Arntzen24df8f02017-02-04 10:26:26 +010019#include "spirv_msl.hpp"
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020020#include <algorithm>
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010021#include <cstdio>
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020022#include <cstring>
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010023#include <functional>
24#include <limits>
25#include <memory>
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020026#include <stdexcept>
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010027#include <unordered_map>
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010028#include <unordered_set>
29
Hans-Kristian Arntzence3fe292017-01-12 10:57:44 +010030#ifdef _MSC_VER
Hans-Kristian Arntzen27f4f752017-01-13 16:32:54 +010031#pragma warning(disable : 4996)
Hans-Kristian Arntzence3fe292017-01-12 10:57:44 +010032#endif
33
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010034using namespace spv;
Hans-Kristian Arntzen147e53a2016-04-04 09:36:04 +020035using namespace spirv_cross;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010036using namespace std;
37
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +010038#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
39#define THROW(x) \
40 do \
41 { \
42 fprintf(stderr, "%s.", x); \
43 exit(1); \
44 } while (0)
45#else
Hans-Kristian Arntzenc92b8392017-02-16 11:06:57 +010046#define THROW(x) throw runtime_error(x)
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +010047#endif
48
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010049struct CLIParser;
50struct CLICallbacks
51{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020052 void add(const char *cli, const function<void(CLIParser &)> &func)
53 {
54 callbacks[cli] = func;
55 }
56 unordered_map<string, function<void(CLIParser &)>> callbacks;
57 function<void()> error_handler;
58 function<void(const char *)> default_handler;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010059};
60
61struct CLIParser
62{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020063 CLIParser(CLICallbacks cbs_, int argc_, char *argv_[])
64 : cbs(move(cbs_))
65 , argc(argc_)
66 , argv(argv_)
67 {
68 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010069
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020070 bool parse()
71 {
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +010072#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020073 try
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +010074#endif
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020075 {
76 while (argc && !ended_state)
77 {
78 const char *next = *argv++;
79 argc--;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010080
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020081 if (*next != '-' && cbs.default_handler)
82 {
83 cbs.default_handler(next);
84 }
85 else
86 {
87 auto itr = cbs.callbacks.find(next);
88 if (itr == ::end(cbs.callbacks))
89 {
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +010090 THROW("Invalid argument");
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020091 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010092
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020093 itr->second(*this);
94 }
95 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010096
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +020097 return true;
98 }
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +010099#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200100 catch (...)
101 {
102 if (cbs.error_handler)
103 {
104 cbs.error_handler();
105 }
106 return false;
107 }
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +0100108#endif
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200109 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100110
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200111 void end()
112 {
113 ended_state = true;
114 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100115
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200116 uint32_t next_uint()
117 {
118 if (!argc)
119 {
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +0100120 THROW("Tried to parse uint, but nothing left in arguments");
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200121 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100122
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200123 uint32_t val = stoul(*argv);
124 if (val > numeric_limits<uint32_t>::max())
125 {
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +0100126 THROW("next_uint() out of range");
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200127 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100128
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200129 argc--;
130 argv++;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100131
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200132 return val;
133 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100134
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200135 double next_double()
136 {
137 if (!argc)
138 {
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +0100139 THROW("Tried to parse double, but nothing left in arguments");
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200140 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100141
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200142 double val = stod(*argv);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100143
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200144 argc--;
145 argv++;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100146
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200147 return val;
148 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100149
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200150 const char *next_string()
151 {
152 if (!argc)
153 {
Panagiotis Christopoulos Charitos7f69f932016-12-15 20:46:10 +0100154 THROW("Tried to parse string, but nothing left in arguments");
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200155 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100156
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200157 const char *ret = *argv;
158 argc--;
159 argv++;
160 return ret;
161 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100162
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200163 CLICallbacks cbs;
164 int argc;
165 char **argv;
166 bool ended_state = false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100167};
168
169static vector<uint32_t> read_spirv_file(const char *path)
170{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200171 FILE *file = fopen(path, "rb");
172 if (!file)
173 {
174 fprintf(stderr, "Failed to open SPIRV file: %s\n", path);
175 return {};
176 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100177
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200178 fseek(file, 0, SEEK_END);
179 long len = ftell(file) / sizeof(uint32_t);
180 rewind(file);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100181
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200182 vector<uint32_t> spirv(len);
183 if (fread(spirv.data(), sizeof(uint32_t), len, file) != size_t(len))
184 spirv.clear();
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100185
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200186 fclose(file);
187 return spirv;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100188}
189
190static bool write_string_to_file(const char *path, const char *string)
191{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200192 FILE *file = fopen(path, "w");
193 if (!file)
194 {
195 fprintf(file, "Failed to write file: %s\n", path);
196 return false;
197 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100198
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200199 fprintf(file, "%s", string);
200 fclose(file);
201 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100202}
203
204static void print_resources(const Compiler &compiler, const char *tag, const vector<Resource> &resources)
205{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200206 fprintf(stderr, "%s\n", tag);
207 fprintf(stderr, "=============\n\n");
Hans-Kristian Arntzenec45c9e2017-04-19 17:33:14 +0200208 bool print_ssbo = !strcmp(tag, "ssbos");
209
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200210 for (auto &res : resources)
211 {
212 auto &type = compiler.get_type(res.type_id);
213 auto mask = compiler.get_decoration_mask(res.id);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100214
Hans-Kristian Arntzenec45c9e2017-04-19 17:33:14 +0200215 if (print_ssbo && compiler.buffer_is_hlsl_counter_buffer(res.id))
216 continue;
217
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200218 // If we don't have a name, use the fallback for the type instead of the variable
219 // for SSBOs and UBOs since those are the only meaningful names to use externally.
220 // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
221 bool is_push_constant = compiler.get_storage_class(res.id) == StorageClassPushConstant;
222 bool is_block = (compiler.get_decoration_mask(type.self) &
223 ((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) != 0;
Hans-Kristian Arntzenb9600aa2016-11-29 09:04:28 +0100224 bool is_sized_block = is_block && (compiler.get_storage_class(res.id) == StorageClassUniform ||
225 compiler.get_storage_class(res.id) == StorageClassUniformConstant);
Hans-Kristian Arntzen5c24d992016-07-12 21:20:18 +0200226 uint32_t fallback_id = !is_push_constant && is_block ? res.base_type_id : res.id;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100227
Hans-Kristian Arntzen2d79d362016-11-28 15:01:36 +0100228 uint32_t block_size = 0;
Hans-Kristian Arntzenb9600aa2016-11-29 09:04:28 +0100229 if (is_sized_block)
Graham Wihlidalbcfe2be2017-01-05 21:02:57 +0100230 block_size = uint32_t(compiler.get_declared_struct_size(compiler.get_type(res.base_type_id)));
Hans-Kristian Arntzen2d79d362016-11-28 15:01:36 +0100231
Hans-Kristian Arntzen5c24d992016-07-12 21:20:18 +0200232 string array;
233 for (auto arr : type.array)
234 array = join("[", arr ? convert_to_string(arr) : "", "]") + array;
235
236 fprintf(stderr, " ID %03u : %s%s", res.id,
237 !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str(), array.c_str());
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100238
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200239 if (mask & (1ull << DecorationLocation))
240 fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation));
241 if (mask & (1ull << DecorationDescriptorSet))
242 fprintf(stderr, " (Set : %u)", compiler.get_decoration(res.id, DecorationDescriptorSet));
243 if (mask & (1ull << DecorationBinding))
244 fprintf(stderr, " (Binding : %u)", compiler.get_decoration(res.id, DecorationBinding));
245 if (mask & (1ull << DecorationInputAttachmentIndex))
246 fprintf(stderr, " (Attachment : %u)", compiler.get_decoration(res.id, DecorationInputAttachmentIndex));
Hans-Kristian Arntzen97f7ab82017-01-05 18:16:33 +0100247 if (mask & (1ull << DecorationNonReadable))
248 fprintf(stderr, " writeonly");
249 if (mask & (1ull << DecorationNonWritable))
250 fprintf(stderr, " readonly");
Hans-Kristian Arntzenb9600aa2016-11-29 09:04:28 +0100251 if (is_sized_block)
Hans-Kristian Arntzen2d79d362016-11-28 15:01:36 +0100252 fprintf(stderr, " (BlockSize : %u bytes)", block_size);
Hans-Kristian Arntzenec45c9e2017-04-19 17:33:14 +0200253
254 uint32_t counter_id = 0;
255 if (print_ssbo && compiler.buffer_get_hlsl_counter_buffer(res.id, counter_id))
256 fprintf(stderr, " (HLSL counter buffer ID: %u)", counter_id);
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200257 fprintf(stderr, "\n");
258 }
259 fprintf(stderr, "=============\n\n");
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100260}
261
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +0200262static const char *execution_model_to_str(spv::ExecutionModel model)
263{
264 switch (model)
265 {
266 case spv::ExecutionModelVertex:
267 return "vertex";
268 case spv::ExecutionModelTessellationControl:
269 return "tessellation control";
270 case ExecutionModelTessellationEvaluation:
271 return "tessellation evaluation";
272 case ExecutionModelGeometry:
273 return "geometry";
274 case ExecutionModelFragment:
275 return "fragment";
276 case ExecutionModelGLCompute:
277 return "compute";
278 default:
279 return "???";
280 }
281}
282
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100283static void print_resources(const Compiler &compiler, const ShaderResources &res)
284{
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +0200285 uint64_t modes = compiler.get_execution_mode_mask();
286
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +0200287 fprintf(stderr, "Entry points:\n");
288 auto entry_points = compiler.get_entry_points();
289 for (auto &e : entry_points)
290 fprintf(stderr, " %s (%s)\n", e.c_str(), execution_model_to_str(compiler.get_entry_point(e).model));
291 fprintf(stderr, "\n");
292
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +0200293 fprintf(stderr, "Execution modes:\n");
294 for (unsigned i = 0; i < 64; i++)
295 {
296 if (!(modes & (1ull << i)))
297 continue;
298
299 auto mode = static_cast<ExecutionMode>(i);
300 uint32_t arg0 = compiler.get_execution_mode_argument(mode, 0);
301 uint32_t arg1 = compiler.get_execution_mode_argument(mode, 1);
302 uint32_t arg2 = compiler.get_execution_mode_argument(mode, 2);
303
304 switch (static_cast<ExecutionMode>(i))
305 {
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200306 case ExecutionModeInvocations:
307 fprintf(stderr, " Invocations: %u\n", arg0);
308 break;
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +0200309
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200310 case ExecutionModeLocalSize:
311 fprintf(stderr, " LocalSize: (%u, %u, %u)\n", arg0, arg1, arg2);
312 break;
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +0200313
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200314 case ExecutionModeOutputVertices:
315 fprintf(stderr, " OutputVertices: %u\n", arg0);
316 break;
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +0200317
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200318#define CHECK_MODE(m) \
319 case ExecutionMode##m: \
320 fprintf(stderr, " %s\n", #m); \
321 break
322 CHECK_MODE(SpacingEqual);
323 CHECK_MODE(SpacingFractionalEven);
324 CHECK_MODE(SpacingFractionalOdd);
325 CHECK_MODE(VertexOrderCw);
326 CHECK_MODE(VertexOrderCcw);
327 CHECK_MODE(PixelCenterInteger);
328 CHECK_MODE(OriginUpperLeft);
329 CHECK_MODE(OriginLowerLeft);
330 CHECK_MODE(EarlyFragmentTests);
331 CHECK_MODE(PointMode);
332 CHECK_MODE(Xfb);
333 CHECK_MODE(DepthReplacing);
334 CHECK_MODE(DepthGreater);
335 CHECK_MODE(DepthLess);
336 CHECK_MODE(DepthUnchanged);
337 CHECK_MODE(LocalSizeHint);
338 CHECK_MODE(InputPoints);
339 CHECK_MODE(InputLines);
340 CHECK_MODE(InputLinesAdjacency);
341 CHECK_MODE(Triangles);
342 CHECK_MODE(InputTrianglesAdjacency);
343 CHECK_MODE(Quads);
344 CHECK_MODE(Isolines);
345 CHECK_MODE(OutputPoints);
346 CHECK_MODE(OutputLineStrip);
347 CHECK_MODE(OutputTriangleStrip);
348 CHECK_MODE(VecTypeHint);
349 CHECK_MODE(ContractionOff);
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +0200350
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200351 default:
352 break;
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +0200353 }
354 }
355 fprintf(stderr, "\n");
356
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200357 print_resources(compiler, "subpass inputs", res.subpass_inputs);
358 print_resources(compiler, "inputs", res.stage_inputs);
359 print_resources(compiler, "outputs", res.stage_outputs);
360 print_resources(compiler, "textures", res.sampled_images);
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +0200361 print_resources(compiler, "separate images", res.separate_images);
Hans-Kristian Arntzene9202082016-09-10 13:05:35 +0200362 print_resources(compiler, "separate samplers", res.separate_samplers);
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200363 print_resources(compiler, "images", res.storage_images);
364 print_resources(compiler, "ssbos", res.storage_buffers);
365 print_resources(compiler, "ubos", res.uniform_buffers);
366 print_resources(compiler, "push", res.push_constant_buffers);
367 print_resources(compiler, "counters", res.atomic_counters);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100368}
369
370static void print_push_constant_resources(const Compiler &compiler, const vector<Resource> &res)
371{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200372 for (auto &block : res)
373 {
374 auto ranges = compiler.get_active_buffer_ranges(block.id);
375 fprintf(stderr, "Active members in buffer: %s\n",
376 !block.name.empty() ? block.name.c_str() : compiler.get_fallback_name(block.id).c_str());
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100377
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200378 fprintf(stderr, "==================\n\n");
379 for (auto &range : ranges)
380 {
Hans-Kristian Arntzen5c24d992016-07-12 21:20:18 +0200381 const auto &name = compiler.get_member_name(block.base_type_id, range.index);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100382
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200383 fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index,
384 !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(),
385 unsigned(range.offset), unsigned(range.range));
386 }
387 fprintf(stderr, "==================\n\n");
388 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100389}
390
Hans-Kristian Arntzen6bd545b2016-09-17 15:16:07 +0200391static void print_spec_constants(const Compiler &compiler)
392{
393 auto spec_constants = compiler.get_specialization_constants();
394 fprintf(stderr, "Specialization constants\n");
395 fprintf(stderr, "==================\n\n");
396 for (auto &c : spec_constants)
397 fprintf(stderr, "ID: %u, Spec ID: %u\n", c.id, c.constant_id);
398 fprintf(stderr, "==================\n\n");
399}
400
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100401struct PLSArg
402{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200403 PlsFormat format;
404 string name;
405};
406
407struct Remap
408{
409 string src_name;
410 string dst_name;
411 unsigned components;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100412};
413
Panagiotis Christopoulos Charitos66e76d92016-09-20 10:17:41 +0200414struct VariableTypeRemap
415{
416 string variable_name;
417 string new_variable_type;
418};
419
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100420struct CLIArguments
421{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200422 const char *input = nullptr;
423 const char *output = nullptr;
424 const char *cpp_interface_name = nullptr;
425 uint32_t version = 0;
Hans-Kristian Arntzen2e3c6ec2017-02-16 11:17:12 +0100426 uint32_t shader_model = 0;
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200427 bool es = false;
428 bool set_version = false;
Hans-Kristian Arntzen2e3c6ec2017-02-16 11:17:12 +0100429 bool set_shader_model = false;
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200430 bool set_es = false;
431 bool dump_resources = false;
432 bool force_temporary = false;
433 bool flatten_ubo = false;
434 bool fixup = false;
Hans-Kristian Arntzen36650c82017-05-22 15:30:43 +0200435 bool sso = false;
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200436 vector<PLSArg> pls_in;
437 vector<PLSArg> pls_out;
438 vector<Remap> remaps;
439 vector<string> extensions;
Panagiotis Christopoulos Charitos66e76d92016-09-20 10:17:41 +0200440 vector<VariableTypeRemap> variable_type_remaps;
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +0200441 string entry;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100442
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200443 uint32_t iterations = 1;
444 bool cpp = false;
Bill Hollings5ad73f32017-03-19 21:06:21 -0400445 bool msl = false;
Bill Hollings5550c872017-03-12 17:42:51 -0400446 bool msl_pack_ubos = true;
Robert Konrad216a6812017-01-25 17:30:52 +0100447 bool hlsl = false;
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +0200448 bool hlsl_compat = false;
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200449 bool vulkan_semantics = false;
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200450 bool remove_unused = false;
Hans-Kristian Arntzenb847c882016-11-18 17:06:49 +0100451 bool cfg_analysis = true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100452};
453
454static void print_help()
455{
Hans-Kristian Arntzenb847c882016-11-18 17:06:49 +0100456 fprintf(stderr, "Usage: spirv-cross [--output <output path>] [SPIR-V file] [--es] [--no-es] [--no-cfg-analysis] "
Bill Hollings5ad73f32017-03-19 21:06:21 -0400457 "[--version <GLSL version>] [--dump-resources] [--help] [--force-temporary] "
458 "[--vulkan-semantics] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] "
459 "[--cpp] [--cpp-interface-name <name>] "
460 "[--msl] [--msl-no-pack-ubos] "
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +0200461 "[--hlsl] [--shader-model] [--hlsl-enable-compat] "
Hans-Kristian Arntzen36650c82017-05-22 15:30:43 +0200462 "[--separate-shader-objects]"
Robert Konrad216a6812017-01-25 17:30:52 +0100463 "[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name "
464 "components] [--extension ext] [--entry name] [--remove-unused-variables] "
Panagiotis Christopoulos Charitos66e76d92016-09-20 10:17:41 +0200465 "[--remap-variable-type <variable_name> <new_variable_type>]\n");
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100466}
467
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200468static bool remap_generic(Compiler &compiler, const vector<Resource> &resources, const Remap &remap)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100469{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200470 auto itr =
471 find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; });
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100472
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200473 if (itr != end(resources))
474 {
475 compiler.set_remapped_variable_state(itr->id, true);
476 compiler.set_name(itr->id, remap.dst_name);
477 compiler.set_subpass_input_remapped_components(itr->id, remap.components);
478 return true;
479 }
480 else
481 return false;
482}
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100483
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200484static vector<PlsRemap> remap_pls(const vector<PLSArg> &pls_variables, const vector<Resource> &resources,
485 const vector<Resource> *secondary_resources)
486{
487 vector<PlsRemap> ret;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100488
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200489 for (auto &pls : pls_variables)
490 {
491 bool found = false;
492 for (auto &res : resources)
493 {
494 if (res.name == pls.name)
495 {
496 ret.push_back({ res.id, pls.format });
497 found = true;
498 break;
499 }
500 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100501
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200502 if (!found && secondary_resources)
503 {
504 for (auto &res : *secondary_resources)
505 {
506 if (res.name == pls.name)
507 {
508 ret.push_back({ res.id, pls.format });
509 found = true;
510 break;
511 }
512 }
513 }
514
515 if (!found)
516 fprintf(stderr, "Did not find stage input/output/target with name \"%s\".\n", pls.name.c_str());
517 }
518
519 return ret;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100520}
521
522static PlsFormat pls_format(const char *str)
523{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200524 if (!strcmp(str, "r11f_g11f_b10f"))
525 return PlsR11FG11FB10F;
526 else if (!strcmp(str, "r32f"))
527 return PlsR32F;
528 else if (!strcmp(str, "rg16f"))
529 return PlsRG16F;
530 else if (!strcmp(str, "rg16"))
531 return PlsRG16;
532 else if (!strcmp(str, "rgb10_a2"))
533 return PlsRGB10A2;
534 else if (!strcmp(str, "rgba8"))
535 return PlsRGBA8;
536 else if (!strcmp(str, "rgba8i"))
537 return PlsRGBA8I;
538 else if (!strcmp(str, "rgba8ui"))
539 return PlsRGBA8UI;
540 else if (!strcmp(str, "rg16i"))
541 return PlsRG16I;
542 else if (!strcmp(str, "rgb10_a2ui"))
543 return PlsRGB10A2UI;
544 else if (!strcmp(str, "rg16ui"))
545 return PlsRG16UI;
546 else if (!strcmp(str, "r32ui"))
547 return PlsR32UI;
548 else
549 return PlsNone;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100550}
551
552int main(int argc, char *argv[])
553{
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200554 CLIArguments args;
555 CLICallbacks cbs;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100556
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200557 cbs.add("--help", [](CLIParser &parser) {
558 print_help();
559 parser.end();
560 });
561 cbs.add("--output", [&args](CLIParser &parser) { args.output = parser.next_string(); });
562 cbs.add("--es", [&args](CLIParser &) {
563 args.es = true;
564 args.set_es = true;
565 });
566 cbs.add("--no-es", [&args](CLIParser &) {
567 args.es = false;
568 args.set_es = true;
569 });
570 cbs.add("--version", [&args](CLIParser &parser) {
571 args.version = parser.next_uint();
572 args.set_version = true;
573 });
Hans-Kristian Arntzenb847c882016-11-18 17:06:49 +0100574 cbs.add("--no-cfg-analysis", [&args](CLIParser &) { args.cfg_analysis = false; });
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200575 cbs.add("--dump-resources", [&args](CLIParser &) { args.dump_resources = true; });
576 cbs.add("--force-temporary", [&args](CLIParser &) { args.force_temporary = true; });
577 cbs.add("--flatten-ubo", [&args](CLIParser &) { args.flatten_ubo = true; });
578 cbs.add("--fixup-clipspace", [&args](CLIParser &) { args.fixup = true; });
579 cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); });
580 cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; });
581 cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); });
Bill Hollings5ad73f32017-03-19 21:06:21 -0400582 cbs.add("--metal", [&args](CLIParser &) { args.msl = true; }); // Legacy compatibility
583 cbs.add("--msl", [&args](CLIParser &) { args.msl = true; });
Bill Hollings5550c872017-03-12 17:42:51 -0400584 cbs.add("--msl-no-pack-ubos", [&args](CLIParser &) { args.msl_pack_ubos = false; });
Robert Konrad216a6812017-01-25 17:30:52 +0100585 cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; });
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +0200586 cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; });
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200587 cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; });
588 cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +0200589 cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); });
Hans-Kristian Arntzen36650c82017-05-22 15:30:43 +0200590 cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; });
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200591 cbs.add("--remap", [&args](CLIParser &parser) {
592 string src = parser.next_string();
593 string dst = parser.next_string();
594 uint32_t components = parser.next_uint();
595 args.remaps.push_back({ move(src), move(dst), components });
596 });
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100597
Panagiotis Christopoulos Charitos66e76d92016-09-20 10:17:41 +0200598 cbs.add("--remap-variable-type", [&args](CLIParser &parser) {
599 string var_name = parser.next_string();
600 string new_type = parser.next_string();
601 args.variable_type_remaps.push_back({ move(var_name), move(new_type) });
602 });
603
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200604 cbs.add("--pls-in", [&args](CLIParser &parser) {
605 auto fmt = pls_format(parser.next_string());
606 auto name = parser.next_string();
607 args.pls_in.push_back({ move(fmt), move(name) });
608 });
609 cbs.add("--pls-out", [&args](CLIParser &parser) {
610 auto fmt = pls_format(parser.next_string());
611 auto name = parser.next_string();
612 args.pls_out.push_back({ move(fmt), move(name) });
613 });
Hans-Kristian Arntzen2e3c6ec2017-02-16 11:17:12 +0100614 cbs.add("--shader-model", [&args](CLIParser &parser) {
615 args.shader_model = parser.next_uint();
616 args.set_shader_model = true;
617 });
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100618
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200619 cbs.add("--remove-unused-variables", [&args](CLIParser &) { args.remove_unused = true; });
620
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200621 cbs.default_handler = [&args](const char *value) { args.input = value; };
622 cbs.error_handler = [] { print_help(); };
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100623
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200624 CLIParser parser{ move(cbs), argc - 1, argv + 1 };
625 if (!parser.parse())
626 {
627 return EXIT_FAILURE;
628 }
629 else if (parser.ended_state)
630 {
631 return EXIT_SUCCESS;
632 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100633
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200634 if (!args.input)
635 {
636 fprintf(stderr, "Didn't specify input file.\n");
637 print_help();
638 return EXIT_FAILURE;
639 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100640
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200641 unique_ptr<CompilerGLSL> compiler;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100642
Hans-Kristian Arntzenbcb55602016-09-10 13:56:36 +0200643 bool combined_image_samplers = false;
644
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200645 if (args.cpp)
646 {
647 compiler = unique_ptr<CompilerGLSL>(new CompilerCPP(read_spirv_file(args.input)));
648 if (args.cpp_interface_name)
649 static_cast<CompilerCPP *>(compiler.get())->set_interface_name(args.cpp_interface_name);
650 }
Bill Hollings5ad73f32017-03-19 21:06:21 -0400651 else if (args.msl)
Bill Hollings5550c872017-03-12 17:42:51 -0400652 {
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200653 compiler = unique_ptr<CompilerMSL>(new CompilerMSL(read_spirv_file(args.input)));
Bill Hollings5550c872017-03-12 17:42:51 -0400654
655 auto *msl_comp = static_cast<CompilerMSL *>(compiler.get());
656 auto msl_opts = msl_comp->get_options();
657 msl_opts.pad_and_pack_uniform_structs = args.msl_pack_ubos;
658 msl_comp->set_options(msl_opts);
659 }
Robert Konrad216a6812017-01-25 17:30:52 +0100660 else if (args.hlsl)
661 compiler = unique_ptr<CompilerHLSL>(new CompilerHLSL(read_spirv_file(args.input)));
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200662 else
Hans-Kristian Arntzenbcb55602016-09-10 13:56:36 +0200663 {
664 combined_image_samplers = !args.vulkan_semantics;
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200665 compiler = unique_ptr<CompilerGLSL>(new CompilerGLSL(read_spirv_file(args.input)));
Hans-Kristian Arntzenbcb55602016-09-10 13:56:36 +0200666 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100667
Panagiotis Christopoulos Charitos66e76d92016-09-20 10:17:41 +0200668 if (!args.variable_type_remaps.empty())
669 {
670 auto remap_cb = [&](const SPIRType &, const string &name, string &out) -> void {
671 for (const VariableTypeRemap &remap : args.variable_type_remaps)
672 if (name == remap.variable_name)
673 out = remap.new_variable_type;
674 };
675
676 compiler->set_variable_type_remap_callback(move(remap_cb));
677 }
678
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +0200679 if (!args.entry.empty())
680 compiler->set_entry_point(args.entry);
681
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200682 if (!args.set_version && !compiler->get_options().version)
683 {
684 fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n");
685 print_help();
686 return EXIT_FAILURE;
687 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100688
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200689 CompilerGLSL::Options opts = compiler->get_options();
690 if (args.set_version)
691 opts.version = args.version;
692 if (args.set_es)
693 opts.es = args.es;
694 opts.force_temporary = args.force_temporary;
Hans-Kristian Arntzen36650c82017-05-22 15:30:43 +0200695 opts.separate_shader_objects = args.sso;
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200696 opts.vulkan_semantics = args.vulkan_semantics;
697 opts.vertex.fixup_clipspace = args.fixup;
Hans-Kristian Arntzenb847c882016-11-18 17:06:49 +0100698 opts.cfg_analysis = args.cfg_analysis;
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200699 compiler->set_options(opts);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100700
Hans-Kristian Arntzen2e3c6ec2017-02-16 11:17:12 +0100701 // Set HLSL specific options.
702 if (args.hlsl)
703 {
704 auto *hlsl = static_cast<CompilerHLSL *>(compiler.get());
705 auto hlsl_opts = hlsl->get_options();
706 if (args.set_shader_model)
707 {
708 if (args.shader_model < 30)
709 {
710 fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n");
711 return EXIT_FAILURE;
712 }
713
714 hlsl_opts.shader_model = args.shader_model;
715 }
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +0200716
717 if (args.hlsl_compat)
718 {
719 // Enable all compat options.
720 hlsl_opts.point_size_compat = true;
721 }
Hans-Kristian Arntzen2e3c6ec2017-02-16 11:17:12 +0100722 hlsl->set_options(hlsl_opts);
723 }
724
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200725 ShaderResources res;
726 if (args.remove_unused)
727 {
728 auto active = compiler->get_active_interface_variables();
729 res = compiler->get_shader_resources(active);
730 compiler->set_enabled_interface_variables(move(active));
731 }
732 else
733 res = compiler->get_shader_resources();
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100734
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200735 if (args.flatten_ubo)
Hans-Kristian Arntzen7f787f02017-01-21 10:27:14 +0100736 {
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200737 for (auto &ubo : res.uniform_buffers)
Arseny Kapoulkine24c66252017-01-16 14:19:49 -0800738 compiler->flatten_buffer_block(ubo.id);
Hans-Kristian Arntzen7f787f02017-01-21 10:27:14 +0100739 for (auto &ubo : res.push_constant_buffers)
740 compiler->flatten_buffer_block(ubo.id);
741 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100742
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200743 auto pls_inputs = remap_pls(args.pls_in, res.stage_inputs, &res.subpass_inputs);
744 auto pls_outputs = remap_pls(args.pls_out, res.stage_outputs, nullptr);
745 compiler->remap_pixel_local_storage(move(pls_inputs), move(pls_outputs));
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100746
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200747 for (auto &ext : args.extensions)
748 compiler->require_extension(ext);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100749
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200750 for (auto &remap : args.remaps)
751 {
752 if (remap_generic(*compiler, res.stage_inputs, remap))
753 continue;
754 if (remap_generic(*compiler, res.stage_outputs, remap))
755 continue;
756 if (remap_generic(*compiler, res.subpass_inputs, remap))
757 continue;
758 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100759
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200760 if (args.dump_resources)
761 {
762 print_resources(*compiler, res);
763 print_push_constant_resources(*compiler, res.push_constant_buffers);
Hans-Kristian Arntzen6bd545b2016-09-17 15:16:07 +0200764 print_spec_constants(*compiler);
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200765 }
766
Hans-Kristian Arntzenbcb55602016-09-10 13:56:36 +0200767 if (combined_image_samplers)
768 {
769 compiler->build_combined_image_samplers();
770 // Give the remapped combined samplers new names.
771 for (auto &remap : compiler->get_combined_image_samplers())
772 {
Hans-Kristian Arntzena5f0abd2016-09-11 12:13:31 +0200773 compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id),
Hans-Kristian Arntzenbcb55602016-09-10 13:56:36 +0200774 compiler->get_name(remap.sampler_id)));
775 }
776 }
777
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +0200778 string glsl;
779 for (uint32_t i = 0; i < args.iterations; i++)
780 glsl = compiler->compile();
781
782 if (args.output)
783 write_string_to_file(args.output, glsl.c_str());
784 else
785 printf("%s", glsl.c_str());
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100786}