/* Copyright (C) 2010-2020 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (gl_capabilities.c). * --------------------------------------------------------------------------------------- * * Permission is hereby granted, free of charge, * to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include static bool gl_core_context = false; bool gl_query_core_context_in_use(void) { return gl_core_context; } void gl_query_core_context_set(bool set) { gl_core_context = set; } void gl_query_core_context_unset(void) { gl_core_context = false; } bool gl_query_extension(const char *ext) { bool ret = false; if (gl_query_core_context_in_use()) { #ifdef GL_NUM_EXTENSIONS GLint i; GLint exts = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &exts); for (i = 0; i < exts; i++) { const char *str = (const char*)glGetStringi(GL_EXTENSIONS, i); if (str && strstr(str, ext)) { ret = true; break; } } #endif } else { const char *str = (const char*)glGetString(GL_EXTENSIONS); ret = str && strstr(str, ext); } return ret; } bool gl_check_error(char **error_string) { int error = glGetError(); switch (error) { case GL_INVALID_ENUM: *error_string = strdup("GL: Invalid enum."); break; case GL_INVALID_VALUE: *error_string = strdup("GL: Invalid value."); break; case GL_INVALID_OPERATION: *error_string = strdup("GL: Invalid operation."); break; case GL_OUT_OF_MEMORY: *error_string = strdup("GL: Out of memory."); break; case GL_NO_ERROR: return true; default: *error_string = strdup("Non specified GL error."); break; } return false; } bool gl_check_capability(enum gl_capability_enum enum_idx) { unsigned major = 0; unsigned minor = 0; const char *vendor = (const char*)glGetString(GL_VENDOR); const char *renderer = (const char*)glGetString(GL_RENDERER); const char *version = (const char*)glGetString(GL_VERSION); #ifdef HAVE_OPENGLES if (version && sscanf(version, "OpenGL ES %u.%u", &major, &minor) != 2) #else if (version && sscanf(version, "%u.%u", &major, &minor) != 2) #endif major = minor = 0; (void)vendor; (void)renderer; switch (enum_idx) { case GL_CAPS_GLES3_SUPPORTED: #if defined(HAVE_OPENGLES) if (major >= 3) return true; #endif break; case GL_CAPS_EGLIMAGE: #if defined(HAVE_EGL) && defined(HAVE_OPENGLES) if (glEGLImageTargetTexture2DOES != NULL) return true; #endif break; case GL_CAPS_SYNC: #ifdef HAVE_OPENGLES if (major >= 3) return true; #else if (gl_query_extension("ARB_sync") && glFenceSync && glDeleteSync && glClientWaitSync) return true; #endif break; case GL_CAPS_MIPMAP: { static bool extension_queried = false; static bool extension = false; if (!extension_queried) { extension = gl_query_extension("ARB_framebuffer_object"); extension_queried = true; } if (extension) return true; } break; case GL_CAPS_VAO: #ifndef HAVE_OPENGLES if (!gl_query_core_context_in_use() && !gl_query_extension("ARB_vertex_array_object")) return false; if (glGenVertexArrays && glBindVertexArray && glDeleteVertexArrays) return true; #endif break; case GL_CAPS_FBO: #if defined(HAVE_PSGL) || defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES3) || defined(HAVE_OPENGLES_3_1) || defined(HAVE_OPENGLES_3_2) return true; #else if ( !gl_query_core_context_in_use() && !gl_query_extension("ARB_framebuffer_object") && !gl_query_extension("EXT_framebuffer_object")) return false; if (gl_query_extension("ARB_framebuffer_object")) return true; if (gl_query_extension("EXT_framebuffer_object")) return true; if (major >= 3) return true; break; #endif case GL_CAPS_ARGB8: #ifdef HAVE_OPENGLES if (gl_query_extension("OES_rgb8_rgba8") || gl_query_extension("ARM_rgba8") || major >= 3) return true; #else /* TODO/FIXME - implement this for non-GLES? */ #endif break; case GL_CAPS_DEBUG: if (gl_query_extension("KHR_debug")) return true; #ifndef HAVE_OPENGLES if (gl_query_extension("ARB_debug_output")) return true; #endif break; case GL_CAPS_PACKED_DEPTH_STENCIL: if (major >= 3) return true; if (gl_query_extension("OES_packed_depth_stencil")) return true; if (gl_query_extension("EXT_packed_depth_stencil")) return true; break; case GL_CAPS_ES2_COMPAT: #ifndef HAVE_OPENGLES /* ATI card detected, skipping check for GL_RGB565 support... */ if (vendor && renderer && (strstr(vendor, "ATI") || strstr(renderer, "ATI"))) return false; if (gl_query_extension("ARB_ES2_compatibility")) return true; #endif break; case GL_CAPS_UNPACK_ROW_LENGTH: #ifdef HAVE_OPENGLES if (major >= 3) return true; /* Extension GL_EXT_unpack_subimage, can copy textures faster * than using UNPACK_ROW_LENGTH */ if (gl_query_extension("GL_EXT_unpack_subimage")) return true; #endif break; case GL_CAPS_FULL_NPOT_SUPPORT: if (major >= 3) return true; #ifdef HAVE_OPENGLES if (gl_query_extension("ARB_texture_non_power_of_two") || gl_query_extension("OES_texture_npot")) return true; #else { GLint max_texture_size = 0; GLint max_native_instr = 0; /* try to detect actual npot support. might fail for older cards. */ bool arb_npot = gl_query_extension("ARB_texture_non_power_of_two"); bool arb_frag_program = gl_query_extension("ARB_fragment_program"); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); #ifdef GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB if (arb_frag_program && glGetProgramivARB) glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &max_native_instr); #endif if (arb_npot && arb_frag_program && (max_texture_size >= 8192) && (max_native_instr >= 4096)) return true; } #endif break; case GL_CAPS_SRGB_FBO_ES3: #ifdef HAVE_OPENGLES if (major >= 3) return true; #else break; #endif case GL_CAPS_SRGB_FBO: #if defined(HAVE_OPENGLES) if (major >= 3 || gl_query_extension("EXT_sRGB")) return true; #endif if (gl_check_capability(GL_CAPS_FBO)) { if ( gl_query_core_context_in_use() || (gl_query_extension("EXT_texture_sRGB") && gl_query_extension("ARB_framebuffer_sRGB")) ) return true; } break; case GL_CAPS_FP_FBO: /* GLES - No extensions for float FBO currently. */ #ifndef HAVE_OPENGLES if (gl_check_capability(GL_CAPS_FBO)) { /* Float FBO is core in 3.2. */ if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float")) return true; } #endif break; case GL_CAPS_BGRA8888: #ifdef HAVE_OPENGLES /* There are both APPLE and EXT variants. */ if (gl_query_extension("BGRA8888")) return true; #else return true; #endif break; case GL_CAPS_TEX_STORAGE: #ifdef HAVE_OPENGLES if (major >= 3) return true; #else if (vendor && strstr(vendor, "ATI Technologies")) return false; if (gl_query_extension("ARB_texture_storage")) return true; #endif break; case GL_CAPS_TEX_STORAGE_EXT: #ifdef TARGET_OS_IPHONE /* Not working on iOS */ return false; #else if (gl_query_extension("EXT_texture_storage")) return true; #endif break; case GL_CAPS_NONE: default: break; } return false; }