xref: /aosp_15_r20/external/mesa3d/src/microsoft/spirv_to_dxil/spirv2dxil.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2015 Intel Corporation
3  * Copyright © Microsoft Corporation
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 /*
26  * A simple executable that opens a SPIR-V shader, converts it to DXIL via
27  * NIR, and dumps out the result.  This should be useful for testing the
28  * nir_to_dxil code.  Based on spirv2nir.c.
29  */
30 
31 #include "nir_to_dxil.h"
32 #include "dxil_validator.h"
33 #include "spirv/nir_spirv.h"
34 #include "spirv_to_dxil.h"
35 #include "dxil_spirv_nir.h"
36 
37 #include "util/os_file.h"
38 #include <errno.h>
39 #include <getopt.h>
40 #include <stdio.h>
41 #include <string.h>
42 
43 #define WORD_SIZE 4
44 
45 static gl_shader_stage
stage_to_enum(char * stage)46 stage_to_enum(char *stage)
47 {
48    if (!strcmp(stage, "vertex"))
49       return MESA_SHADER_VERTEX;
50    else if (!strcmp(stage, "tess-ctrl"))
51       return MESA_SHADER_TESS_CTRL;
52    else if (!strcmp(stage, "tess-eval"))
53       return MESA_SHADER_TESS_EVAL;
54    else if (!strcmp(stage, "geometry"))
55       return MESA_SHADER_GEOMETRY;
56    else if (!strcmp(stage, "fragment"))
57       return MESA_SHADER_FRAGMENT;
58    else if (!strcmp(stage, "compute"))
59       return MESA_SHADER_COMPUTE;
60    else
61       return MESA_SHADER_NONE;
62 }
63 
64 static void
log_spirv_to_dxil_error(void * priv,const char * msg)65 log_spirv_to_dxil_error(void *priv, const char *msg)
66 {
67    fprintf(stderr, "spirv_to_dxil error: %s", msg);
68 }
69 
70 struct shader {
71    const char *entry_point;
72    const char *output_file;
73    nir_shader *nir;
74 };
75 
76 bool validate = false, debug = false;
77 enum dxil_validator_version val_ver = DXIL_VALIDATOR_1_4;
78 
79 struct nir_shader_compiler_options nir_options;
80 
81 static bool
compile_shader(const char * filename,gl_shader_stage shader_stage,struct shader * shader,struct dxil_spirv_runtime_conf * conf)82 compile_shader(const char *filename, gl_shader_stage shader_stage, struct shader *shader,
83                struct dxil_spirv_runtime_conf *conf)
84 {
85    size_t file_size;
86    char *file_contents = os_read_file(filename, &file_size);
87    if (!file_contents) {
88       fprintf(stderr, "Failed to open %s\n", filename);
89       return false;
90    }
91 
92    if (file_size % WORD_SIZE != 0) {
93       fprintf(stderr, "%s size == %zu is not a multiple of %d\n", filename,
94               file_size, WORD_SIZE);
95       free(file_contents);
96       return false;
97    }
98 
99    size_t word_count = file_size / WORD_SIZE;
100 
101    const struct spirv_to_nir_options *spirv_opts = dxil_spirv_nir_get_spirv_options();
102 
103    shader->nir = spirv_to_nir(
104       (const uint32_t *)file_contents, word_count, NULL,
105       0, (gl_shader_stage)shader_stage, shader->entry_point,
106       spirv_opts, &nir_options);
107    free(file_contents);
108    if (!shader->nir) {
109       fprintf(stderr, "SPIR-V to NIR failed\n");
110       return false;
111    }
112 
113    nir_validate_shader(shader->nir,
114                        "Validate before feeding NIR to the DXIL compiler");
115 
116    dxil_spirv_nir_prep(shader->nir);
117 
118    struct dxil_spirv_metadata metadata = { 0 };
119    dxil_spirv_nir_passes(shader->nir, conf, &metadata);
120 
121    if (debug)
122       nir_print_shader(shader->nir, stderr);
123 
124    return true;
125 }
126 
127 #if DETECT_OS_WINDOWS
128 
129 static bool
validate_dxil(struct blob * blob)130 validate_dxil(struct blob *blob)
131 {
132    struct dxil_validator *val = dxil_create_validator(NULL);
133 
134    char *err;
135    bool res = dxil_validate_module(val, blob->data,
136                                    blob->size, &err);
137    if (!res && err)
138       fprintf(stderr, "DXIL: %s\n\n", err);
139 
140    dxil_destroy_validator(val);
141    return res;
142 }
143 
144 #else
145 
146 static bool
validate_dxil(struct blob * blob)147 validate_dxil(struct blob *blob)
148 {
149    fprintf(stderr, "DXIL validation only available in Windows.\n");
150    return false;
151 }
152 
153 #endif
154 
155 int
main(int argc,char ** argv)156 main(int argc, char **argv)
157 {
158    glsl_type_singleton_init_or_ref();
159    int ch;
160 
161    static struct option long_options[] = {
162       {"stage", required_argument, 0, 's'},
163       {"entry", required_argument, 0, 'e'},
164       {"output", required_argument, 0, 'o'},
165       {"validate", no_argument, 0, 'v'},
166       {"debug", no_argument, 0, 'd'},
167       {"shadermodel", required_argument, 0, 'm'},
168       {"validatorver", required_argument, 0, 'x'},
169       {0, 0, 0, 0}};
170 
171    struct shader shaders[MESA_SHADER_COMPUTE + 1];
172    memset(shaders, 0, sizeof(shaders));
173    struct shader cur_shader = {
174       .entry_point = "main",
175       .output_file = NULL,
176    };
177    gl_shader_stage shader_stage = MESA_SHADER_FRAGMENT;
178 
179    struct dxil_spirv_runtime_conf conf;
180    memset(&conf, 0, sizeof(conf));
181    conf.runtime_data_cbv.base_shader_register = 0;
182    conf.runtime_data_cbv.register_space = 31;
183    conf.push_constant_cbv.base_shader_register = 0;
184    conf.push_constant_cbv.register_space = 30;
185    conf.first_vertex_and_base_instance_mode = DXIL_SPIRV_SYSVAL_TYPE_ZERO;
186    conf.declared_read_only_images_as_srvs = true;
187    conf.shader_model_max = SHADER_MODEL_6_2;
188 
189    const unsigned supported_bit_sizes = 16 | 32 | 64;
190    dxil_get_nir_compiler_options(&nir_options, conf.shader_model_max, supported_bit_sizes, supported_bit_sizes);
191    // We will manually handle base_vertex when vertex_id and instance_id have
192    // have been already converted to zero-base.
193    nir_options.lower_base_vertex = false;
194 
195    bool any_shaders = false;
196    while ((ch = getopt_long(argc, argv, "-s:e:o:m:x:vd", long_options, NULL)) !=
197             -1) {
198       switch (ch)
199       {
200       case 's':
201          shader_stage = stage_to_enum(optarg);
202          if (shader_stage == MESA_SHADER_NONE) {
203             fprintf(stderr, "Unknown stage %s\n", optarg);
204             return 1;
205          }
206          break;
207       case 'e':
208          cur_shader.entry_point = optarg;
209          break;
210       case 'o':
211          cur_shader.output_file = optarg;
212          break;
213       case 'v':
214          validate = true;
215          break;
216       case 'd':
217          debug = true;
218          break;
219       case 'm':
220          conf.shader_model_max = SHADER_MODEL_6_0 + atoi(optarg);
221          conf.first_vertex_and_base_instance_mode = conf.shader_model_max >= SHADER_MODEL_6_8 ?
222             DXIL_SPIRV_SYSVAL_TYPE_NATIVE : DXIL_SPIRV_SYSVAL_TYPE_ZERO;
223          break;
224       case 'x':
225          val_ver = DXIL_VALIDATOR_1_0 + atoi(optarg);
226          break;
227       case 1:
228          if (!compile_shader(optarg, shader_stage, &cur_shader, &conf))
229             return 1;
230          shaders[shader_stage] = cur_shader;
231          any_shaders = true;
232          break;
233       default:
234          fprintf(stderr, "Unrecognized option.\n");
235          return 1;
236       }
237    }
238 
239    if (!any_shaders) {
240       fprintf(stderr, "Specify a shader filename\n");
241       return 1;
242    }
243 
244    for (int32_t cur = MESA_SHADER_FRAGMENT; cur >= MESA_SHADER_VERTEX; --cur) {
245       if (!shaders[cur].nir)
246          continue;
247       for (int32_t prev = cur - 1; prev >= MESA_SHADER_VERTEX; --prev) {
248          if (!shaders[prev].nir)
249             continue;
250          struct dxil_spirv_metadata metadata = { 0 };
251          dxil_spirv_nir_link(shaders[cur].nir, shaders[prev].nir, &conf, &metadata);
252          break;
253       }
254    }
255 
256    struct nir_to_dxil_options opts = {
257       .environment = DXIL_ENVIRONMENT_VULKAN,
258       .shader_model_max = conf.shader_model_max,
259       .validator_version_max = val_ver,
260    };
261 
262    struct dxil_logger logger_inner = {.priv = NULL,
263                                       .log = log_spirv_to_dxil_error};
264 
265    for (uint32_t i = 0; i <= MESA_SHADER_COMPUTE; ++i) {
266       if (!shaders[i].nir)
267          continue;
268       struct blob dxil_blob;
269       bool success = nir_to_dxil(shaders[i].nir, &opts, &logger_inner, &dxil_blob);
270       ralloc_free(shaders[i].nir);
271 
272       if (!success) {
273          fprintf(stderr, "Failed to convert to DXIL\n");
274          if (dxil_blob.allocated)
275             blob_finish(&dxil_blob);
276          return false;
277       }
278 
279       if (validate && !validate_dxil(&dxil_blob)) {
280          fprintf(stderr, "Failed to validate DXIL\n");
281          blob_finish(&dxil_blob);
282          return 1;
283       }
284 
285       if (shaders[i].output_file) {
286          FILE *file = fopen(shaders[i].output_file, "wb");
287          if (!file) {
288             fprintf(stderr, "Failed to open %s, %s\n", shaders[i].output_file,
289                     strerror(errno));
290             blob_finish(&dxil_blob);
291             return 1;
292          }
293 
294          fwrite(dxil_blob.data, sizeof(char), dxil_blob.size, file);
295          fclose(file);
296          blob_finish(&dxil_blob);
297       }
298    }
299 
300    glsl_type_singleton_decref();
301 
302    return 0;
303 }
304