• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    bool requires_runtime_data;
119    dxil_spirv_nir_passes(shader->nir, conf, &requires_runtime_data);
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.zero_based_vertex_instance_id = true;
184    conf.declared_read_only_images_as_srvs = true;
185    conf.shader_model_max = SHADER_MODEL_6_2;
186 
187    const unsigned supported_bit_sizes = 16 | 32 | 64;
188    dxil_get_nir_compiler_options(&nir_options, conf.shader_model_max, supported_bit_sizes, supported_bit_sizes);
189    // We will manually handle base_vertex when vertex_id and instance_id have
190    // have been already converted to zero-base.
191    nir_options.lower_base_vertex = false;
192 
193    bool any_shaders = false;
194    while ((ch = getopt_long(argc, argv, "-s:e:o:m:x:vd", long_options, NULL)) !=
195             -1) {
196       switch (ch)
197       {
198       case 's':
199          shader_stage = stage_to_enum(optarg);
200          if (shader_stage == MESA_SHADER_NONE) {
201             fprintf(stderr, "Unknown stage %s\n", optarg);
202             return 1;
203          }
204          break;
205       case 'e':
206          cur_shader.entry_point = optarg;
207          break;
208       case 'o':
209          cur_shader.output_file = optarg;
210          break;
211       case 'v':
212          validate = true;
213          break;
214       case 'd':
215          debug = true;
216          break;
217       case 'm':
218          conf.shader_model_max = SHADER_MODEL_6_0 + atoi(optarg);
219          break;
220       case 'x':
221          val_ver = DXIL_VALIDATOR_1_0 + atoi(optarg);
222          break;
223       case 1:
224          if (!compile_shader(optarg, shader_stage, &cur_shader, &conf))
225             return 1;
226          shaders[shader_stage] = cur_shader;
227          any_shaders = true;
228          break;
229       default:
230          fprintf(stderr, "Unrecognized option.\n");
231          return 1;
232       }
233    }
234 
235    if (!any_shaders) {
236       fprintf(stderr, "Specify a shader filename\n");
237       return 1;
238    }
239 
240    for (int32_t cur = MESA_SHADER_FRAGMENT; cur >= MESA_SHADER_VERTEX; --cur) {
241       if (!shaders[cur].nir)
242          continue;
243       for (int32_t prev = cur - 1; prev >= MESA_SHADER_VERTEX; --prev) {
244          if (!shaders[prev].nir)
245             continue;
246          bool requires_runtime_data;
247          dxil_spirv_nir_link(shaders[cur].nir, shaders[prev].nir, &conf, &requires_runtime_data);
248          break;
249       }
250    }
251 
252    struct nir_to_dxil_options opts = {
253       .environment = DXIL_ENVIRONMENT_VULKAN,
254       .shader_model_max = conf.shader_model_max,
255       .validator_version_max = val_ver,
256    };
257 
258    struct dxil_logger logger_inner = {.priv = NULL,
259                                       .log = log_spirv_to_dxil_error};
260 
261    for (uint32_t i = 0; i <= MESA_SHADER_COMPUTE; ++i) {
262       if (!shaders[i].nir)
263          continue;
264       struct blob dxil_blob;
265       bool success = nir_to_dxil(shaders[i].nir, &opts, &logger_inner, &dxil_blob);
266       ralloc_free(shaders[i].nir);
267 
268       if (!success) {
269          fprintf(stderr, "Failed to convert to DXIL\n");
270          if (dxil_blob.allocated)
271             blob_finish(&dxil_blob);
272          return false;
273       }
274 
275       if (validate && !validate_dxil(&dxil_blob)) {
276          fprintf(stderr, "Failed to validate DXIL\n");
277          blob_finish(&dxil_blob);
278          return 1;
279       }
280 
281       if (shaders[i].output_file) {
282          FILE *file = fopen(shaders[i].output_file, "wb");
283          if (!file) {
284             fprintf(stderr, "Failed to open %s, %s\n", shaders[i].output_file,
285                     strerror(errno));
286             blob_finish(&dxil_blob);
287             return 1;
288          }
289 
290          fwrite(dxil_blob.data, sizeof(char), dxil_blob.size, file);
291          fclose(file);
292          blob_finish(&dxil_blob);
293       }
294    }
295 
296    glsl_type_singleton_decref();
297 
298    return 0;
299 }
300