2016-09-14 18:01:13 +00:00
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2005-2016 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
2013-06-23 07:49:34 +00:00
/*
* * r_opengl . cpp
* *
* * OpenGL system interface
* *
*/
2016-09-14 18:01:13 +00:00
2013-06-23 07:49:34 +00:00
# include "gl/system/gl_system.h"
# include "tarray.h"
# include "doomtype.h"
# include "m_argv.h"
# include "zstring.h"
# include "version.h"
# include "i_system.h"
2013-09-02 06:43:56 +00:00
# include "v_text.h"
2016-04-26 16:24:02 +00:00
# include "r_data/r_translate.h"
2013-09-03 16:29:39 +00:00
# include "gl/system/gl_interface.h"
2013-06-23 07:49:34 +00:00
# include "gl/system/gl_cvars.h"
2016-05-01 11:09:13 +00:00
void gl_PatchMenu ( ) ;
2013-06-23 07:49:34 +00:00
static TArray < FString > m_Extensions ;
2013-08-18 13:41:52 +00:00
RenderContext gl ;
2013-06-23 07:49:34 +00:00
//==========================================================================
//
//
//
//==========================================================================
static void CollectExtensions ( )
{
2014-07-15 00:26:23 +00:00
const char * extension ;
2013-06-23 07:49:34 +00:00
2014-07-15 00:26:23 +00:00
int max = 0 ;
glGetIntegerv ( GL_NUM_EXTENSIONS , & max ) ;
2013-06-23 07:49:34 +00:00
2016-04-30 14:23:32 +00:00
if ( 0 = = max )
2013-06-23 07:49:34 +00:00
{
2016-04-30 14:23:32 +00:00
// Try old method to collect extensions
const char * supported = ( char * ) glGetString ( GL_EXTENSIONS ) ;
if ( nullptr ! = supported )
{
char * extensions = new char [ strlen ( supported ) + 1 ] ;
strcpy ( extensions , supported ) ;
char * extension = strtok ( extensions , " " ) ;
while ( extension )
{
m_Extensions . Push ( FString ( extension ) ) ;
extension = strtok ( nullptr , " " ) ;
}
delete [ ] extensions ;
}
}
else
{
// Use modern method to collect extensions
for ( int i = 0 ; i < max ; i + + )
{
extension = ( const char * ) glGetStringi ( GL_EXTENSIONS , i ) ;
m_Extensions . Push ( FString ( extension ) ) ;
}
2013-06-23 07:49:34 +00:00
}
}
//==========================================================================
//
//
//
//==========================================================================
static bool CheckExtension ( const char * ext )
{
for ( unsigned int i = 0 ; i < m_Extensions . Size ( ) ; + + i )
{
if ( m_Extensions [ i ] . CompareNoCase ( ext ) = = 0 ) return true ;
}
return false ;
}
2013-08-18 13:41:52 +00:00
2013-06-23 07:49:34 +00:00
//==========================================================================
//
//
//
//==========================================================================
2013-08-18 13:41:52 +00:00
static void InitContext ( )
2013-06-23 07:49:34 +00:00
{
2013-08-18 13:41:52 +00:00
gl . flags = 0 ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-04-26 11:50:05 +00:00
# define FUDGE_FUNC(name, ext) if (_ptrc_##name == NULL) _ptrc_##name = _ptrc_##name##ext;
2013-08-18 13:41:52 +00:00
void gl_LoadExtensions ( )
{
InitContext ( ) ;
2013-06-23 07:49:34 +00:00
CollectExtensions ( ) ;
2016-04-26 11:50:05 +00:00
const char * glversion = ( const char * ) glGetString ( GL_VERSION ) ;
2017-01-25 06:18:26 +00:00
gl . es = false ;
if ( glversion & & strlen ( glversion ) > 10 & & memcmp ( glversion , " OpenGL ES " , 10 ) = = 0 )
{
glversion + = 10 ;
gl . es = true ;
}
const char * version = Args - > CheckValue ( " -glversion " ) ;
2016-04-26 11:50:05 +00:00
if ( version = = NULL )
{
version = glversion ;
}
else
{
double v1 = strtod ( version , NULL ) ;
double v2 = strtod ( glversion , NULL ) ;
if ( v2 < v1 ) version = glversion ;
else Printf ( " Emulating OpenGL v %s \n " , version ) ;
}
2016-09-01 09:52:52 +00:00
float gl_version = ( float ) strtod ( version , NULL ) + 0.01f ;
2014-06-13 23:24:28 +00:00
2017-01-25 06:18:26 +00:00
if ( gl . es )
2013-06-23 07:49:34 +00:00
{
2017-01-25 06:18:26 +00:00
if ( gl_version < 2.0f )
{
I_FatalError ( " Unsupported OpenGL ES version. \n At least OpenGL ES 2.0 is required to run " GAMENAME " . \n " ) ;
}
const char * glslversion = ( const char * ) glGetString ( GL_SHADING_LANGUAGE_VERSION ) ;
if ( glslversion & & strlen ( glslversion ) > 18 & & memcmp ( glslversion , " OpenGL ES GLSL ES " , 10 ) = = 0 )
{
glslversion + = 18 ;
}
// add 0.01 to account for roundoff errors making the number a tad smaller than the actual version
gl . glslversion = strtod ( glslversion , NULL ) + 0.01f ;
gl . vendorstring = ( char * ) glGetString ( GL_VENDOR ) ;
// Use the slowest/oldest modern path for now
gl . legacyMode = false ;
gl . lightmethod = LM_DEFERRED ;
gl . buffermethod = BM_DEFERRED ;
2013-06-23 07:49:34 +00:00
}
2017-01-25 06:18:26 +00:00
else
{
// Don't even start if it's lower than 2.0 or no framebuffers are available (The framebuffer extension is needed for glGenerateMipmapsEXT!)
if ( ( gl_version < 2.0f | | ! CheckExtension ( " GL_EXT_framebuffer_object " ) ) & & gl_version < 3.0f )
{
I_FatalError ( " Unsupported OpenGL version. \n At least OpenGL 2.0 with framebuffer support is required to run " GAMENAME " . \n " ) ;
}
gl . es = false ;
2014-07-15 00:26:23 +00:00
2017-01-25 06:18:26 +00:00
// add 0.01 to account for roundoff errors making the number a tad smaller than the actual version
gl . glslversion = strtod ( ( char * ) glGetString ( GL_SHADING_LANGUAGE_VERSION ) , NULL ) + 0.01f ;
2013-06-23 07:49:34 +00:00
2017-01-25 06:18:26 +00:00
gl . vendorstring = ( char * ) glGetString ( GL_VENDOR ) ;
2013-06-23 07:49:34 +00:00
2017-01-25 06:18:26 +00:00
// first test for optional features
if ( CheckExtension ( " GL_ARB_texture_compression " ) ) gl . flags | = RFL_TEXTURE_COMPRESSION ;
if ( CheckExtension ( " GL_EXT_texture_compression_s3tc " ) ) gl . flags | = RFL_TEXTURE_COMPRESSION_S3TC ;
2016-09-01 09:52:52 +00:00
2017-01-25 06:18:26 +00:00
if ( ( gl_version > = 3.3f | | CheckExtension ( " GL_ARB_sampler_objects " ) ) & & ! Args - > CheckParm ( " -nosampler " ) )
{
gl . flags | = RFL_SAMPLER_OBJECTS ;
}
2016-04-26 11:50:05 +00:00
2017-01-25 06:18:26 +00:00
// The minimum requirement for the modern render path are GL 3.0 + uniform buffers. Also exclude the Linux Mesa driver at GL 3.0 because it errors out on shader compilation.
if ( gl_version < 3.0f | | ( gl_version < 3.1f & & ( ! CheckExtension ( " GL_ARB_uniform_buffer_object " ) | | strstr ( gl . vendorstring , " X.Org " ) ! = nullptr ) ) )
2016-08-29 08:43:03 +00:00
{
2017-01-25 06:18:26 +00:00
gl . legacyMode = true ;
gl . lightmethod = LM_LEGACY ;
gl . buffermethod = BM_LEGACY ;
gl . glslversion = 0 ;
gl . flags | = RFL_NO_CLIP_PLANES ;
2016-09-01 09:52:52 +00:00
}
2017-01-25 06:18:26 +00:00
else
2014-09-17 07:01:16 +00:00
{
2017-01-25 06:18:26 +00:00
gl . legacyMode = false ;
gl . lightmethod = LM_DEFERRED ;
gl . buffermethod = BM_DEFERRED ;
if ( gl_version < 4.f )
{
# ifdef _WIN32
if ( strstr ( gl . vendorstring , " ATI Tech " ) )
{
gl . flags | = RFL_NO_CLIP_PLANES ; // gl_ClipDistance is horribly broken on ATI GL3 drivers for Windows.
}
# endif
}
else if ( gl_version < 4.5f )
2014-09-17 07:01:16 +00:00
{
2017-01-25 06:18:26 +00:00
// don't use GL 4.x features when running a GL 3.x context.
if ( CheckExtension ( " GL_ARB_buffer_storage " ) )
2014-12-01 08:58:23 +00:00
{
2017-01-25 06:18:26 +00:00
// work around a problem with older AMD drivers: Their implementation of shader storage buffer objects is piss-poor and does not match uniform buffers even closely.
// Recent drivers, GL 4.4 don't have this problem, these can easily be recognized by also supporting the GL_ARB_buffer_storage extension.
if ( CheckExtension ( " GL_ARB_shader_storage_buffer_object " ) )
2016-09-01 09:52:52 +00:00
{
2017-03-11 19:14:18 +00:00
// Intel's GLSL compiler is a bit broken with extensions, so unlock the feature only if not on Intel or having GL 4.3.
if ( strstr ( gl . vendorstring , " Intel " ) = = NULL | | gl_version > = 4.3f )
2017-01-25 06:18:26 +00:00
{
gl . flags | = RFL_SHADER_STORAGE_BUFFER ;
}
2016-09-01 09:52:52 +00:00
}
2017-01-25 06:18:26 +00:00
gl . flags | = RFL_BUFFER_STORAGE ;
gl . lightmethod = LM_DIRECT ;
gl . buffermethod = BM_PERSISTENT ;
2014-12-01 08:58:23 +00:00
}
2017-01-25 06:18:26 +00:00
}
else
{
// Assume that everything works without problems on GL 4.5 drivers where these things are core features.
gl . flags | = RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE ;
gl . lightmethod = LM_DIRECT ;
2016-09-01 09:52:52 +00:00
gl . buffermethod = BM_PERSISTENT ;
2014-09-17 07:01:16 +00:00
}
2016-04-26 11:50:05 +00:00
2017-01-25 06:18:26 +00:00
if ( gl_version > = 4.3f | | CheckExtension ( " GL_ARB_invalidate_subdata " ) ) gl . flags | = RFL_INVALIDATE_BUFFER ;
if ( gl_version > = 4.3f | | CheckExtension ( " GL_KHR_debug " ) ) gl . flags | = RFL_DEBUG ;
2016-08-17 15:37:13 +00:00
2017-01-25 06:18:26 +00:00
const char * lm = Args - > CheckValue ( " -lightmethod " ) ;
if ( lm ! = NULL )
{
if ( ! stricmp ( lm , " deferred " ) & & gl . lightmethod = = LM_DIRECT ) gl . lightmethod = LM_DEFERRED ;
}
2016-04-26 11:50:05 +00:00
2017-01-25 06:18:26 +00:00
lm = Args - > CheckValue ( " -buffermethod " ) ;
if ( lm ! = NULL )
{
if ( ! stricmp ( lm , " deferred " ) & & gl . buffermethod = = BM_PERSISTENT ) gl . buffermethod = BM_DEFERRED ;
}
2016-09-01 09:52:52 +00:00
}
2016-08-06 10:03:16 +00:00
}
2014-09-14 21:01:57 +00:00
int v ;
2016-04-30 14:31:09 +00:00
2016-09-01 09:52:52 +00:00
if ( ! gl . legacyMode & & ! ( gl . flags & RFL_SHADER_STORAGE_BUFFER ) )
2016-04-30 14:31:09 +00:00
{
glGetIntegerv ( GL_MAX_FRAGMENT_UNIFORM_COMPONENTS , & v ) ;
gl . maxuniforms = v ;
glGetIntegerv ( GL_MAX_UNIFORM_BLOCK_SIZE , & v ) ;
gl . maxuniformblock = v ;
glGetIntegerv ( GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT , & v ) ;
gl . uniformblockalignment = v ;
}
else
{
gl . maxuniforms = 0 ;
gl . maxuniformblock = 0 ;
gl . uniformblockalignment = 0 ;
}
2016-04-26 11:50:05 +00:00
glGetIntegerv ( GL_MAX_TEXTURE_SIZE , & gl . max_texturesize ) ;
2013-06-23 07:49:34 +00:00
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
2016-04-26 11:50:05 +00:00
2016-09-01 09:52:52 +00:00
if ( gl . legacyMode )
{
// fudge a bit with the framebuffer stuff to avoid redundancies in the main code. Some of the older cards do not have the ARB stuff but the calls are nearly identical.
FUDGE_FUNC ( glGenerateMipmap , EXT ) ;
FUDGE_FUNC ( glGenFramebuffers , EXT ) ;
FUDGE_FUNC ( glBindFramebuffer , EXT ) ;
FUDGE_FUNC ( glDeleteFramebuffers , EXT ) ;
FUDGE_FUNC ( glFramebufferTexture2D , EXT ) ;
FUDGE_FUNC ( glGenerateMipmap , EXT ) ;
FUDGE_FUNC ( glGenFramebuffers , EXT ) ;
FUDGE_FUNC ( glBindFramebuffer , EXT ) ;
FUDGE_FUNC ( glDeleteFramebuffers , EXT ) ;
FUDGE_FUNC ( glFramebufferTexture2D , EXT ) ;
FUDGE_FUNC ( glFramebufferRenderbuffer , EXT ) ;
FUDGE_FUNC ( glGenRenderbuffers , EXT ) ;
FUDGE_FUNC ( glDeleteRenderbuffers , EXT ) ;
FUDGE_FUNC ( glRenderbufferStorage , EXT ) ;
FUDGE_FUNC ( glBindRenderbuffer , EXT ) ;
FUDGE_FUNC ( glCheckFramebufferStatus , EXT ) ;
gl_PatchMenu ( ) ;
}
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
2013-08-18 13:41:52 +00:00
void gl_PrintStartupLog ( )
2013-06-23 07:49:34 +00:00
{
2015-04-27 19:25:16 +00:00
int v = 0 ;
2016-09-01 09:52:52 +00:00
if ( ! gl . legacyMode ) glGetIntegerv ( GL_CONTEXT_PROFILE_MASK , & v ) ;
2014-08-23 23:09:44 +00:00
2013-06-23 07:49:34 +00:00
Printf ( " GL_VENDOR: %s \n " , glGetString ( GL_VENDOR ) ) ;
Printf ( " GL_RENDERER: %s \n " , glGetString ( GL_RENDERER ) ) ;
2014-08-23 23:09:44 +00:00
Printf ( " GL_VERSION: %s (%s profile) \n " , glGetString ( GL_VERSION ) , ( v & GL_CONTEXT_CORE_PROFILE_BIT ) ? " Core " : " Compatibility " ) ;
2013-06-23 07:49:34 +00:00
Printf ( " GL_SHADING_LANGUAGE_VERSION: %s \n " , glGetString ( GL_SHADING_LANGUAGE_VERSION ) ) ;
2017-01-05 11:01:00 +00:00
Printf ( PRINT_LOG , " GL_EXTENSIONS: " ) ;
2014-07-15 00:26:23 +00:00
for ( unsigned i = 0 ; i < m_Extensions . Size ( ) ; i + + )
{
2017-01-05 11:01:00 +00:00
Printf ( PRINT_LOG , " %s " , m_Extensions [ i ] . GetChars ( ) ) ;
2014-07-15 00:26:23 +00:00
}
2013-06-23 07:49:34 +00:00
2014-04-06 12:35:44 +00:00
glGetIntegerv ( GL_MAX_TEXTURE_SIZE , & v ) ;
2014-07-15 00:26:23 +00:00
Printf ( " \n Max. texture size: %d \n " , v ) ;
2013-06-23 07:49:34 +00:00
glGetIntegerv ( GL_MAX_TEXTURE_IMAGE_UNITS , & v ) ;
Printf ( " Max. texture units: %d \n " , v ) ;
glGetIntegerv ( GL_MAX_VARYING_FLOATS , & v ) ;
Printf ( " Max. varying: %d \n " , v ) ;
2016-04-30 14:31:09 +00:00
2016-09-01 09:52:52 +00:00
if ( ! gl . legacyMode & & ! ( gl . flags & RFL_SHADER_STORAGE_BUFFER ) )
2016-04-30 14:31:09 +00:00
{
glGetIntegerv ( GL_MAX_UNIFORM_BLOCK_SIZE , & v ) ;
Printf ( " Max. uniform block size: %d \n " , v ) ;
glGetIntegerv ( GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT , & v ) ;
Printf ( " Uniform block alignment: %d \n " , v ) ;
}
if ( gl . flags & RFL_SHADER_STORAGE_BUFFER )
{
glGetIntegerv ( GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS , & v ) ;
Printf ( " Max. combined shader storage blocks: %d \n " , v ) ;
glGetIntegerv ( GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS , & v ) ;
Printf ( " Max. vertex shader storage blocks: %d \n " , v ) ;
}
2013-06-23 07:49:34 +00:00
2016-04-26 16:24:02 +00:00
// For shader-less, the special alphatexture translation must be changed to actually set the alpha, because it won't get translated by a shader.
2016-09-01 09:52:52 +00:00
if ( gl . legacyMode )
2016-04-26 16:24:02 +00:00
{
FRemapTable * remap = translationtables [ TRANSLATION_Standard ] [ 8 ] ;
for ( int i = 0 ; i < 256 ; i + + )
{
remap - > Remap [ i ] = i ;
2016-04-26 17:11:32 +00:00
remap - > Palette [ i ] = PalEntry ( i , 255 , 255 , 255 ) ;
2016-04-26 16:24:02 +00:00
}
}
2014-07-30 21:13:16 +00:00
2013-06-23 07:49:34 +00:00
}