1 /*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include "compiler/shader_enums.h"
25 #include "nir/nir.h"
26 #include "rogue.h"
27 #include "util/macros.h"
28 #include "util/os_file.h"
29 #include "util/ralloc.h"
30 #include "util/u_dynarray.h"
31
32 #include <getopt.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 /* Number of hex columns to print before starting a new line. */
39 #define ARRAY_PRINT_COLS 16
40
41 /**
42 * \file vk_compiler.c
43 *
44 * \brief Rogue offline Vulkan shader compiler.
45 */
46
47 static const struct option cmdline_opts[] = {
48 /* Arguments. */
49 { "stage", required_argument, NULL, 's' },
50 { "file", required_argument, NULL, 'f' },
51 { "entry", required_argument, NULL, 'e' },
52
53 /* Options. */
54 { "help", no_argument, NULL, 'h' },
55 { "out", required_argument, NULL, 'o' },
56
57 { NULL, 0, NULL, 0 },
58 };
59
60 typedef struct compiler_opts {
61 gl_shader_stage stage;
62 char *file;
63 char *entry;
64 char *out_file;
65 } compiler_opts;
66
usage(const char * argv0)67 static void usage(const char *argv0)
68 {
69 /* clang-format off */
70 printf("Rogue offline Vulkan shader compiler.\n");
71 printf("Usage: %s -s <stage> -f <file> [-e <entry>] [-o <file>] [-h]\n", argv0);
72 printf("\n");
73
74 printf("Required arguments:\n");
75 printf("\t-s, --stage <stage> Shader stage (supported options: frag, vert).\n");
76 printf("\t-f, --file <file> Shader SPIR-V filename.\n");
77 printf("\n");
78
79 printf("Options:\n");
80 printf("\t-h, --help Prints this help message.\n");
81 printf("\t-e, --entry <entry> Overrides the shader entry-point name (default: 'main').\n");
82 printf("\t-o, --out <file> Overrides the output filename (default: 'out.bin').\n");
83 printf("\n");
84 /* clang-format on */
85 }
86
parse_cmdline(int argc,char * argv[],struct compiler_opts * opts)87 static bool parse_cmdline(int argc, char *argv[], struct compiler_opts *opts)
88 {
89 int opt;
90 int longindex;
91
92 while (
93 (opt = getopt_long(argc, argv, "hs:f:e:o:", cmdline_opts, &longindex)) !=
94 -1) {
95 switch (opt) {
96 case 'e':
97 if (opts->entry)
98 continue;
99
100 opts->entry = optarg;
101 break;
102
103 case 'f':
104 if (opts->file)
105 continue;
106
107 opts->file = optarg;
108 break;
109
110 case 'o':
111 if (opts->out_file)
112 continue;
113
114 opts->out_file = optarg;
115 break;
116
117 case 's':
118 if (opts->stage != MESA_SHADER_NONE)
119 continue;
120
121 if (!strcmp(optarg, "frag") || !strcmp(optarg, "f"))
122 opts->stage = MESA_SHADER_FRAGMENT;
123 else if (!strcmp(optarg, "vert") || !strcmp(optarg, "v"))
124 opts->stage = MESA_SHADER_VERTEX;
125 else {
126 fprintf(stderr, "Unsupported stage \"%s\".\n", optarg);
127 usage(argv[0]);
128 return false;
129 }
130
131 break;
132
133 case 'h':
134 default:
135 usage(argv[0]);
136 return false;
137 }
138 }
139
140 if (opts->stage == MESA_SHADER_NONE || !opts->file) {
141 fprintf(stderr,
142 "%s: --stage and --file are required arguments.\n",
143 argv[0]);
144 usage(argv[0]);
145 return false;
146 }
147
148 if (!opts->out_file)
149 opts->out_file = "out.bin";
150
151 if (!opts->entry)
152 opts->entry = "main";
153
154 return true;
155 }
156
main(int argc,char * argv[])157 int main(int argc, char *argv[])
158 {
159 /* Command-line options. */
160 /* N.B. MESA_SHADER_NONE != 0 */
161 compiler_opts opts = { .stage = MESA_SHADER_NONE, 0 };
162
163 /* Input file data. */
164 char *input_data;
165 size_t input_size;
166
167 /* Compiler context. */
168 struct rogue_compiler *compiler;
169
170 /* Multi-stage build context. */
171 struct rogue_build_ctx *ctx;
172
173 /* Output file. */
174 FILE *fp;
175 size_t bytes_written;
176
177 /* Parse command-line options. */
178 if (!parse_cmdline(argc, argv, &opts))
179 return 1;
180
181 /* Load SPIR-V input file. */
182 input_data = os_read_file(opts.file, &input_size);
183 if (!input_data) {
184 fprintf(stderr, "Failed to read file \"%s\".\n", opts.file);
185 return 1;
186 }
187
188 /* Create compiler context. */
189 compiler = rogue_compiler_create(NULL);
190 if (!compiler) {
191 fprintf(stderr, "Failed to set up compiler context.\n");
192 goto err_free_input;
193 }
194
195 /* Create build context. */
196 ctx = rogue_build_context_create(compiler, NULL);
197 if (!ctx) {
198 fprintf(stderr, "Failed to set up build context.\n");
199 goto err_destroy_compiler;
200 }
201
202 /* SPIR-V -> NIR. */
203 ctx->nir[opts.stage] = rogue_spirv_to_nir(ctx,
204 opts.stage,
205 opts.entry,
206 input_size / sizeof(uint32_t),
207 (uint32_t *)input_data,
208 0,
209 NULL);
210 if (!ctx->nir[opts.stage]) {
211 fprintf(stderr, "Failed to translate SPIR-V input to NIR.\n");
212 goto err_free_build_context;
213 }
214
215 /* NIR -> Rogue. */
216 ctx->rogue[opts.stage] = rogue_nir_to_rogue(ctx, ctx->nir[opts.stage]);
217 if (!ctx->rogue[opts.stage]) {
218 fprintf(stderr, "Failed to translate NIR input to Rogue.\n");
219 goto err_free_build_context;
220 }
221
222 rogue_encode_shader(ctx, ctx->rogue[opts.stage], &ctx->binary[opts.stage]);
223
224 /* Write shader binary to disk. */
225 fp = fopen(opts.out_file, "wb");
226 if (!fp) {
227 fprintf(stderr, "Failed to open output file \"%s\".\n", opts.out_file);
228 goto err_free_build_context;
229 }
230
231 bytes_written = fwrite(util_dynarray_begin(&ctx->binary[opts.stage]),
232 1,
233 ctx->binary[opts.stage].size,
234 fp);
235 if (bytes_written != ctx->binary[opts.stage].size) {
236 fprintf(
237 stderr,
238 "Failed to write to output file \"%s\" (%zu bytes of %u written).\n",
239 opts.out_file,
240 bytes_written,
241 ctx->binary[opts.stage].size);
242 goto err_close_outfile;
243 }
244
245 /* Clean up. */
246 fclose(fp);
247 ralloc_free(ctx);
248 ralloc_free(compiler);
249 free(input_data);
250
251 return 0;
252
253 err_close_outfile:
254 fclose(fp);
255 err_free_build_context:
256 ralloc_free(ctx);
257 err_destroy_compiler:
258 ralloc_free(compiler);
259 err_free_input:
260 free(input_data);
261
262 return 1;
263 }
264