taisei/src/renderer/glcommon/shaders.c
2019-09-16 22:53:43 +03:00

220 lines
6.1 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 "shaders.h"
#include "util.h"
#include "opengl.h"
#include "rwops/rwops_autobuf.h"
static size_t num_langs, num_langs_allocated;
ShaderLangInfo *glcommon_shader_lang_table = NULL;
static ShaderLangInfo* alloc_lang(void) {
if(num_langs == num_langs_allocated) {
num_langs_allocated *= 2;
glcommon_shader_lang_table = realloc(glcommon_shader_lang_table, num_langs_allocated * sizeof(ShaderLangInfo));
}
ShaderLangInfo *lang = glcommon_shader_lang_table + num_langs++;
memset(lang, 0, sizeof(*lang));
return lang;
}
static void add_glsl_version_parsed(GLSLVersion v) {
for(ShaderLangInfo *lang = glcommon_shader_lang_table; lang < glcommon_shader_lang_table + num_langs; ++lang) {
assert(lang->lang == SHLANG_GLSL);
if(!memcmp(&lang->glsl.version, &v, sizeof(v))) {
return;
}
}
ShaderLangInfo *lang = alloc_lang();
lang->lang = SHLANG_GLSL;
lang->glsl.version = v;
if(v.profile == GLSL_PROFILE_NONE && v.version >= 330) {
v.profile = GLSL_PROFILE_CORE;
lang = alloc_lang();
lang->lang = SHLANG_GLSL;
lang->glsl.version = v;
}
}
static void add_glsl_version_nonstandard(const char *vstr) {
// because intel wants to be fucking special again
char a, b, c;
if(sscanf(vstr, "%c.%c%c - ", &a, &b, &c) == 3) {
if(isdigit(a) && isdigit(b) && isdigit(c)) {
GLSLVersion v = { 0 };
v.version = (a - '0') * 100 + (b - '0') * 10 + (c - '0');
v.profile = GLSL_PROFILE_NONE;
add_glsl_version_parsed(v);
v.profile = GLSL_PROFILE_CORE;
add_glsl_version_parsed(v);
v.profile = GLSL_PROFILE_COMPATIBILITY;
add_glsl_version_parsed(v);
}
}
}
static void add_glsl_version(const char *vstr) {
if(!*vstr) {
// Special case: the very first GLSL version doesn't have a version string.
// We'll just ignore it, it's way too old to be useful anyway.
return;
}
GLSLVersion v;
if(glsl_parse_version(vstr, &v) == vstr) {
add_glsl_version_nonstandard(vstr);
return;
}
add_glsl_version_parsed(v);
}
static void glcommon_build_shader_lang_table_fallback(void);
static void glcommon_build_shader_lang_table_finish(void);
void glcommon_build_shader_lang_table(void) {
num_langs_allocated = 8;
glcommon_shader_lang_table = calloc(num_langs_allocated, sizeof(ShaderLangInfo));
// NOTE: The ability to query supported GLSL versions was added in GL 4.3,
// but it's not exposed by any extension. This is pretty silly.
GLint num_versions = 0;
if(!glext.version.is_webgl) {
glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &num_versions);
}
for(int i = 0; i < num_versions; ++i) {
add_glsl_version((char*)glGetStringi(GL_SHADING_LANGUAGE_VERSION, i));
}
if(num_langs < 1) {
glcommon_build_shader_lang_table_fallback();
}
// TODO: Maybe also detect compatibility profile somehow.
glcommon_build_shader_lang_table_finish();
}
static void glcommon_build_shader_lang_table_fallback(void) {
log_warn("Can not reliably determine all supported GLSL versions, resorting to guesswork.");
struct vtable {
uint gl;
GLSLVersion glsl;
};
static struct vtable version_map[] = {
// OpenGL Core
{ 45, { 450, GLSL_PROFILE_NONE } },
{ 46, { 460, GLSL_PROFILE_NONE } },
{ 44, { 440, GLSL_PROFILE_NONE } },
{ 43, { 430, GLSL_PROFILE_NONE } },
{ 42, { 420, GLSL_PROFILE_NONE } },
{ 41, { 410, GLSL_PROFILE_NONE } },
{ 40, { 400, GLSL_PROFILE_NONE } },
{ 33, { 330, GLSL_PROFILE_NONE } },
{ 32, { 150, GLSL_PROFILE_NONE } },
{ 31, { 140, GLSL_PROFILE_NONE } },
{ 30, { 130, GLSL_PROFILE_NONE } },
{ 21, { 120, GLSL_PROFILE_NONE } },
{ 20, { 110, GLSL_PROFILE_NONE } },
// OpenGL ES
{ 32, { 320, GLSL_PROFILE_ES } },
{ 31, { 310, GLSL_PROFILE_ES } },
{ 30, { 300, GLSL_PROFILE_ES } },
// { 20, { 100, GLSL_PROFILE_ES } },
};
const char *glslvstr = (char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
const char *glslvstr_orig = glslvstr;
static const char gles_prefix[] = "OpenGL ES GLSL ES";
GLSLProfile profile = GLSL_PROFILE_NONE;
uint major, minor;
if(glslvstr == NULL) {
log_error("Failed to obtain the GLSL version string");
glcommon_free_shader_lang_table();
return;
}
if(strstartswith(glslvstr, gles_prefix)) {
glslvstr += strlen(gles_prefix);
profile = GLSL_PROFILE_ES;
}
if(sscanf(glslvstr, " %u.%u", &major, &minor) == 2) {
GLSLVersion glsl_version = { major * 100 + minor, profile };
// This one *must* work.
add_glsl_version_parsed(glsl_version);
// Let's be a bit optimistic here and just assume that all the older versions are supported (except those commented out).
// This seems to be the common behavior anyway.
for(struct vtable *v = version_map; v < version_map + sizeof(version_map)/sizeof(*version_map); ++v) {
if(v->glsl.version < glsl_version.version && v->glsl.profile == profile) {
add_glsl_version_parsed(v->glsl);
}
}
} else {
log_error("Failed to parse GLSL version string: %s", glslvstr_orig);
// We could try to infer it from the context version, but whatever.
// If we got here, then we're probably fucked anyway.
glcommon_free_shader_lang_table();
return;
}
}
static void glcommon_build_shader_lang_table_finish(void) {
if(num_langs > 0) {
alloc_lang(); // sentinel for iteration
char *str;
SDL_RWops *abuf = SDL_RWAutoBuffer((void**)&str, 256);
SDL_RWprintf(abuf, "Supported GLSL versions: ");
char vbuf[32];
for(ShaderLangInfo *lang = glcommon_shader_lang_table; lang->lang != SHLANG_INVALID; ++lang) {
assert(lang->lang == SHLANG_GLSL);
glsl_format_version(vbuf, sizeof(vbuf), lang->glsl.version);
if(lang == glcommon_shader_lang_table) {
SDL_RWprintf(abuf, "%s", vbuf);
} else {
SDL_RWprintf(abuf, ", %s", vbuf);
}
}
SDL_WriteU8(abuf, 0);
log_info("%s", str);
SDL_RWclose(abuf);
} else {
log_error("Can not determine supported GLSL versions. Looks like the OpenGL implementation is non-conformant. Expect nothing to work.");
}
}
void glcommon_free_shader_lang_table(void) {
free(glcommon_shader_lang_table);
glcommon_shader_lang_table = NULL;
}