blob: 9cf1eab3e3feadc5b8c81e4f8d4f25acfb622e17 [file] [log] [blame]
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001/*
2 * Copyright 2015-2016 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 "spir2cpp.hpp"
18#include <cstdio>
19#include <stdexcept>
20#include <functional>
21#include <limits>
22#include <memory>
23#include <unordered_map>
24#include <cstring>
25#include <unordered_set>
26
27using namespace spv;
28using namespace spir2cross;
29using namespace std;
30
31struct CLIParser;
32struct CLICallbacks
33{
34 void add(const char *cli, const function<void (CLIParser&)> &func)
35 {
36 callbacks[cli] = func;
37 }
38 unordered_map<string, function<void (CLIParser&)>> callbacks;
39 function<void ()> error_handler;
40 function<void (const char*)> default_handler;
41};
42
43struct CLIParser
44{
45 CLIParser(CLICallbacks cbs, int argc, char *argv[])
46 : cbs(move(cbs)), argc(argc), argv(argv)
47 {}
48
49 bool parse()
50 {
51 try
52 {
53 while (argc && !ended_state)
54 {
55 const char *next = *argv++;
56 argc--;
57
58 if (*next != '-' && cbs.default_handler)
59 {
60 cbs.default_handler(next);
61 }
62 else
63 {
64 auto itr = cbs.callbacks.find(next);
65 if (itr == ::end(cbs.callbacks))
66 {
67 throw logic_error("Invalid argument.\n");
68 }
69
70 itr->second(*this);
71 }
72 }
73
74 return true;
75 }
76 catch (...)
77 {
78 if (cbs.error_handler)
79 {
80 cbs.error_handler();
81 }
82 return false;
83 }
84 }
85
86 void end()
87 {
88 ended_state = true;
89 }
90
91 uint32_t next_uint()
92 {
93 if (!argc)
94 {
95 throw logic_error("Tried to parse uint, but nothing left in arguments.\n");
96 }
97
98 uint32_t val = stoul(*argv);
99 if (val > numeric_limits<uint32_t>::max())
100 {
101 throw out_of_range("next_uint() out of range.\n");
102 }
103
104 argc--;
105 argv++;
106
107 return val;
108 }
109
110 double next_double()
111 {
112 if (!argc)
113 {
114 throw logic_error("Tried to parse double, but nothing left in arguments.\n");
115 }
116
117 double val = stod(*argv);
118
119 argc--;
120 argv++;
121
122 return val;
123 }
124
125 const char *next_string()
126 {
127 if (!argc)
128 {
129 throw logic_error("Tried to parse string, but nothing left in arguments.\n");
130 }
131
132 const char *ret = *argv;
133 argc--;
134 argv++;
135 return ret;
136 }
137
138 CLICallbacks cbs;
139 int argc;
140 char **argv;
141 bool ended_state = false;
142};
143
144static vector<uint32_t> read_spirv_file(const char *path)
145{
146 FILE *file = fopen(path, "rb");
147 if (!file)
148 {
149 fprintf(stderr, "Failed to open SPIRV file: %s\n", path);
150 return {};
151 }
152
153 fseek(file, 0, SEEK_END);
154 long len = ftell(file) / sizeof(uint32_t);
155 rewind(file);
156
157 vector<uint32_t> spirv(len);
158 if (fread(spirv.data(), sizeof(uint32_t), len, file) != size_t(len))
159 spirv.clear();
160
161 fclose(file);
162 return spirv;
163}
164
165static bool write_string_to_file(const char *path, const char *string)
166{
167 FILE *file = fopen(path, "w");
168 if (!file)
169 {
170 fprintf(file, "Failed to write file: %s\n", path);
171 return false;
172 }
173
174 fprintf(file, "%s", string);
175 fclose(file);
176 return true;
177}
178
179static void print_resources(const Compiler &compiler, const char *tag, const vector<Resource> &resources)
180{
181 fprintf(stderr, "%s\n", tag);
182 fprintf(stderr, "=============\n\n");
183 for (auto &res : resources)
184 {
185 auto &type = compiler.get_type(res.type_id);
186 auto mask = compiler.get_decoration_mask(res.id);
187
188 // If we don't have a name, use the fallback for the type instead of the variable
189 // for SSBOs and UBOs since those are the only meaningful names to use externally.
190 // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
191 bool is_push_constant = compiler.get_storage_class(res.id) == StorageClassPushConstant;
192 bool is_block = (compiler.get_decoration_mask(type.self) &
193 ((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) != 0;
194 uint32_t fallback_id = !is_push_constant && is_block ? res.type_id : res.id;
195
196 fprintf(stderr, " ID %03u : %s", res.id,
197 !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str());
198
199 if (mask & (1ull << DecorationLocation))
200 fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation));
201 if (mask & (1ull << DecorationDescriptorSet))
202 fprintf(stderr, " (Set : %u)", compiler.get_decoration(res.id, DecorationDescriptorSet));
203 if (mask & (1ull << DecorationBinding))
204 fprintf(stderr, " (Binding : %u)", compiler.get_decoration(res.id, DecorationBinding));
205 fprintf(stderr, "\n");
206 }
207 fprintf(stderr, "=============\n\n");
208}
209
210static void print_resources(const Compiler &compiler, const ShaderResources &res)
211{
212 print_resources(compiler, "subpass inputs", res.subpass_inputs);
213 print_resources(compiler, "inputs", res.stage_inputs);
214 print_resources(compiler, "outputs", res.stage_outputs);
215 print_resources(compiler, "textures", res.sampled_images);
216 print_resources(compiler, "images", res.storage_images);
217 print_resources(compiler, "ssbos", res.storage_buffers);
218 print_resources(compiler, "ubos", res.uniform_buffers);
219 print_resources(compiler, "push", res.push_constant_buffers);
220 print_resources(compiler, "counters", res.atomic_counters);
221}
222
223static void print_push_constant_resources(const Compiler &compiler, const vector<Resource> &res)
224{
225 for (auto &block : res)
226 {
227 auto ranges = compiler.get_active_buffer_ranges(block.id);
228 fprintf(stderr, "Active members in buffer: %s\n",
229 !block.name.empty() ? block.name.c_str() : compiler.get_fallback_name(block.id).c_str());
230
231 fprintf(stderr, "==================\n\n");
232 for (auto &range : ranges)
233 {
234 const auto &name = compiler.get_member_name(block.type_id, range.index);
235
236 fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n",
237 range.index, !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(),
238 unsigned(range.offset), unsigned(range.range));
239 }
240 fprintf(stderr, "==================\n\n");
241 }
242}
243
244struct PLSArg
245{
246 PlsFormat format;
247 string name;
248};
249
250struct CLIArguments
251{
252 const char *input = nullptr;
253 const char *output = nullptr;
254 uint32_t version = 0;
255 bool es = false;
256 bool set_version = false;
257 bool set_es = false;
258 bool dump_resources = false;
259 bool force_temporary = false;
260 bool flatten_ubo = false;
261 bool fixup = false;
262 vector<PLSArg> pls_in;
263 vector<PLSArg> pls_out;
264
265 uint32_t iterations = 1;
266 bool cpp = false;
267};
268
269static void print_help()
270{
271 fprintf(stderr, "Usage: spir2cross [--output <output path>] [SPIR-V file] [--es] [--no-es] [--version <GLSL version>] [--dump-resources] [--help] [--force-temporary] [-cpp] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] [--pls-in format input-name] [--pls-out format output-name]\n");
272}
273
274static vector<PlsRemap> remap_pls(const vector<PLSArg> &pls_variables, const vector<Resource> &resources, const vector<Resource> *secondary_resources)
275{
276 vector<PlsRemap> ret;
277
278 for (auto &pls : pls_variables)
279 {
280 bool found = false;
281 for (auto &res : resources)
282 {
283 if (res.name == pls.name)
284 {
285 ret.push_back({ res.id, pls.format });
286 found = true;
287 break;
288 }
289 }
290
291 if (!found && secondary_resources)
292 {
293 for (auto &res : *secondary_resources)
294 {
295 if (res.name == pls.name)
296 {
297 ret.push_back({ res.id, pls.format });
298 found = true;
299 break;
300 }
301 }
302 }
303
304 if (!found)
305 fprintf(stderr, "Did not find stage input/output/target with name \"%s\".\n",
306 pls.name.c_str());
307 }
308
309 return ret;
310}
311
312static PlsFormat pls_format(const char *str)
313{
314 if (!strcmp(str, "r11f_g11f_b10f")) return PlsR11FG11FB10F;
315 else if (!strcmp(str, "r32f")) return PlsR32F;
316 else if (!strcmp(str, "rg16f")) return PlsRG16F;
317 else if (!strcmp(str, "rg16")) return PlsRG16;
318 else if (!strcmp(str, "rgb10_a2")) return PlsRGB10A2;
319 else if (!strcmp(str, "rgba8")) return PlsRGBA8;
320 else if (!strcmp(str, "rgba8i")) return PlsRGBA8I;
321 else if (!strcmp(str, "rgba8ui")) return PlsRGBA8UI;
322 else if (!strcmp(str, "rg16i")) return PlsRG16I;
323 else if (!strcmp(str, "rgb10_a2ui")) return PlsRGB10A2UI;
324 else if (!strcmp(str, "rg16ui")) return PlsRG16UI;
325 else if (!strcmp(str, "r32ui")) return PlsR32UI;
326 else return PlsNone;
327}
328
329int main(int argc, char *argv[])
330{
331 CLIArguments args;
332 CLICallbacks cbs;
333
334 cbs.add("--help", [](CLIParser &parser) { print_help(); parser.end(); });
335 cbs.add("--output", [&args](CLIParser &parser) { args.output = parser.next_string(); });
336 cbs.add("--es", [&args](CLIParser &) { args.es = true; args.set_es = true; });
337 cbs.add("--no-es", [&args](CLIParser &) { args.es = false; args.set_es = true; });
338 cbs.add("--version", [&args](CLIParser &parser) { args.version = parser.next_uint(); args.set_version = true; });
339 cbs.add("--dump-resources", [&args](CLIParser &) { args.dump_resources = true; });
340 cbs.add("--force-temporary", [&args](CLIParser &) { args.force_temporary = true; });
341 cbs.add("--flatten-ubo", [&args](CLIParser &) { args.flatten_ubo = true; });
342 cbs.add("--fixup-clipspace", [&args](CLIParser &) { args.fixup = true; });
343 cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); });
344 cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; });
345
346 cbs.add("--pls-in", [&args](CLIParser &parser) {
347 auto fmt = pls_format(parser.next_string());
348 auto name = parser.next_string();
349 args.pls_in.push_back({ move(fmt), move(name) });
350 });
351 cbs.add("--pls-out", [&args](CLIParser &parser) {
352 auto fmt = pls_format(parser.next_string());
353 auto name = parser.next_string();
354 args.pls_out.push_back({ move(fmt), move(name) });
355 });
356
357 cbs.default_handler = [&args](const char *value) { args.input = value; };
358 cbs.error_handler = []{ print_help(); };
359
360 CLIParser parser{move(cbs), argc - 1, argv + 1};
361 if (!parser.parse())
362 {
363 return EXIT_FAILURE;
364 }
365 else if (parser.ended_state)
366 {
367 return EXIT_SUCCESS;
368 }
369
370 if (!args.input)
371 {
372 fprintf(stderr, "Didn't specify input file.\n");
373 print_help();
374 return EXIT_FAILURE;
375 }
376
377 unique_ptr<CompilerGLSL> compiler;
378
379 if (args.cpp)
380 compiler = unique_ptr<CompilerGLSL>(new CompilerCPP(read_spirv_file(args.input)));
381 else
382 compiler = unique_ptr<CompilerGLSL>(new CompilerGLSL(read_spirv_file(args.input)));
383
384 if (!args.set_version && !compiler->get_options().version)
385 {
386 fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n");
387 print_help();
388 return EXIT_FAILURE;
389 }
390
391 CompilerGLSL::Options opts = compiler->get_options();
392 if (args.set_version)
393 opts.version = args.version;
394 if (args.set_es)
395 opts.es = args.es;
396 opts.force_temporary = args.force_temporary;
397 opts.vertex.fixup_clipspace = args.fixup;
398 compiler->set_options(opts);
399
400 auto res = compiler->get_shader_resources();
401
402 if (args.flatten_ubo)
403 for (auto &ubo : res.uniform_buffers)
404 compiler->flatten_interface_block(ubo.id);
405
406 auto pls_inputs = remap_pls(args.pls_in, res.stage_inputs, &res.subpass_inputs);
407 auto pls_outputs = remap_pls(args.pls_out, res.stage_outputs, nullptr);
408 compiler->remap_pixel_local_storage(move(pls_inputs), move(pls_outputs));
409
410 if (args.dump_resources)
411 {
412 print_resources(*compiler, res);
413 print_push_constant_resources(*compiler, res.push_constant_buffers);
414 }
415
416 string glsl;
417 for (uint32_t i = 0; i < args.iterations; i++)
418 glsl = compiler->compile();
419
420 if (args.output)
421 write_string_to_file(args.output, glsl.c_str());
422 else
423 printf("%s", glsl.c_str());
424}
425