taisei/src/renderer/glcommon/opengl.c
2021-04-30 03:34:36 +03:00

1010 lines
24 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 "rwops/rwops_autobuf.h"
#include "opengl.h"
#include "debug.h"
#include "shaders.h"
#include "texture.h"
struct glext_s glext = { 0 };
typedef void (*glad_glproc_ptr)(void);
#ifndef STATIC_GLES3
//
// shims
//
APIENTRY
static void shim_glClearDepth(GLdouble depthval) {
glClearDepthf(depthval);
}
APIENTRY
static void shim_glClearDepthf(GLfloat depthval) {
glClearDepth(depthval);
}
#endif
// WEBGL_debug_renderer_info
const GLenum GL_UNMASKED_VENDOR_WEBGL = 0x9245;
const GLenum GL_UNMASKED_RENDERER_WEBGL = 0x9246;
static const char *const ext_vendor_table[] = {
#define TSGL_EXT_VENDOR(v) [_TSGL_EXTVNUM_##v] = #v,
TSGL_EXT_VENDORS
#undef TSGL_EXT_VENDOR
NULL,
};
attr_nonnull_all
static ext_flag_t glcommon_ext_flag(const char *ext) {
const char *ext_orig = ext;
ext = strchr(ext, '_');
if(ext == NULL) {
log_fatal("Bad extension string: %s", ext_orig);
}
const char *sep = strchr(++ext, '_');
if(sep == NULL) {
log_fatal("Bad extension string: %s", ext_orig);
}
char vendor[sep - ext + 1];
strlcpy(vendor, ext, sizeof(vendor));
for(const char *const *p = ext_vendor_table; *p; ++p) {
if(!strcmp(*p, vendor)) {
return 1 << (p - ext_vendor_table);
}
}
log_fatal("Unknown vendor '%s' in extension string %s", vendor, ext_orig);
}
ext_flag_t glcommon_check_extension(const char *ext) {
assert(*ext != 0);
assert(strchr(ext, ' ') == NULL);
const char *overrides = env_get("TAISEI_GL_EXT_OVERRIDES", "");
ext_flag_t flag = glcommon_ext_flag(ext);
if(*overrides) {
char buf[strlen(overrides)+1], *save, *arg, *e;
strcpy(buf, overrides);
arg = buf;
while((e = strtok_r(arg, " ", &save))) {
bool r = true;
if(*e == '-') {
++e;
r = false;
}
if(!strcmp(e, ext)) {
return r ? flag : 0;
}
arg = NULL;
}
}
// SDL_GL_ExtensionSupported is stupid and requires dlopen() with emscripten.
// Let's reinvent it!
// SDL does this
if(env_get_int(ext, 1) == 0) {
return 0;
}
#ifndef STATIC_GLES3
if(GL_ATLEAST(3, 0) || GLES_ATLEAST(3, 0))
#endif
{
GLint num_exts = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts);
for(GLint i = 0; i < num_exts; ++i) {
const char *e = (const char*)glGetStringi(GL_EXTENSIONS, i);
if(!strcmp(ext, e)) {
return flag;
}
}
return 0;
}
#ifndef STATIC_GLES3
// The legacy way
const char *extensions = (const char*)glGetString(GL_EXTENSIONS);
if(!extensions) {
return 0;
}
const char *start = extensions;
size_t ext_len = strlen(ext);
for(;;) {
const char *where = strstr(start, ext);
if(!where) {
return 0;
}
const char *term = where + ext_len;
if(
(where == extensions || where[-1] == ' ') &&
(*term == ' ' || *term == '\0')
) {
return flag;
}
start = term;
}
#endif
}
#define EXT_FLAG(flagname) \
ext_flag_t *pExtField = &glext.flagname
#define CHECK_CORE(cond) do { \
if((cond)) { \
*pExtField = TSGL_EXTFLAG_NATIVE; \
log_info("Using core functionality"); \
return; \
} \
} while(0)
#define CHECK_EXT(extname) do { \
if((*pExtField = glcommon_check_extension(#extname))) { \
log_info("Using " #extname); \
return; \
} \
} while(0)
#define EXT_MISSING() do { \
*pExtField = 0; \
log_warn("Extension not supported"); \
} while(0)
ext_flag_t glcommon_require_extension(const char *ext) {
ext_flag_t val = glcommon_check_extension(ext);
if(!val) {
if(env_get("TAISEI_GL_REQUIRE_EXTENSION_FATAL", 0)) {
log_fatal("Required extension %s is not available", ext);
}
log_error("Required extension %s is not available, expect crashes or rendering errors", ext);
}
return val;
}
static void glcommon_ext_debug_output(void) {
EXT_FLAG(debug_output);
#ifndef STATIC_GLES3
if(HAVE_GL_FUNC(glDebugMessageCallback) && HAVE_GL_FUNC(glDebugMessageControl)) {
if(HAVE_GL_FUNC(glObjectLabel)) {
CHECK_CORE(GL_ATLEAST(4, 3));
CHECK_EXT(GL_KHR_debug);
}
CHECK_EXT(GL_ARB_debug_output);
}
#endif
EXT_MISSING();
}
static void glcommon_ext_pixel_buffer_object(void) {
EXT_FLAG(pixel_buffer_object);
#ifdef STATIC_GLES3
CHECK_CORE(1);
#else
// TODO: verify that these requirements are correct
CHECK_CORE(GL_ATLEAST(2, 0) || GLES_ATLEAST(3, 0));
CHECK_EXT(GL_ARB_pixel_buffer_object);
CHECK_EXT(GL_EXT_pixel_buffer_object);
CHECK_EXT(GL_NV_pixel_buffer_object);
EXT_MISSING();
#endif
}
static void glcommon_ext_depth_texture(void) {
EXT_FLAG(depth_texture);
#ifdef STATIC_GLES3
CHECK_CORE(1);
#else
// TODO: detect this for core OpenGL properly
CHECK_CORE(!glext.version.is_es || GLES_ATLEAST(3, 0));
CHECK_EXT(GL_OES_depth_texture);
CHECK_EXT(GL_ANGLE_depth_texture);
CHECK_EXT(GL_SGIX_depth_texture);
EXT_MISSING();
#endif
}
static void glcommon_ext_instanced_arrays(void) {
EXT_FLAG(instanced_arrays);
#ifdef STATIC_GLES3
CHECK_CORE(1);
#else
if(
HAVE_GL_FUNC(glDrawArraysInstanced) &&
HAVE_GL_FUNC(glDrawElementsInstanced) &&
HAVE_GL_FUNC(glVertexAttribDivisor)
) {
CHECK_CORE(GL_ATLEAST(3, 3) || GLES_ATLEAST(3, 0));
CHECK_EXT(GL_ANGLE_instanced_arrays);
CHECK_EXT(GL_ARB_instanced_arrays);
CHECK_EXT(GL_EXT_instanced_arrays);
CHECK_EXT(GL_NV_instanced_arrays);
}
EXT_MISSING();
#endif
}
static void glcommon_ext_internalformat_query2(void) {
EXT_FLAG(internalformat_query2);
#ifndef STATIC_GLES3
CHECK_CORE(GL_ATLEAST(4, 3));
CHECK_EXT(GL_ARB_internalformat_query2);
#endif
EXT_MISSING();
}
static void glcommon_ext_draw_buffers(void) {
EXT_FLAG(draw_buffers);
#ifdef STATIC_GLES3
CHECK_CORE(1);
#else
if(HAVE_GL_FUNC(glDrawBuffers)) {
CHECK_CORE(GL_ATLEAST(2, 0) || GLES_ATLEAST(3, 0));
CHECK_EXT(GL_ARB_draw_buffers);
CHECK_EXT(GL_EXT_draw_buffers);
}
EXT_MISSING();
#endif
}
static void glcommon_ext_texture_filter_anisotropic(void) {
EXT_FLAG(texture_filter_anisotropic);
CHECK_CORE(GL_ATLEAST(4, 6));
CHECK_EXT(GL_ARB_texture_filter_anisotropic);
CHECK_EXT(GL_EXT_texture_filter_anisotropic);
EXT_MISSING();
}
static void glcommon_ext_clear_texture(void) {
EXT_FLAG(clear_texture);
if(HAVE_GL_FUNC(glClearTexImage)) {
CHECK_CORE(GL_ATLEAST(4, 4));
CHECK_EXT(GL_ARB_clear_texture);
CHECK_EXT(GL_EXT_clear_texture);
}
EXT_MISSING();
}
static void glcommon_ext_texture_norm16(void) {
EXT_FLAG(texture_norm16);
CHECK_CORE(!glext.version.is_es);
CHECK_EXT(GL_EXT_texture_norm16);
EXT_MISSING();
}
static void glcommon_ext_texture_rg(void) {
EXT_FLAG(texture_rg);
CHECK_CORE(!glext.version.is_es);
CHECK_EXT(GL_EXT_texture_rg);
EXT_MISSING();
}
static void glcommon_ext_texture_swizzle(void) {
EXT_FLAG(texture_swizzle);
CHECK_CORE(GL_ATLEAST(3, 3) || (GLES_ATLEAST(3, 0) && !glext.version.is_webgl));
CHECK_EXT(GL_ARB_texture_swizzle);
CHECK_EXT(GL_EXT_texture_swizzle);
EXT_MISSING();
}
static void glcommon_ext_texture_float(void) {
EXT_FLAG(texture_float);
CHECK_CORE(!glext.version.is_es || GLES_ATLEAST(3, 0));
CHECK_EXT(GL_OES_texture_float);
EXT_MISSING();
}
static void glcommon_ext_texture_half_float(void) {
EXT_FLAG(texture_half_float);
CHECK_CORE(!glext.version.is_es || GLES_ATLEAST(3, 0));
CHECK_EXT(GL_OES_texture_half_float);
EXT_MISSING();
}
static void glcommon_ext_texture_float_linear(void) {
EXT_FLAG(texture_float_linear);
CHECK_CORE(!glext.version.is_es);
CHECK_EXT(GL_OES_texture_float_linear);
EXT_MISSING();
}
static void glcommon_ext_texture_half_float_linear(void) {
EXT_FLAG(texture_half_float_linear);
CHECK_CORE(!glext.version.is_es);
CHECK_EXT(GL_OES_texture_half_float_linear);
EXT_MISSING();
}
static void glcommon_ext_color_buffer_float(void) {
EXT_FLAG(color_buffer_float);
CHECK_CORE(!glext.version.is_es);
CHECK_EXT(GL_EXT_color_buffer_float);
EXT_MISSING();
}
static void glcommon_ext_float_blend(void) {
EXT_FLAG(float_blend);
CHECK_CORE(!glext.version.is_es);
CHECK_EXT(GL_EXT_float_blend);
EXT_MISSING();
}
static void glcommon_ext_vertex_array_object(void) {
EXT_FLAG(vertex_array_object);
#ifdef STATIC_GLES3
CHECK_CORE(1);
#else
if(
HAVE_GL_FUNC(glBindVertexArray) &&
HAVE_GL_FUNC(glDeleteVertexArrays) &&
HAVE_GL_FUNC(glGenVertexArrays) &&
HAVE_GL_FUNC(glIsVertexArray)
) {
CHECK_CORE(GL_ATLEAST(3, 0) || GLES_ATLEAST(3, 0));
CHECK_EXT(GL_ARB_vertex_array_object);
CHECK_EXT(GL_OES_vertex_array_object);
}
EXT_MISSING();
#endif
}
static void glcommon_ext_viewport_array(void) {
EXT_FLAG(viewport_array);
#ifndef STATIC_GLES3
if(
HAVE_GL_FUNC(glGetFloati_v) &&
HAVE_GL_FUNC(glViewportIndexedfv)
) {
CHECK_CORE(GL_ATLEAST(4, 1));
CHECK_EXT(GL_ARB_viewport_array);
CHECK_EXT(GL_OES_viewport_array);
CHECK_EXT(GL_NV_viewport_array2);
}
#endif
EXT_MISSING();
}
static void glcommon_ext_texture_format_r8_srgb(void) {
EXT_FLAG(tex_format.r8_srgb);
CHECK_EXT(GL_EXT_texture_sRGB_R8);
EXT_MISSING();
}
static void glcommon_ext_texture_format_rg8_srgb(void) {
EXT_FLAG(tex_format.rg8_srgb);
CHECK_EXT(GL_EXT_texture_sRGB_RG8);
EXT_MISSING();
}
static void glcommon_ext_texture_format_rgb8_rgba8_srgb(void) {
EXT_FLAG(tex_format.rgb8_rgba8_srgb);
CHECK_CORE(GL_ATLEAST(3, 0) || GLES_ATLEAST(3, 0));
CHECK_EXT(GL_EXT_texture_sRGB);
CHECK_EXT(GL_EXT_sRGB);
EXT_MISSING();
}
static void glcommon_ext_texture_format_s3tc_dx1(void) {
EXT_FLAG(tex_format.s3tc_dx1);
CHECK_EXT(GL_EXT_texture_compression_s3tc);
CHECK_EXT(GL_NV_texture_compression_s3tc);
CHECK_EXT(GL_EXT_texture_compression_dxt1);
CHECK_EXT(GL_ANGLE_texture_compression_dxt1);
CHECK_EXT(GL_WEBGL_compressed_texture_s3tc);
EXT_MISSING();
}
static void glcommon_ext_texture_format_s3tc_dx5(void) {
EXT_FLAG(tex_format.s3tc_dx5);
CHECK_EXT(GL_EXT_texture_compression_s3tc);
CHECK_EXT(GL_NV_texture_compression_s3tc);
CHECK_EXT(GL_ANGLE_texture_compression_dxt5);
CHECK_EXT(GL_WEBGL_compressed_texture_s3tc);
EXT_MISSING();
}
static void glcommon_ext_texture_format_s3tc_srgb(void) {
EXT_FLAG(tex_format.s3tc_srgb);
if(glext.tex_format.s3tc_dx1 || glext.tex_format.s3tc_dx5) {
CHECK_EXT(GL_EXT_texture_sRGB);
CHECK_EXT(GL_EXT_texture_compression_s3tc_srgb);
CHECK_EXT(GL_NV_sRGB_formats);
CHECK_EXT(GL_WEBGL_compressed_texture_s3tc_srgb);
}
EXT_MISSING();
}
static void glcommon_ext_texture_format_rgtc(void) {
EXT_FLAG(tex_format.rgtc);
CHECK_CORE(GL_ATLEAST(3, 0));
CHECK_EXT(GL_EXT_texture_compression_rgtc);
EXT_MISSING();
}
static void glcommon_ext_texture_format_etc1(void) {
EXT_FLAG(tex_format.etc1);
CHECK_EXT(GL_OES_compressed_ETC1_RGB8_texture);
CHECK_EXT(GL_WEBGL_compressed_texture_etc1);
EXT_MISSING();
}
static void glcommon_ext_texture_format_etc1_srgb(void) {
EXT_FLAG(tex_format.etc1_srgb);
if(glext.tex_format.etc1) {
CHECK_EXT(GL_NV_sRGB_formats);
}
EXT_MISSING();
}
static void glcommon_ext_texture_format_etc2_eac(void) {
EXT_FLAG(tex_format.etc2_eac);
CHECK_CORE(GL_ATLEAST(4, 3) || (GLES_ATLEAST(3, 0) && !glext.version.is_webgl));
CHECK_EXT(GL_OES_compressed_ETC2_RGBA8_texture);
CHECK_EXT(GL_ARB_ES3_compatibility);
CHECK_EXT(GL_ANGLE_compressed_texture_etc);
CHECK_EXT(GL_WEBGL_compressed_texture_etc);
// FIXME: maybe don't just assume R11/RG11 are supported as well?
EXT_MISSING();
}
static void glcommon_ext_texture_format_etc2_eac_srgb(void) {
EXT_FLAG(tex_format.etc2_eac_srgb);
CHECK_CORE(GL_ATLEAST(4, 3) || (GLES_ATLEAST(3, 0) && !glext.version.is_webgl));
CHECK_EXT(GL_OES_compressed_ETC2_sRGB8_alpha8_texture);
CHECK_EXT(GL_ARB_ES3_compatibility);
CHECK_EXT(GL_WEBGL_compressed_texture_etc);
// FIXME: maybe don't just assume R11/RG11 are supported as well?
EXT_MISSING();
}
static void glcommon_ext_texture_format_bptc(void) {
EXT_FLAG(tex_format.bptc);
CHECK_CORE(GL_ATLEAST(4, 2));
CHECK_EXT(GL_ARB_texture_compression_bptc);
CHECK_EXT(GL_EXT_texture_compression_bptc);
EXT_MISSING();
}
static void glcommon_ext_texture_format_pvrtc(void) {
EXT_FLAG(tex_format.pvrtc);
CHECK_EXT(GL_IMG_texture_compression_pvrtc);
CHECK_EXT(GL_WEBGL_compressed_texture_pvrtc);
EXT_MISSING();
}
static void glcommon_ext_texture_format_pvrtc2(void) {
EXT_FLAG(tex_format.pvrtc2);
CHECK_EXT(GL_IMG_texture_compression_pvrtc2);
EXT_MISSING();
}
static void glcommon_ext_texture_format_pvrtc_srgb(void) {
EXT_FLAG(tex_format.pvrtc_srgb);
CHECK_EXT(GL_EXT_pvrtc_sRGB);
EXT_MISSING();
}
static void glcommon_ext_texture_format_astc(void) {
EXT_FLAG(tex_format.astc);
CHECK_CORE(GLES_ATLEAST(3, 2));
CHECK_EXT(GL_KHR_texture_compression_astc_ldr);
CHECK_EXT(GL_OES_texture_compression_astc);
CHECK_EXT(GL_WEBGL_compressed_texture_astc);
EXT_MISSING();
}
static void glcommon_ext_texture_format_atc(void) {
EXT_FLAG(tex_format.atc);
CHECK_EXT(GL_AMD_compressed_ATC_texture);
EXT_MISSING();
}
static void glcommon_ext_texture_format_fxt1(void) {
EXT_FLAG(tex_format.fxt1);
CHECK_EXT(GL_3DFX_texture_compression_FXT1);
EXT_MISSING();
}
static const char *get_unmasked_property(GLenum prop, bool fallback) {
const char *val = NULL;
if(glext.version.is_webgl) {
if(glcommon_check_extension("GL_WEBGL_debug_renderer_info")) {
GLenum prop_unmasked;
switch(prop) {
case GL_VENDOR: prop_unmasked = GL_UNMASKED_VENDOR_WEBGL; break;
case GL_RENDERER: prop_unmasked = GL_UNMASKED_RENDERER_WEBGL; break;
default: UNREACHABLE;
}
val = (const char*)glGetString(prop_unmasked);
if(!*val) {
val = NULL;
}
}
}
if(val == NULL && fallback) {
val = (const char*)glGetString(prop);
}
return val;
}
static void detect_broken_intel_driver(void) {
#ifdef TAISEI_BUILDCONF_WINDOWS_ANGLE_INTEL
extern DECLSPEC int SDLCALL SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid);
bool is_broken_intel_driver = (
!glext.version.is_es &&
strstartswith((const char*)glGetString(GL_VENDOR), "Intel") &&
strstr((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION), " - Build ")
);
if(!is_broken_intel_driver) {
return;
}
int button;
SDL_MessageBoxData mbdata = { 0 };
mbdata.flags = SDL_MESSAGEBOX_WARNING;
mbdata.title = "Taisei Project";
char *msg = strfmt(
"Looks like you have a broken OpenGL driver.\n"
"Taisei will probably not work correctly, if at all.\n\n"
"Starting the game in ANGLE mode should fix the problem, but may introduce slowdown.\n\n"
"Restart in ANGLE mode now? (If unsure, press YES)"
);
mbdata.message = msg;
mbdata.numbuttons = 3;
mbdata.buttons = (SDL_MessageBoxButtonData[]) {
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 2, "Abort" },
{ 0, 1, "No" },
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Yes (safe)" },
};
int mbresult = SDL_ShowMessageBox(&mbdata, &button);
free(msg);
if(mbresult < 0) {
log_sdl_error(LOG_ERROR, "SDL_ShowMessageBox");
} else if(button == 1) {
return;
} else if(button == 2) {
exit(1);
}
const WCHAR *cmdline = GetCommandLine();
const WCHAR renderer_args[] = L" --renderer gles30";
WCHAR new_cmdline[wcslen(cmdline) + wcslen(renderer_args) + 1];
memcpy(new_cmdline, cmdline, sizeof(WCHAR) * wcslen(cmdline));
memcpy(new_cmdline + wcslen(cmdline), renderer_args, sizeof(renderer_args));
PROCESS_INFORMATION pi;
STARTUPINFO si = { sizeof(si) };
CreateProcessW(
NULL,
new_cmdline,
NULL,
NULL,
false,
0,
NULL,
NULL,
&si,
&pi
);
exit(0);
#endif
}
static bool glcommon_check_workaround(const char *name, const char *envvar, const char *(*detect)(void)) {
int env_setting = env_get_int(envvar, -1);
glext.issues.avoid_sampler_uniform_updates = false;
if(env_setting > 0) {
log_warn("Enabled workaround `%s` (forced by environment)", name);
return true;
}
if(env_setting == 0) {
log_warn("Disabled workaround `%s` (forced by environment)", name);
return false;
}
const char *reason = detect();
if(reason != NULL) {
log_warn("Enabled workaround `%s` (%s)", name, reason);
return true;
}
log_info("Workaround `%s` not needed", name);
return false;
}
#if defined(__EMSCRIPTEN__)
#include <emscripten.h>
EM_JS(bool, webgl_is_mac, (void), {
try {
return !!navigator.platform.match(/mac/i);
} catch(e) {
// whatever...
return false;
}
})
#endif
static const char *detect_slow_sampler_update(void) {
#if defined(__MACOSX__) || defined(__EMSCRIPTEN__)
const char *gl_vendor = get_unmasked_property(GL_VENDOR, true);
const char *gl_renderer = get_unmasked_property(GL_RENDERER, true);
if(
#if defined(__EMSCRIPTEN__)
webgl_is_mac() &&
#endif
strstr(gl_renderer, "Radeon") && ( // This looks like an AMD Radeon card...
(strstr(gl_vendor, "ATI") || strstr(gl_vendor, "AMD")) || // ...and AMD's official driver...
(strstr(gl_vendor, "Google") && strstr(gl_renderer, "OpenGL")) // ...or ANGLE, backed by OpenGL.
)
) {
return "buggy AMD driver on macOS; see https://github.com/taisei-project/taisei/issues/182";
}
#endif
return NULL;
}
static void glcommon_check_issues(void) {
glext.issues.avoid_sampler_uniform_updates = glcommon_check_workaround(
"avoid sampler uniform updates",
"TAISEI_GL_WORKAROUND_AVOID_SAMPLER_UNIFORM_UPDATES",
detect_slow_sampler_update
);
}
static inline void (*load_gl_func(const char *name))(void);
void glcommon_check_capabilities(void) {
const char *glslv = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
const char *glv = (const char*)glGetString(GL_VERSION);
if(!glslv) {
glslv = "None";
}
glext.version.is_es = strstartswith(glv, "OpenGL ES");
#ifdef STATIC_GLES3
if(!GLES_ATLEAST(3, 0)) {
log_fatal("Compiled with STATIC_GLES3, but got a non-GLES3 context. Can't work with this");
}
#endif
if(glext.version.is_es) {
glext.version.is_ANGLE = strstr(glv, "(ANGLE ");
glext.version.is_webgl = strstr(glv, "(WebGL ");
}
if(!glext.version.is_webgl) {
glext.version.is_webgl = glcommon_check_extension("GL_ANGLE_webgl_compatibility");
}
if(glext.version.is_webgl) {
log_info("WebGL compatibility mode");
}
log_info("OpenGL version: %s", glv);
log_info("OpenGL vendor: %s", (const char*)glGetString(GL_VENDOR));
log_info("OpenGL renderer: %s", (const char*)glGetString(GL_RENDERER));
if(glext.version.is_webgl) {
const char *unmasked_vendor = get_unmasked_property(GL_VENDOR, false);
const char *unmasked_renderer = get_unmasked_property(GL_RENDERER, false);
log_info("OpenGL unmasked vendor: %s", unmasked_vendor ? unmasked_vendor : "Unknown");
log_info("OpenGL unmasked renderer: %s", unmasked_renderer ? unmasked_renderer : "Unknown");
}
log_info("GLSL version: %s", glslv);
if(!GL_ATLEAST(3, 3) && !GLES_ATLEAST(3, 0)) {
log_warn("Unsupported OpenGL version, expect problems");
}
detect_broken_intel_driver();
#ifndef STATIC_GLES3
if(glcommon_check_extension("GL_ANGLE_request_extension")) {
PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = (PFNGLREQUESTEXTENSIONANGLEPROC)load_gl_func("glRequestExtensionANGLE");
assert(glRequestExtensionANGLE != NULL);
const char *src_string = (const char*)glGetString(GL_REQUESTABLE_EXTENSIONS_ANGLE);
char exts[strlen(src_string) + 1];
char *extsptr = exts, *ext;
memcpy(exts, src_string, sizeof(exts));
log_info("Requestable extensions: %s", src_string);
while((ext = strtok_r(NULL, " ", &extsptr))) {
if(!ext || !*ext || !env_get(ext, true)) {
continue;
}
glRequestExtensionANGLE(ext);
}
}
#endif
// XXX: this is the legacy way, maybe we shouldn't try this first
const char *exts = (const char*)glGetString(GL_EXTENSIONS);
if(exts) {
log_info("Supported extensions: %s", exts);
} else {
void *buf;
SDL_RWops *writer = SDL_RWAutoBuffer(&buf, 1024);
GLint num_extensions;
SDL_RWprintf(writer, "Supported extensions:");
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
for(int i = 0; i < num_extensions; ++i) {
SDL_RWprintf(writer, " %s", (const char*)glGetStringi(GL_EXTENSIONS, i));
}
SDL_WriteU8(writer, 0);
log_info("%s", (char*)buf);
SDL_RWclose(writer);
}
glcommon_ext_clear_texture();
glcommon_ext_color_buffer_float();
glcommon_ext_debug_output();
glcommon_ext_depth_texture();
glcommon_ext_draw_buffers();
glcommon_ext_float_blend();
glcommon_ext_instanced_arrays();
glcommon_ext_internalformat_query2();
glcommon_ext_pixel_buffer_object();
glcommon_ext_texture_filter_anisotropic();
glcommon_ext_texture_float();
glcommon_ext_texture_float_linear();
glcommon_ext_texture_half_float();
glcommon_ext_texture_half_float_linear();
glcommon_ext_texture_norm16();
glcommon_ext_texture_rg();
glcommon_ext_texture_swizzle();
glcommon_ext_vertex_array_object();
glcommon_ext_viewport_array();
glcommon_ext_texture_format_r8_srgb();
glcommon_ext_texture_format_rg8_srgb();
glcommon_ext_texture_format_rgb8_rgba8_srgb();
glcommon_ext_texture_format_s3tc_dx1();
glcommon_ext_texture_format_s3tc_dx5();
glcommon_ext_texture_format_s3tc_srgb();
glcommon_ext_texture_format_rgtc();
glcommon_ext_texture_format_etc1();
glcommon_ext_texture_format_etc1_srgb();
glcommon_ext_texture_format_etc2_eac();
glcommon_ext_texture_format_etc2_eac_srgb();
glcommon_ext_texture_format_bptc();
glcommon_ext_texture_format_pvrtc();
glcommon_ext_texture_format_pvrtc2();
glcommon_ext_texture_format_pvrtc_srgb();
glcommon_ext_texture_format_astc();
glcommon_ext_texture_format_atc();
glcommon_ext_texture_format_fxt1();
glcommon_build_shader_lang_table();
glcommon_init_texture_formats();
glcommon_check_issues();
}
void glcommon_load_library(void) {
#ifndef STATIC_GLES3
const char *lib = env_get("TAISEI_LIBGL", "");
if(!*lib) {
lib = NULL;
}
if(SDL_GL_LoadLibrary(lib) < 0) {
log_fatal("SDL_GL_LoadLibrary() failed: %s", SDL_GetError());
}
#endif
}
void glcommon_unload_library(void) {
#ifndef STATIC_GLES3
SDL_GL_UnloadLibrary();
#endif
glcommon_free_shader_lang_table();
glcommon_free_texture_formats();
}
attr_unused
static inline void (*load_gl_func(const char *name))(void) {
union {
void *vp;
void (*fp)(void);
} c_sucks;
c_sucks.vp = SDL_GL_GetProcAddress(name);
// log_debug("%s: %p", name, c_sucks.vp);
return c_sucks.fp;
}
void glcommon_load_functions(void) {
#ifndef STATIC_GLES3
int profile, version;
if(SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile) < 0) {
log_fatal("SDL_GL_GetAttribute() failed: %s", SDL_GetError());
}
if(profile == SDL_GL_CONTEXT_PROFILE_ES) {
if(!(version = gladLoadGLES2(load_gl_func))) {
log_fatal("Failed to load OpenGL ES functions");
}
} else {
if(!(version = gladLoadGL(load_gl_func))) {
log_fatal("Failed to load OpenGL functions");
}
}
glext.version.major = GLAD_VERSION_MAJOR(version);
glext.version.minor = GLAD_VERSION_MINOR(version);
log_debug("GLAD reported OpenGL version %i.%i", glext.version.major, glext.version.minor);
// GLES has only glClearDepthf
// Core has only glClearDepth until GL 4.1
if(!HAVE_GL_FUNC(glClearDepth)) {
glClearDepth = shim_glClearDepth;
assert(HAVE_GL_FUNC(glClearDepthf));
}
if(!HAVE_GL_FUNC(glClearDepthf)) {
glClearDepthf = shim_glClearDepthf;
assert(HAVE_GL_FUNC(glClearDepth));
}
#else
GLint major, minor;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
glext.version.major = major;
glext.version.minor = minor;
#endif
}
void glcommon_setup_attributes(SDL_GLprofile profile, uint major, uint minor, SDL_GLcontextFlag ctxflags) {
if(glcommon_debug_requested()) {
ctxflags |= SDL_GL_CONTEXT_DEBUG_FLAG;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
#ifndef __EMSCRIPTEN__
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, ctxflags);
#endif
}