mirror of https://gitlab.freedesktop.org/mesa/mesa
312 lines
9.0 KiB
C
312 lines
9.0 KiB
C
/*
|
|
* Copyright © 2015 Intel Corporation
|
|
* Copyright © Microsoft Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* A simple executable that opens a SPIR-V shader, converts it to DXIL via
|
|
* NIR, and dumps out the result. This should be useful for testing the
|
|
* nir_to_dxil code. Based on spirv2nir.c.
|
|
*/
|
|
|
|
#include "nir_to_dxil.h"
|
|
#include "dxil_validator.h"
|
|
#include "spirv/nir_spirv.h"
|
|
#include "spirv_to_dxil.h"
|
|
#include "dxil_spirv_nir.h"
|
|
|
|
#include "util/os_file.h"
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define WORD_SIZE 4
|
|
|
|
static gl_shader_stage
|
|
stage_to_enum(char *stage)
|
|
{
|
|
if (!strcmp(stage, "vertex"))
|
|
return MESA_SHADER_VERTEX;
|
|
else if (!strcmp(stage, "tess-ctrl"))
|
|
return MESA_SHADER_TESS_CTRL;
|
|
else if (!strcmp(stage, "tess-eval"))
|
|
return MESA_SHADER_TESS_EVAL;
|
|
else if (!strcmp(stage, "geometry"))
|
|
return MESA_SHADER_GEOMETRY;
|
|
else if (!strcmp(stage, "fragment"))
|
|
return MESA_SHADER_FRAGMENT;
|
|
else if (!strcmp(stage, "compute"))
|
|
return MESA_SHADER_COMPUTE;
|
|
else
|
|
return MESA_SHADER_NONE;
|
|
}
|
|
|
|
static void
|
|
log_spirv_to_dxil_error(void *priv, const char *msg)
|
|
{
|
|
fprintf(stderr, "spirv_to_dxil error: %s", msg);
|
|
}
|
|
|
|
struct shader {
|
|
const char *entry_point;
|
|
const char *output_file;
|
|
nir_shader *nir;
|
|
};
|
|
|
|
bool validate = false, debug = false;
|
|
enum dxil_validator_version val_ver = DXIL_VALIDATOR_1_4;
|
|
enum dxil_shader_model shader_model = SHADER_MODEL_6_2;
|
|
|
|
struct nir_shader_compiler_options nir_options;
|
|
|
|
static bool
|
|
compile_shader(const char *filename, gl_shader_stage shader_stage, struct shader *shader,
|
|
struct dxil_spirv_runtime_conf *conf)
|
|
{
|
|
size_t file_size;
|
|
char *file_contents = os_read_file(filename, &file_size);
|
|
if (!file_contents) {
|
|
fprintf(stderr, "Failed to open %s\n", filename);
|
|
return false;
|
|
}
|
|
|
|
if (file_size % WORD_SIZE != 0) {
|
|
fprintf(stderr, "%s size == %zu is not a multiple of %d\n", filename,
|
|
file_size, WORD_SIZE);
|
|
free(file_contents);
|
|
return false;
|
|
}
|
|
|
|
size_t word_count = file_size / WORD_SIZE;
|
|
|
|
struct spirv_to_nir_options spirv_opts = {
|
|
.caps = {
|
|
.draw_parameters = true,
|
|
},
|
|
.ubo_addr_format = nir_address_format_32bit_index_offset,
|
|
.ssbo_addr_format = nir_address_format_32bit_index_offset,
|
|
.shared_addr_format = nir_address_format_32bit_offset_as_64bit,
|
|
|
|
// use_deref_buffer_array_length + nir_lower_explicit_io force
|
|
// get_ssbo_size to take in the return from load_vulkan_descriptor
|
|
// instead of vulkan_resource_index. This makes it much easier to
|
|
// get the DXIL handle for the SSBO.
|
|
.use_deref_buffer_array_length = true
|
|
};
|
|
|
|
shader->nir = spirv_to_nir(
|
|
(const uint32_t *)file_contents, word_count, NULL,
|
|
0, (gl_shader_stage)shader_stage, shader->entry_point,
|
|
&spirv_opts, &nir_options);
|
|
free(file_contents);
|
|
if (!shader->nir) {
|
|
fprintf(stderr, "SPIR-V to NIR failed\n");
|
|
return false;
|
|
}
|
|
|
|
nir_validate_shader(shader->nir,
|
|
"Validate before feeding NIR to the DXIL compiler");
|
|
|
|
dxil_spirv_nir_prep(shader->nir);
|
|
|
|
bool requires_runtime_data;
|
|
dxil_spirv_nir_passes(shader->nir, conf, &requires_runtime_data);
|
|
|
|
if (debug)
|
|
nir_print_shader(shader->nir, stderr);
|
|
|
|
return true;
|
|
}
|
|
|
|
#if DETECT_OS_WINDOWS
|
|
|
|
static bool
|
|
validate_dxil(struct blob *blob)
|
|
{
|
|
struct dxil_validator *val = dxil_create_validator(NULL);
|
|
|
|
char *err;
|
|
bool res = dxil_validate_module(val, blob->data,
|
|
blob->size, &err);
|
|
if (!res && err)
|
|
fprintf(stderr, "DXIL: %s\n\n", err);
|
|
|
|
dxil_destroy_validator(val);
|
|
return res;
|
|
}
|
|
|
|
#else
|
|
|
|
static bool
|
|
validate_dxil(struct blob *blob)
|
|
{
|
|
fprintf(stderr, "DXIL validation only available in Windows.\n");
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
glsl_type_singleton_init_or_ref();
|
|
int ch;
|
|
|
|
static struct option long_options[] = {
|
|
{"stage", required_argument, 0, 's'},
|
|
{"entry", required_argument, 0, 'e'},
|
|
{"output", required_argument, 0, 'o'},
|
|
{"validate", no_argument, 0, 'v'},
|
|
{"debug", no_argument, 0, 'd'},
|
|
{"shadermodel", required_argument, 0, 'm'},
|
|
{"validatorver", required_argument, 0, 'x'},
|
|
{0, 0, 0, 0}};
|
|
|
|
struct shader shaders[MESA_SHADER_COMPUTE + 1];
|
|
memset(shaders, 0, sizeof(shaders));
|
|
struct shader cur_shader = {
|
|
.entry_point = "main",
|
|
.output_file = "",
|
|
};
|
|
gl_shader_stage shader_stage = MESA_SHADER_FRAGMENT;
|
|
|
|
nir_options = *dxil_get_nir_compiler_options();
|
|
// We will manually handle base_vertex when vertex_id and instance_id have
|
|
// have been already converted to zero-base.
|
|
nir_options.lower_base_vertex = false;
|
|
|
|
struct dxil_spirv_runtime_conf conf;
|
|
memset(&conf, 0, sizeof(conf));
|
|
conf.runtime_data_cbv.base_shader_register = 0;
|
|
conf.runtime_data_cbv.register_space = 31;
|
|
conf.zero_based_vertex_instance_id = true;
|
|
|
|
bool any_shaders = false;
|
|
while ((ch = getopt_long(argc, argv, "-s:e:o:m:x:vd", long_options, NULL)) !=
|
|
-1) {
|
|
switch (ch)
|
|
{
|
|
case 's':
|
|
shader_stage = stage_to_enum(optarg);
|
|
if (shader_stage == MESA_SHADER_NONE) {
|
|
fprintf(stderr, "Unknown stage %s\n", optarg);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'e':
|
|
cur_shader.entry_point = optarg;
|
|
break;
|
|
case 'o':
|
|
cur_shader.output_file = optarg;
|
|
break;
|
|
case 'v':
|
|
validate = true;
|
|
break;
|
|
case 'd':
|
|
debug = true;
|
|
break;
|
|
case 'm':
|
|
shader_model = SHADER_MODEL_6_0 + atoi(optarg);
|
|
nir_options.lower_helper_invocation = shader_model < SHADER_MODEL_6_6;
|
|
break;
|
|
case 'x':
|
|
val_ver = DXIL_VALIDATOR_1_0 + atoi(optarg);
|
|
break;
|
|
case 1:
|
|
if (!compile_shader(optarg, shader_stage, &cur_shader, &conf))
|
|
return 1;
|
|
shaders[shader_stage] = cur_shader;
|
|
any_shaders = true;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unrecognized option.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!any_shaders) {
|
|
fprintf(stderr, "Specify a shader filename\n");
|
|
return 1;
|
|
}
|
|
|
|
for (int32_t cur = MESA_SHADER_FRAGMENT; cur >= MESA_SHADER_VERTEX; --cur) {
|
|
if (!shaders[cur].nir)
|
|
continue;
|
|
for (int32_t prev = cur - 1; prev >= MESA_SHADER_VERTEX; --prev) {
|
|
if (!shaders[prev].nir)
|
|
continue;
|
|
bool requires_runtime_data;
|
|
dxil_spirv_nir_link(shaders[cur].nir, shaders[prev].nir, &conf, &requires_runtime_data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct nir_to_dxil_options opts = {
|
|
.environment = DXIL_ENVIRONMENT_VULKAN,
|
|
.shader_model_max = shader_model,
|
|
.validator_version_max = val_ver,
|
|
};
|
|
|
|
struct dxil_logger logger_inner = {.priv = NULL,
|
|
.log = log_spirv_to_dxil_error};
|
|
|
|
for (uint32_t i = 0; i <= MESA_SHADER_COMPUTE; ++i) {
|
|
if (!shaders[i].nir)
|
|
continue;
|
|
struct blob dxil_blob;
|
|
bool success = nir_to_dxil(shaders[i].nir, &opts, &logger_inner, &dxil_blob);
|
|
ralloc_free(shaders[i].nir);
|
|
|
|
if (!success) {
|
|
fprintf(stderr, "Failed to convert to DXIL\n");
|
|
if (dxil_blob.allocated)
|
|
blob_finish(&dxil_blob);
|
|
return false;
|
|
}
|
|
|
|
if (validate && !validate_dxil(&dxil_blob)) {
|
|
fprintf(stderr, "Failed to validate DXIL\n");
|
|
blob_finish(&dxil_blob);
|
|
return 1;
|
|
}
|
|
|
|
if (shaders[i].output_file) {
|
|
FILE *file = fopen(shaders[i].output_file, "wb");
|
|
if (!file) {
|
|
fprintf(stderr, "Failed to open %s, %s\n", shaders[i].output_file,
|
|
strerror(errno));
|
|
blob_finish(&dxil_blob);
|
|
return 1;
|
|
}
|
|
|
|
fwrite(dxil_blob.data, sizeof(char), dxil_blob.size, file);
|
|
fclose(file);
|
|
blob_finish(&dxil_blob);
|
|
}
|
|
}
|
|
|
|
glsl_type_singleton_decref();
|
|
|
|
return 0;
|
|
}
|