b6978178b1
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.
191 lines
4.4 KiB
C
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,
|
|
},
|
|
};
|