mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-04-30 21:21:39 +02:00
spirv2dxil: Support linking multiple shaders
This probably could/should be split up into multiple commits, but it's simpler to make this a monolithic change. This change inlines a bunch of logic from spirv_to_dxil into the spirv2dxil tool so that linking can be done on the nir shaders. Probably the linking functionality should be exposed in the lib/dll form too, which means that a helper for freeing intermediate nir would be needed too. That's TODO for now. The tool now requires arguments to be in-order, and once a filename is encountered, will use the previous arguments to compile the shader. If multiple graphics shaders are passed, they're linked as if they were forming a pipeline together. Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/20440>
This commit is contained in:
parent
ac7abf0483
commit
531d17c334
4 changed files with 210 additions and 190 deletions
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2021 Intel 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#include "dxil_validation.h"
|
|
||||||
|
|
||||||
#if DETECT_OS_WINDOWS
|
|
||||||
|
|
||||||
#include "dxil_validator.h"
|
|
||||||
|
|
||||||
bool
|
|
||||||
validate_dxil(dxil_spirv_object *dxil_obj)
|
|
||||||
{
|
|
||||||
struct dxil_validator *val = dxil_create_validator(NULL);
|
|
||||||
|
|
||||||
char *err;
|
|
||||||
bool res = dxil_validate_module(val, dxil_obj->binary.buffer,
|
|
||||||
dxil_obj->binary.size, &err);
|
|
||||||
if (!res && err)
|
|
||||||
fprintf(stderr, "DXIL: %s\n\n", err);
|
|
||||||
|
|
||||||
dxil_destroy_validator(val);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
bool
|
|
||||||
validate_dxil(dxil_spirv_object *dxil_obj)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "DXIL validation only available in Windows.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2021 Intel 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DXIL_VALIDATION_H
|
|
||||||
#define DXIL_VALIDATION_H
|
|
||||||
|
|
||||||
#include "spirv_to_dxil.h"
|
|
||||||
#include "util/detect_os.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool
|
|
||||||
validate_dxil(struct dxil_spirv_object *dxil_obj);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* DXIL_VALIDATION_H */
|
|
||||||
|
|
@ -24,6 +24,7 @@ libspirv_to_dxil = both_libraries(
|
||||||
[
|
[
|
||||||
files(
|
files(
|
||||||
'dxil_spirv_nir.c',
|
'dxil_spirv_nir.c',
|
||||||
|
'dxil_spirv_nir.h',
|
||||||
'spirv_to_dxil.c',
|
'spirv_to_dxil.c',
|
||||||
'spirv_to_dxil.h',
|
'spirv_to_dxil.h',
|
||||||
),
|
),
|
||||||
|
|
@ -44,8 +45,6 @@ spirv_to_dxil = executable(
|
||||||
'spirv2dxil',
|
'spirv2dxil',
|
||||||
files(
|
files(
|
||||||
'spirv2dxil.c',
|
'spirv2dxil.c',
|
||||||
'dxil_validation.h',
|
|
||||||
'dxil_validation.cpp',
|
|
||||||
),
|
),
|
||||||
dependencies : [idep_nir, idep_libdxil_compiler, idep_getopt, idep_libspirv_to_dxil],
|
dependencies : [idep_nir, idep_libdxil_compiler, idep_getopt, idep_libspirv_to_dxil],
|
||||||
include_directories : [inc_include, inc_src, inc_compiler, inc_gallium],
|
include_directories : [inc_include, inc_src, inc_compiler, inc_gallium],
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nir_to_dxil.h"
|
#include "nir_to_dxil.h"
|
||||||
#include "dxil_validation.h"
|
#include "dxil_validator.h"
|
||||||
#include "spirv/nir_spirv.h"
|
#include "spirv/nir_spirv.h"
|
||||||
#include "spirv_to_dxil.h"
|
#include "spirv_to_dxil.h"
|
||||||
|
#include "dxil_spirv_nir.h"
|
||||||
|
|
||||||
#include "util/os_file.h"
|
#include "util/os_file.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
@ -56,8 +57,6 @@ stage_to_enum(char *stage)
|
||||||
return MESA_SHADER_FRAGMENT;
|
return MESA_SHADER_FRAGMENT;
|
||||||
else if (!strcmp(stage, "compute"))
|
else if (!strcmp(stage, "compute"))
|
||||||
return MESA_SHADER_COMPUTE;
|
return MESA_SHADER_COMPUTE;
|
||||||
else if (!strcmp(stage, "kernel"))
|
|
||||||
return MESA_SHADER_KERNEL;
|
|
||||||
else
|
else
|
||||||
return MESA_SHADER_NONE;
|
return MESA_SHADER_NONE;
|
||||||
}
|
}
|
||||||
|
|
@ -68,73 +67,32 @@ log_spirv_to_dxil_error(void *priv, const char *msg)
|
||||||
fprintf(stderr, "spirv_to_dxil error: %s", msg);
|
fprintf(stderr, "spirv_to_dxil error: %s", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
struct shader {
|
||||||
main(int argc, char **argv)
|
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)
|
||||||
{
|
{
|
||||||
gl_shader_stage shader_stage = MESA_SHADER_FRAGMENT;
|
|
||||||
char *entry_point = "main";
|
|
||||||
char *output_file = "";
|
|
||||||
int ch;
|
|
||||||
bool validate = false, debug = false;
|
|
||||||
|
|
||||||
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'},
|
|
||||||
{0, 0, 0, 0}};
|
|
||||||
|
|
||||||
|
|
||||||
while ((ch = getopt_long(argc, argv, "s:e:o: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':
|
|
||||||
entry_point = optarg;
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
output_file = optarg;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
validate = true;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
debug = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Unrecognized option.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind != argc - 1) {
|
|
||||||
if (optind < argc)
|
|
||||||
fprintf(stderr, "Please specify only one input file.");
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Please specify an input file.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *filename = argv[optind];
|
|
||||||
size_t file_size;
|
size_t file_size;
|
||||||
char *file_contents = os_read_file(filename, &file_size);
|
char *file_contents = os_read_file(filename, &file_size);
|
||||||
if (!file_contents) {
|
if (!file_contents) {
|
||||||
fprintf(stderr, "Failed to open %s\n", filename);
|
fprintf(stderr, "Failed to open %s\n", filename);
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_size % WORD_SIZE != 0) {
|
if (file_size % WORD_SIZE != 0) {
|
||||||
fprintf(stderr, "%s size == %zu is not a multiple of %d\n", filename,
|
fprintf(stderr, "%s size == %zu is not a multiple of %d\n", filename,
|
||||||
file_size, WORD_SIZE);
|
file_size, WORD_SIZE);
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t word_count = file_size / WORD_SIZE;
|
size_t word_count = file_size / WORD_SIZE;
|
||||||
|
|
@ -145,46 +103,206 @@ main(int argc, char **argv)
|
||||||
conf.runtime_data_cbv.register_space = 31;
|
conf.runtime_data_cbv.register_space = 31;
|
||||||
conf.zero_based_vertex_instance_id = true;
|
conf.zero_based_vertex_instance_id = true;
|
||||||
|
|
||||||
struct dxil_spirv_debug_options dbg_opts = {
|
struct spirv_to_nir_options spirv_opts = {
|
||||||
.dump_nir = debug,
|
.caps = {
|
||||||
};
|
.draw_parameters = true,
|
||||||
const struct dxil_spirv_logger logger = {
|
},
|
||||||
.priv = NULL,
|
.ubo_addr_format = nir_address_format_32bit_index_offset,
|
||||||
.log = log_spirv_to_dxil_error
|
.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
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dxil_spirv_object obj;
|
shader->nir = spirv_to_nir(
|
||||||
memset(&obj, 0, sizeof(obj));
|
(const uint32_t *)file_contents, word_count, NULL,
|
||||||
if (spirv_to_dxil((uint32_t *)file_contents, word_count, NULL, 0,
|
0, (gl_shader_stage)shader_stage, shader->entry_point,
|
||||||
(dxil_spirv_shader_stage)shader_stage, entry_point,
|
&spirv_opts, &nir_options);
|
||||||
SHADER_MODEL_6_2, DXIL_VALIDATOR_1_4,
|
free(file_contents);
|
||||||
&dbg_opts, &conf, &logger, &obj)) {
|
if (!shader->nir) {
|
||||||
|
fprintf(stderr, "SPIR-V to NIR failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (validate && !validate_dxil(&obj)) {
|
nir_validate_shader(shader->nir,
|
||||||
fprintf(stderr, "Failed to validate DXIL\n");
|
"Validate before feeding NIR to the DXIL compiler");
|
||||||
spirv_to_dxil_free(&obj);
|
|
||||||
free(file_contents);
|
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;
|
||||||
|
|
||||||
|
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))
|
||||||
|
return 1;
|
||||||
|
shaders[shader_stage] = cur_shader;
|
||||||
|
any_shaders = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unrecognized option.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FILE *file = fopen(output_file, "wb");
|
if (!any_shaders) {
|
||||||
if (!file) {
|
fprintf(stderr, "Specify a shader filename\n");
|
||||||
fprintf(stderr, "Failed to open %s, %s\n", output_file,
|
|
||||||
strerror(errno));
|
|
||||||
spirv_to_dxil_free(&obj);
|
|
||||||
free(file_contents);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fwrite(obj.binary.buffer, sizeof(char), obj.binary.size, file);
|
|
||||||
fclose(file);
|
|
||||||
spirv_to_dxil_free(&obj);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Compilation failed\n");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(file_contents);
|
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;
|
||||||
|
dxil_spirv_nir_link(shaders[cur].nir, shaders[prev].nir);
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue