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