1010 lines
24 KiB
C
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
|
|
}
|