taisei/src/resource/shader_object.c
Andrei Alexeyev b6978178b1
memory: use custom memory allocation wrappers
Introduces wrappers around memory allocation functions in `memory.h`
that should be used instead of the standard C ones.

These never return NULL and, with the exception of `mem_realloc()`,
zero-initialize the allocated memory like `calloc()` does.

All allocations made with the memory.h API must be deallocated with
`mem_free()`. Although standard `free()` will work on some platforms,
it's not portable (currently it won't work on Windows). Likewise,
`mem_free()` must not be used to free foreign allocations.

The standard C allocation functions are now diagnosed as deprecated.
They are, however, available with the `libc_` prefix in case interfacing
with foreign APIs is required. So far they are only used to implement
`memory.h`.

Perhaps the most important change is the introduction of the `ALLOC()`,
`ALLOC_ARRAY()`, and `ALLOC_FLEX()` macros. They take a type as a
parameter, and allocate enough memory with the correct alignment for
that type. That includes overaligned types as well. In most
circumstances you should prefer to use these macros. See the `memory.h`
header for some usage examples.
2023-01-18 13:23:22 +01:00

191 lines
4.4 KiB
C

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "util.h"
#include "shader_object.h"
#include "renderer/api.h"
struct shobj_type {
const char *ext;
ShaderLanguage lang;
ShaderStage stage;
};
struct shobj_load_data {
ShaderSource source;
};
static const char *const shobj_exts[] = {
".glsl",
NULL,
};
static struct shobj_type shobj_type_table[] = {
{ ".vert.glsl", SHLANG_GLSL, SHADER_STAGE_VERTEX },
{ ".frag.glsl", SHLANG_GLSL, SHADER_STAGE_FRAGMENT },
{ NULL }
};
static struct shobj_type *get_shobj_type(const char *name) {
for(struct shobj_type *type = shobj_type_table; type->ext; ++type) {
if(strendswith(name, type->ext)) {
return type;
}
}
return NULL;
}
static char *shader_object_path(const char *name) {
char *path = NULL;
for(const char *const *ext = shobj_exts; *ext; ++ext) {
if((path = try_path(SHOBJ_PATH_PREFIX, name, *ext))) {
return path;
}
}
return path;
}
static bool check_shader_object_path(const char *path) {
return strstartswith(path, SHOBJ_PATH_PREFIX) && get_shobj_type(path);
}
static void load_shader_object_stage1(ResourceLoadState *st);
static void load_shader_object_stage2(ResourceLoadState *st);
static SDL_RWops *glsl_open_callback(const char *path, void *userdata) {
ResourceLoadState *st = userdata;
return res_open_file(st, path, VFS_MODE_READ);
}
static void load_shader_object_stage1(ResourceLoadState *st) {
struct shobj_type *type = get_shobj_type(st->path);
if(type == NULL) {
log_error("%s: can not determine shading language and/or shader stage from the filename", st->path);
res_load_failed(st);
return;
}
auto ldata = ALLOC(struct shobj_load_data);
char backend_macro[32] = "BACKEND_";
{
const char *backend_name = r_backend_name();
char *out = backend_macro + sizeof("BACKEND_") - 1;
for(const char *in = backend_name; *in;) {
*out++ = toupper(*in++);
}
*out = 0;
}
ShaderMacro macros[] = {
{ backend_macro, "1" },
{ NULL, },
};
switch(type->lang) {
case SHLANG_GLSL: {
GLSLSourceOptions opts = {
.version = { 330, GLSL_PROFILE_CORE },
.stage = type->stage,
.macros = macros,
.file_open_callback = glsl_open_callback,
.file_open_callback_userdata = st,
};
if(!glsl_load_source(st->path, &ldata->source, &opts)) {
goto fail;
}
break;
}
default: UNREACHABLE;
}
ShaderLangInfo altlang = { SHLANG_INVALID };
if(!r_shader_language_supported(&ldata->source.lang, &altlang)) {
if(altlang.lang == SHLANG_INVALID) {
log_error("%s: shading language not supported by backend", st->path);
goto fail;
}
log_warn("%s: shading language not supported by backend, attempting to translate", st->path);
assert(r_shader_language_supported(&altlang, NULL));
ShaderSource newsrc;
bool result = spirv_transpile(&ldata->source, &newsrc, &(SPIRVTranspileOptions) {
.lang = &altlang,
.optimization_level = SPIRV_OPTIMIZE_PERFORMANCE,
.filename = st->path,
});
if(!result) {
log_error("%s: translation failed", st->path);
goto fail;
}
shader_free_source(&ldata->source);
ldata->source = newsrc;
}
res_load_continue_on_main(st, load_shader_object_stage2, ldata);
return;
fail:
shader_free_source(&ldata->source);
mem_free(ldata);
res_load_failed(st);
}
static void load_shader_object_stage2(ResourceLoadState *st) {
struct shobj_load_data *ldata = NOT_NULL(st->opaque);
ShaderObject *shobj = r_shader_object_compile(&ldata->source);
shader_free_source(&ldata->source);
mem_free(ldata);
if(shobj) {
r_shader_object_set_debug_label(shobj, st->name);
res_load_finished(st, shobj);
} else {
log_error("%s: failed to compile shader object", st->path);
res_load_failed(st);
}
}
static void unload_shader_object(void *vsha) {
r_shader_object_destroy(vsha);
}
static bool transfer_shader_object(void *dst, void *src) {
return r_shader_object_transfer(dst, src);
}
ResourceHandler shader_object_res_handler = {
.type = RES_SHADER_OBJECT,
.typename = "shader object",
.subdir = SHOBJ_PATH_PREFIX,
.procs = {
.init = spirv_init_compiler,
.shutdown = spirv_shutdown_compiler,
.find = shader_object_path,
.check = check_shader_object_path,
.load = load_shader_object_stage1,
.unload = unload_shader_object,
.transfer = transfer_shader_object,
},
};