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 "rogue_build_data.h" 28#include "rogue_compiler.h" 29#include "rogue_dump.h" 30#include "util/os_file.h" 31#include "util/ralloc.h" 32 33#include <getopt.h> 34#include <limits.h> 35#include <stdbool.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39 40/* Number of hex columns to dump before starting a new line. */ 41#define ARRAY_DUMP_COLS 16 42 43/** 44 * \file compiler.c 45 * 46 * \brief Rogue offline compiler. 47 */ 48 49static const struct option cmdline_opts[] = { 50 /* Arguments. */ 51 { "stage", required_argument, NULL, 's' }, 52 { "file", required_argument, NULL, 'f' }, 53 { "entry", required_argument, NULL, 'e' }, 54 55 /* Options. */ 56 { "help", no_argument, NULL, 'h' }, 57 { "out", required_argument, NULL, 'o' }, 58 59 { "dump-c-array", no_argument, NULL, 'c' }, 60 { "dump-rogue", no_argument, NULL, 'r' }, 61 { "dump-nir", no_argument, NULL, 'n' }, 62 63 { NULL, 0, NULL, 0 }, 64}; 65 66struct compiler_opts { 67 gl_shader_stage stage; 68 char *file; 69 char *entry; 70 char *out_file; 71 bool dump_c_array; 72 bool dump_rogue; 73 bool dump_nir; 74}; 75 76static void usage(const char *argv0) 77{ 78 /* clang-format off */ 79 printf("Rogue offline compiler.\n"); 80 printf("Usage: %s -s <stage> -f <file> [-e <entry>] [-o <file>] [-c] [-r] [-n] [-h]\n", argv0); 81 printf("\n"); 82 83 printf("Required arguments:\n"); 84 printf("\t-s, --stage <stage> Shader stage (supported options: frag, vert).\n"); 85 printf("\t-f, --file <file> Shader SPIR-V filename.\n"); 86 printf("\n"); 87 88 printf("Options:\n"); 89 printf("\t-h, --help Prints this help message.\n"); 90 printf("\t-e, --entry <entry> Overrides the shader entry-point name (default: 'main').\n"); 91 printf("\t-o, --out <file> Overrides the output filename (default: 'out.bin').\n"); 92 printf("\n"); 93 94 printf("\t-c, --dump-c-array Print the shader binary as a C byte array.\n"); 95 printf("\t-r, --dump-rogue Prints the shader Rogue assembly.\n"); 96 printf("\t-n, --dump-nir Prints the shader NIR.\n"); 97 printf("\n"); 98 /* clang-format on */ 99} 100 101static bool parse_cmdline(int argc, char *argv[], struct compiler_opts *opts) 102{ 103 int opt; 104 int longindex; 105 106 while ( 107 (opt = 108 getopt_long(argc, argv, "crnhs:f:e:o:", cmdline_opts, &longindex)) != 109 -1) { 110 switch (opt) { 111 case 'c': 112 opts->dump_c_array = true; 113 break; 114 115 case 'e': 116 if (opts->entry) 117 continue; 118 119 opts->entry = optarg; 120 break; 121 122 case 'f': 123 if (opts->file) 124 continue; 125 126 opts->file = optarg; 127 break; 128 129 case 'n': 130 opts->dump_nir = true; 131 break; 132 133 case 'o': 134 if (opts->out_file) 135 continue; 136 137 opts->out_file = optarg; 138 break; 139 140 case 'r': 141 opts->dump_rogue = true; 142 break; 143 144 case 's': 145 if (opts->stage != MESA_SHADER_NONE) 146 continue; 147 148 if (!strcmp(optarg, "frag")) 149 opts->stage = MESA_SHADER_FRAGMENT; 150 else if (!strcmp(optarg, "vert")) 151 opts->stage = MESA_SHADER_VERTEX; 152 else { 153 fprintf(stderr, "Invalid stage \"%s\".\n", optarg); 154 usage(argv[0]); 155 return false; 156 } 157 158 break; 159 160 case 'h': 161 default: 162 usage(argv[0]); 163 return false; 164 } 165 } 166 167 if (opts->stage == MESA_SHADER_NONE || !opts->file) { 168 fprintf(stderr, 169 "%s: --stage and --file are required arguments.\n", 170 argv[0]); 171 usage(argv[0]); 172 return false; 173 } 174 175 if (!opts->out_file) 176 opts->out_file = "out.bin"; 177 178 if (!opts->entry) 179 opts->entry = "main"; 180 181 return true; 182} 183 184int main(int argc, char *argv[]) 185{ 186 /* Command-line options. */ 187 /* N.B. MESA_SHADER_NONE != 0 */ 188 struct compiler_opts opts = { .stage = MESA_SHADER_NONE, 0 }; 189 190 /* Input file data. */ 191 char *input_data; 192 size_t input_size; 193 194 /* Compiler context. */ 195 struct rogue_compiler *compiler; 196 197 /* Multi-stage build context. */ 198 struct rogue_build_ctx *ctx; 199 200 /* Output file. */ 201 FILE *fp; 202 size_t bytes_written; 203 204 /* Parse command-line options. */ 205 if (!parse_cmdline(argc, argv, &opts)) 206 return 1; 207 208 /* Load SPIR-V input file. */ 209 input_data = os_read_file(opts.file, &input_size); 210 if (!input_data) { 211 fprintf(stderr, "Failed to read file \"%s\".\n", opts.file); 212 return 1; 213 } 214 215 /* Create compiler context. */ 216 compiler = rogue_compiler_create(NULL); 217 if (!compiler) { 218 fprintf(stderr, "Failed to set up compiler context.\n"); 219 goto err_free_input; 220 } 221 222 ctx = rogue_create_build_context(compiler); 223 if (!ctx) { 224 fprintf(stderr, "Failed to set up build context.\n"); 225 goto err_destroy_compiler; 226 } 227 228 /* SPIR-V -> NIR. */ 229 ctx->nir[opts.stage] = rogue_spirv_to_nir(ctx, 230 opts.stage, 231 opts.entry, 232 input_size / sizeof(uint32_t), 233 (uint32_t *)input_data, 234 0, 235 NULL); 236 if (!ctx->nir[opts.stage]) { 237 fprintf(stderr, "Failed to translate SPIR-V input to NIR.\n"); 238 goto err_free_build_context; 239 } 240 241 /* Dump NIR shader. */ 242 if (opts.dump_nir) 243 nir_print_shader(ctx->nir[opts.stage], stdout); 244 245 /* NIR -> Rogue. */ 246 ctx->rogue[opts.stage] = rogue_nir_to_rogue(ctx, ctx->nir[opts.stage]); 247 if (!ctx->rogue[opts.stage]) { 248 fprintf(stderr, "Failed to translate NIR input to Rogue.\n"); 249 goto err_free_build_context; 250 } 251 252 /* Dump Rogue shader. */ 253 if (opts.dump_rogue) 254 rogue_dump_shader(ctx->rogue[opts.stage], stdout); 255 256 /* Rogue -> Binary. */ 257 ctx->binary[opts.stage] = rogue_to_binary(ctx, ctx->rogue[opts.stage]); 258 if (!ctx->binary[opts.stage]) { 259 fprintf(stderr, "Failed to translate Rogue to binary.\n"); 260 goto err_free_build_context; 261 } 262 263 /* Dump binary as a C array. */ 264 if (opts.dump_c_array) { 265 printf("uint8_t shader_bytes[%zu] = {", ctx->binary[opts.stage]->size); 266 for (size_t u = 0U; u < ctx->binary[opts.stage]->size; ++u) { 267 if (!(u % ARRAY_DUMP_COLS)) 268 printf("\n\t"); 269 270 printf("0x%02x, ", ctx->binary[opts.stage]->data[u]); 271 } 272 printf("\n};\n"); 273 } 274 275 /* Write shader binary to disk. */ 276 fp = fopen(opts.out_file, "wb"); 277 if (!fp) { 278 fprintf(stderr, "Failed to open output file \"%s\".\n", opts.out_file); 279 goto err_free_build_context; 280 } 281 282 bytes_written = fwrite(ctx->binary[opts.stage]->data, 283 1, 284 ctx->binary[opts.stage]->size, 285 fp); 286 if (bytes_written != ctx->binary[opts.stage]->size) { 287 fprintf( 288 stderr, 289 "Failed to write to output file \"%s\" (%zu bytes of %zu written).\n", 290 opts.out_file, 291 bytes_written, 292 ctx->binary[opts.stage]->size); 293 goto err_close_outfile; 294 } 295 296 /* Clean up. */ 297 fclose(fp); 298 ralloc_free(ctx); 299 rogue_compiler_destroy(compiler); 300 free(input_data); 301 302 return 0; 303 304err_close_outfile: 305 fclose(fp); 306err_free_build_context: 307 ralloc_free(ctx); 308err_destroy_compiler: 309 rogue_compiler_destroy(compiler); 310err_free_input: 311 free(input_data); 312 313 return 1; 314} 315