/* =========================================================================== Copyright (C) 2011 James Canete (use.less01@gmail.com) This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // tr_extensions.c - extensions needed by the renderer not in sdl_glimp.c #ifdef USE_LOCAL_HEADERS # include "SDL.h" #else # include #endif #include "tr_local.h" #include "tr_dsa.h" #define GLE(ret, name, ...) name##proc * qgl##name; QGL_1_2_PROCS; QGL_1_3_PROCS; QGL_1_4_PROCS; QGL_1_5_PROCS; QGL_2_0_PROCS; QGL_EXT_framebuffer_object_PROCS; QGL_EXT_framebuffer_blit_PROCS; QGL_EXT_framebuffer_multisample_PROCS; QGL_ARB_vertex_array_object_PROCS; QGL_EXT_direct_state_access_PROCS; #undef GLE // GL_EXT_framebuffer_object GLboolean (APIENTRY * qglIsRenderbufferEXT)(GLuint renderbuffer); void (APIENTRY * qglBindRenderbufferEXT)(GLenum target, GLuint renderbuffer); void (APIENTRY * qglDeleteRenderbuffersEXT)(GLsizei n, const GLuint *renderbuffers); void (APIENTRY * qglGenRenderbuffersEXT)(GLsizei n, GLuint *renderbuffers); void (APIENTRY * qglRenderbufferStorageEXT)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); void (APIENTRY * qglGetRenderbufferParameterivEXT)(GLenum target, GLenum pname, GLint *params); GLboolean (APIENTRY * qglIsFramebufferEXT)(GLuint framebuffer); void (APIENTRY * qglBindFramebufferEXT)(GLenum target, GLuint framebuffer); void (APIENTRY * qglDeleteFramebuffersEXT)(GLsizei n, const GLuint *framebuffers); void (APIENTRY * qglGenFramebuffersEXT)(GLsizei n, GLuint *framebuffers); GLenum (APIENTRY * qglCheckFramebufferStatusEXT)(GLenum target); void (APIENTRY * qglFramebufferTexture1DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); void (APIENTRY * qglFramebufferTexture2DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); void (APIENTRY * qglFramebufferTexture3DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); void (APIENTRY * qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); void (APIENTRY * qglGetFramebufferAttachmentParameterivEXT)(GLenum target, GLenum attachment, GLenum pname, GLint *params); void (APIENTRY * qglGenerateMipmapEXT)(GLenum target); // GL_EXT_framebuffer_blit void (APIENTRY * qglBlitFramebufferEXT)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); // GL_EXT_framebuffer_multisample void (APIENTRY * qglRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); // GL_ARB_vertex_array_object void (APIENTRY * qglBindVertexArrayARB)(GLuint array); void (APIENTRY * qglDeleteVertexArraysARB)(GLsizei n, const GLuint *arrays); void (APIENTRY * qglGenVertexArraysARB)(GLsizei n, GLuint *arrays); GLboolean (APIENTRY * qglIsVertexArrayARB)(GLuint array); // GL_EXT_direct_state_access GLvoid (APIENTRY * qglBindMultiTexture)(GLenum texunit, GLenum target, GLuint texture); GLvoid (APIENTRY * qglTextureParameterf)(GLuint texture, GLenum target, GLenum pname, GLfloat param); GLvoid (APIENTRY * qglTextureParameteri)(GLuint texture, GLenum target, GLenum pname, GLint param); GLvoid (APIENTRY * qglTextureImage2D)(GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); GLvoid (APIENTRY * qglTextureSubImage2D)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); GLvoid (APIENTRY * qglCopyTextureImage2D)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); GLvoid (APIENTRY * qglCompressedTextureImage2D)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); GLvoid (APIENTRY * qglCompressedTextureSubImage2D)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); GLvoid (APIENTRY * qglGenerateTextureMipmap)(GLuint texture, GLenum target); GLvoid(APIENTRY * qglProgramUniform1i)(GLuint program, GLint location, GLint v0); GLvoid(APIENTRY * qglProgramUniform1f)(GLuint program, GLint location, GLfloat v0); GLvoid(APIENTRY * qglProgramUniform2f)(GLuint program, GLint location, GLfloat v0, GLfloat v1); GLvoid(APIENTRY * qglProgramUniform3f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); GLvoid(APIENTRY * qglProgramUniform4f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); GLvoid(APIENTRY * qglProgramUniform1fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value); GLvoid(APIENTRY * qglProgramUniformMatrix4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLvoid(APIENTRY * qglNamedRenderbufferStorage)(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); GLvoid(APIENTRY * qglNamedRenderbufferStorageMultisample)(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); GLenum(APIENTRY * qglCheckNamedFramebufferStatus)(GLuint framebuffer, GLenum target); GLvoid(APIENTRY * qglNamedFramebufferTexture2D)(GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLvoid(APIENTRY * qglNamedFramebufferRenderbuffer)(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); static qboolean GLimp_HaveExtension(const char *ext) { const char *ptr = Q_stristr( glConfig.extensions_string, ext ); if (ptr == NULL) return qfalse; ptr += strlen(ext); return ((*ptr == ' ') || (*ptr == '\0')); // verify it's complete string. } void GLimp_InitExtraExtensions() { char *extension; const char* result[3] = { "...ignoring %s\n", "...using %s\n", "...%s not found\n" }; // Check OpenGL version sscanf(glConfig.version_string, "%d.%d", &glRefConfig.openglMajorVersion, &glRefConfig.openglMinorVersion); if (glRefConfig.openglMajorVersion < 2) ri.Error(ERR_FATAL, "OpenGL 2.0 required!"); ri.Printf(PRINT_ALL, "...using OpenGL %s\n", glConfig.version_string); // set DSA fallbacks #define GLE(ret, name, ...) qgl##name = GLDSA_##name; QGL_EXT_direct_state_access_PROCS; #undef GLE // GL function loader, based on https://gist.github.com/rygorous/16796a0c876cf8a5f542caddb55bce8a #define GLE(ret, name, ...) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name); // OpenGL 1.2, was GL_EXT_draw_range_elements QGL_1_2_PROCS; glRefConfig.drawRangeElements = !!r_ext_draw_range_elements->integer; ri.Printf(PRINT_ALL, result[glRefConfig.drawRangeElements], "glDrawRangeElements()"); // OpenGL 1.3, was GL_ARB_texture_compression QGL_1_3_PROCS; // OpenGL 1.4, was GL_EXT_multi_draw_arrays QGL_1_4_PROCS; glRefConfig.multiDrawArrays = !!r_ext_multi_draw_arrays->integer; ri.Printf(PRINT_ALL, result[glRefConfig.multiDrawArrays], "glMultiDrawElements()"); // OpenGL 1.5, was GL_ARB_vertex_buffer_object and GL_ARB_occlusion_query QGL_1_5_PROCS; glRefConfig.occlusionQuery = qtrue; // OpenGL 2.0, was GL_ARB_shading_language_100, GL_ARB_vertex_program, GL_ARB_shader_objects, and GL_ARB_vertex_shader QGL_2_0_PROCS; // Determine GLSL version if (1) { char version[256]; Q_strncpyz(version, (char *)qglGetString(GL_SHADING_LANGUAGE_VERSION), sizeof(version)); sscanf(version, "%d.%d", &glRefConfig.glslMajorVersion, &glRefConfig.glslMinorVersion); ri.Printf(PRINT_ALL, "...using GLSL version %s\n", version); } glRefConfig.memInfo = MI_NONE; // GL_NVX_gpu_memory_info extension = "GL_NVX_gpu_memory_info"; if( GLimp_HaveExtension( extension ) ) { glRefConfig.memInfo = MI_NVX; ri.Printf(PRINT_ALL, result[1], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_ATI_meminfo extension = "GL_ATI_meminfo"; if( GLimp_HaveExtension( extension ) ) { if (glRefConfig.memInfo == MI_NONE) { glRefConfig.memInfo = MI_ATI; ri.Printf(PRINT_ALL, result[1], extension); } else { ri.Printf(PRINT_ALL, result[0], extension); } } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_ARB_texture_non_power_of_two extension = "GL_ARB_texture_non_power_of_two"; glRefConfig.textureNonPowerOfTwo = qfalse; if( GLimp_HaveExtension( extension ) ) { glRefConfig.textureNonPowerOfTwo = qtrue; // !!r_ext_texture_non_power_of_two->integer ri.Printf(PRINT_ALL, result[glRefConfig.textureNonPowerOfTwo], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_ARB_texture_float extension = "GL_ARB_texture_float"; glRefConfig.textureFloat = qfalse; if( GLimp_HaveExtension( extension ) ) { glRefConfig.textureFloat = !!r_ext_texture_float->integer; ri.Printf(PRINT_ALL, result[glRefConfig.textureFloat], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_ARB_half_float_pixel extension = "GL_ARB_half_float_pixel"; glRefConfig.halfFloatPixel = qfalse; if( GLimp_HaveExtension( extension ) ) { glRefConfig.halfFloatPixel = !!r_arb_half_float_pixel->integer; ri.Printf(PRINT_ALL, result[glRefConfig.halfFloatPixel], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_EXT_framebuffer_object extension = "GL_EXT_framebuffer_object"; glRefConfig.framebufferObject = qfalse; if( GLimp_HaveExtension( extension ) ) { glRefConfig.framebufferObject = !!r_ext_framebuffer_object->integer; glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &glRefConfig.maxRenderbufferSize); glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &glRefConfig.maxColorAttachments); QGL_EXT_framebuffer_object_PROCS; ri.Printf(PRINT_ALL, result[glRefConfig.framebufferObject], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_EXT_framebuffer_blit extension = "GL_EXT_framebuffer_blit"; glRefConfig.framebufferBlit = qfalse; if (GLimp_HaveExtension(extension)) { glRefConfig.framebufferBlit = qtrue; QGL_EXT_framebuffer_blit_PROCS; ri.Printf(PRINT_ALL, result[glRefConfig.framebufferBlit], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_EXT_framebuffer_multisample extension = "GL_EXT_framebuffer_multisample"; glRefConfig.framebufferMultisample = qfalse; if (GLimp_HaveExtension(extension)) { glRefConfig.framebufferMultisample = qtrue; QGL_EXT_framebuffer_multisample_PROCS; ri.Printf(PRINT_ALL, result[glRefConfig.framebufferMultisample], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } glRefConfig.textureCompression = TCR_NONE; // GL_ARB_texture_compression_rgtc extension = "GL_ARB_texture_compression_rgtc"; if (GLimp_HaveExtension(extension)) { qboolean useRgtc = r_ext_compressed_textures->integer >= 1; if (useRgtc) glRefConfig.textureCompression |= TCR_RGTC; ri.Printf(PRINT_ALL, result[useRgtc], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } glRefConfig.swizzleNormalmap = r_ext_compressed_textures->integer && !(glRefConfig.textureCompression & TCR_RGTC); // GL_ARB_texture_compression_bptc extension = "GL_ARB_texture_compression_bptc"; if (GLimp_HaveExtension(extension)) { qboolean useBptc = r_ext_compressed_textures->integer >= 2; if (useBptc) glRefConfig.textureCompression |= TCR_BPTC; ri.Printf(PRINT_ALL, result[useBptc], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_ARB_depth_clamp extension = "GL_ARB_depth_clamp"; glRefConfig.depthClamp = qfalse; if( GLimp_HaveExtension( extension ) ) { glRefConfig.depthClamp = qtrue; ri.Printf(PRINT_ALL, result[glRefConfig.depthClamp], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_ARB_seamless_cube_map extension = "GL_ARB_seamless_cube_map"; glRefConfig.seamlessCubeMap = qfalse; if( GLimp_HaveExtension( extension ) ) { glRefConfig.seamlessCubeMap = !!r_arb_seamless_cube_map->integer; ri.Printf(PRINT_ALL, result[glRefConfig.seamlessCubeMap], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_ARB_vertex_type_2_10_10_10_rev extension = "GL_ARB_vertex_type_2_10_10_10_rev"; glRefConfig.packedNormalDataType = GL_BYTE; if( GLimp_HaveExtension( extension ) ) { qboolean useExt = !!r_arb_vertex_type_2_10_10_10_rev->integer; if (useExt) glRefConfig.packedNormalDataType = GL_INT_2_10_10_10_REV; ri.Printf(PRINT_ALL, result[useExt], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // use float lightmaps? glRefConfig.floatLightmap = (glRefConfig.textureFloat && glRefConfig.halfFloatPixel && r_floatLightmap->integer && r_hdr->integer); // GL_ARB_vertex_array_object extension = "GL_ARB_vertex_array_object"; glRefConfig.vertexArrayObject = qfalse; if( GLimp_HaveExtension( extension ) ) { glRefConfig.vertexArrayObject = !!r_arb_vertex_array_object->integer; QGL_ARB_vertex_array_object_PROCS; ri.Printf(PRINT_ALL, result[glRefConfig.vertexArrayObject], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_ARB_half_float_vertex extension = "GL_ARB_half_float_vertex"; glRefConfig.packedTexcoordDataType = GL_FLOAT; glRefConfig.packedTexcoordDataSize = sizeof(float) * 2; glRefConfig.packedColorDataType = GL_FLOAT; glRefConfig.packedColorDataSize = sizeof(float) * 4; if( GLimp_HaveExtension( extension ) ) { qboolean useExt = !!r_arb_half_float_vertex->integer; if (useExt) { glRefConfig.packedTexcoordDataType = GL_HALF_FLOAT; glRefConfig.packedTexcoordDataSize = sizeof(uint16_t) * 2; glRefConfig.packedColorDataType = GL_HALF_FLOAT; glRefConfig.packedColorDataSize = sizeof(uint16_t) * 4; } ri.Printf(PRINT_ALL, result[useExt], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } // GL_EXT_direct_state_access extension = "GL_EXT_direct_state_access"; glRefConfig.directStateAccess = qfalse; if (GLimp_HaveExtension(extension)) { glRefConfig.directStateAccess = !!r_ext_direct_state_access->integer; // QGL_*_PROCS becomes several functions, do not remove {} if (glRefConfig.directStateAccess) { QGL_EXT_direct_state_access_PROCS; } ri.Printf(PRINT_ALL, result[glRefConfig.directStateAccess], extension); } else { ri.Printf(PRINT_ALL, result[2], extension); } #undef GLE }