diff --git a/src/engine/renderer/GLUtils.h b/src/engine/renderer/GLUtils.h index 7c5645a99b..d0aaaccf4b 100644 --- a/src/engine/renderer/GLUtils.h +++ b/src/engine/renderer/GLUtils.h @@ -128,6 +128,8 @@ struct GLConfig bool gpuShader4Available; bool gpuShader5Available; bool textureGatherAvailable; + bool textureSrgbR8Available; + bool textureSrgbRG8Available; int maxDrawBuffers; float maxTextureAnisotropy; diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 3c66a2d249..1cdb7f49df 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -782,12 +782,17 @@ static GLint GL_ToSRGB( GLint internalFormat, bool isSRGB ) { switch ( format ) { -#if 0 // Not used in the code base. - /* EXT_texture_sRGB_R8 extension. - See: https://github.com/KhronosGroup/OpenGL-Registry/blob/main/extensions/EXT/EXT_texture_sRGB_R8.txt */ case GL_RED: + case GL_R8: + /* EXT_texture_sRGB_R8 extension. + See: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_sRGB_R8.txt */ + ASSERT( glConfig.textureSrgbR8Available ); return GL_SR8_EXT; -#endif + case GL_RG8: + /* EXT_texture_sRGB_RG8 extension. + See: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_sRGB_RG8.txt */ + ASSERT( glConfig.textureSrgbRG8Available ); + return GL_SRG8_EXT; case GL_RGB: return GL_SRGB; case GL_RGBA: @@ -796,12 +801,10 @@ static GLint GL_ToSRGB( GLint internalFormat, bool isSRGB ) return GL_SRGB8; case GL_RGBA8: return GL_SRGB8_ALPHA8; -#if 0 // Internal formats, should not be used directly. case GL_COMPRESSED_RGB: return GL_COMPRESSED_SRGB; case GL_COMPRESSED_RGBA: return GL_COMPRESSED_SRGB_ALPHA; -#endif case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index 29cb4bb626..bac7a02011 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -483,8 +483,8 @@ static void R_LoadLightmaps( lump_t *l, const char *bspName ) return; } - int lightMapBits = IF_LIGHTMAP | IF_NOPICMIP; - int deluxeMapBits = IF_NORMALMAP | IF_NOPICMIP; + int lightMapBits = IF_LIGHTMAP | IF_NOALPHA | IF_NOPICMIP; + int deluxeMapBits = IF_NORMALMAP | IF_NOALPHA | IF_NOPICMIP; if ( tr.worldLinearizeLightMap ) { diff --git a/src/engine/renderer/tr_image.cpp b/src/engine/renderer/tr_image.cpp index 3d87ecf326..af8aa9e69c 100644 --- a/src/engine/renderer/tr_image.cpp +++ b/src/engine/renderer/tr_image.cpp @@ -163,7 +163,9 @@ class ListImagesCmd : public Cmd::StaticCmd "If internalformat is specified as a base internal format, the GL stores the resulting texture with internal component resolutions of its own choosing, referred to as the effective internal format." Use 4 bytes as an estimate: */ + { GL_RGB, { "RGB", 3 } }, { GL_RGBA, { "RGBA", 4 } }, + { GL_RED, { "RED", 1 } }, { GL_RGB8, { "RGB8", 3 } }, { GL_RGBA8, { "RGBA8", 4 } }, @@ -176,10 +178,12 @@ class ListImagesCmd : public Cmd::StaticCmd { GL_RGBA32UI, { "RGBA32UI", 16 } }, { GL_ALPHA16F_ARB, { "A16F", 2 } }, { GL_ALPHA32F_ARB, { "A32F", 4 } }, + { GL_R8, { "R8", 1 } }, { GL_R16F, { "R16F", 2 } }, { GL_R32F, { "R32F", 4 } }, { GL_LUMINANCE_ALPHA16F_ARB, { "LA16F", 4 } }, { GL_LUMINANCE_ALPHA32F_ARB, { "LA32F", 8 } }, + { GL_RG8, { "RG8", 2 } }, { GL_RG16F, { "RG16F", 4 } }, { GL_RG32F, { "RG32F", 8 } }, @@ -193,6 +197,7 @@ class ListImagesCmd : public Cmd::StaticCmd If internalformat is one of the six generic compressed internal formats, its value is replaced by the symbolic constant for a specific compressed internal format of the GL’s choosing with the same base internal format." Use 4x4 blocks with 8 bytes per block here as an estimate: */ + { GL_COMPRESSED_RGB, { "RGBC", 8 } }, { GL_COMPRESSED_RGBA, { "RGBAC", 8 } }, /* https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc.txt @@ -374,6 +379,7 @@ class ListImagesCmd : public Cmd::StaticCmd { switch ( image->internalFormat ) { // Compressed formats encode blocks of 4x4 texels + case GL_COMPRESSED_RGB: case GL_COMPRESSED_RGBA: case GL_COMPRESSED_RED_RGTC1: case GL_COMPRESSED_SIGNED_RED_RGTC1: @@ -847,12 +853,14 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int const byte *data; byte *scaledBuffer = nullptr; int mipWidth, mipHeight, mipLayers, mipSize, blockSize = 0; - int i, c; const byte *scan; + + bool isSRGB = image->bits & IF_SRGB; + bool isAlpha = !( image->bits & IF_NOALPHA ); + GLenum target; GLenum format = GL_RGBA; - GLenum internalFormat = GL_RGB; - bool isSRGB = image->bits & IF_SRGB; + GLenum internalFormat = isAlpha ? GL_RGBA : GL_RGB; static const vec4_t oneClampBorder = { 1, 1, 1, 1 }; static const vec4_t zeroClampBorder = { 0, 0, 0, 1 }; @@ -1050,30 +1058,166 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int } else { - // scan the texture for each channel's max values - // and verify if the alpha channel is being used or not + internalFormat = GL_RGBA8; + } - c = image->width * image->height; - scan = dataArray[0]; + if ( internalFormat == GL_RGBA8 && !isAlpha ) + { + internalFormat = GL_RGB8; + } - // lightmap does not have alpha channel + // Detect formats. + if ( dataArray ) + { + if ( internalFormat == GL_RGBA8 ) + { + /* Scan the texture for alpha channel's max values + and verify if the alpha channel is being used or not. */ - // normalmap may have the heightmap in the alpha channel - // opaque alpha channel means no displacement, so we can enable - // alpha channel everytime it is used, even for normalmap + internalFormat = GL_RGB8; - internalFormat = GL_RGB8; + int c = image->width * image->height; - if ( !( image->bits & IF_LIGHTMAP ) ) + /* A normalmap may have the heightmap in the alpha channel, + an opaque alpha channel means no displacement, so we can enable + the alpha channel everytime it is used, even for normalmap. */ + + for ( int l = 0; l < numLayers; l++ ) + { + scan = dataArray[ l ]; + + for ( int i = 0; i < c * 4; i += 4 ) + { + if ( scan[ i + 3 ] != 255 ) + { + internalFormat = GL_RGBA8; + break; + } + } + + if ( internalFormat == GL_RGBA8 ) + { + break; + } + } + } + + if ( internalFormat == GL_RGB8 ) { - for ( i = 0; i < c; i++ ) + /* Scan the texture for green and blue channels' max values + and verify if the green and blue channels are being used or not. */ + + bool hasGreen = false; + bool hasBlue = false; + + int c = image->width * image->height; + + for ( int l = 0; l < numLayers; l++ ) { - if ( scan[ i * 4 + 3 ] != 255 ) + scan = dataArray[ l ]; + + for ( int i = 0; i < c * 4; i += 4 ) + { + if ( scan[ i + 2 ] != 0 ) + { + // We need GL_RGB8. + hasBlue = true; + break; + } + + if ( scan[ i + 1 ] != 0 ) + { + hasGreen = true; + + if ( !glConfig.textureRGAvailable ) + { + // We can't store RG so we can stop there and use GL_RGB8. + break; + } + // Else continue to make sure there is no blue at all. + } + + // Else use GL_RED or GL_R8. + } + + if ( hasBlue || ( hasGreen && !glConfig.textureRGAvailable ) ) { - internalFormat = GL_RGBA8; break; } } + + if ( hasBlue ) + { + // Keep GL_RGB8. + } + else if ( hasGreen ) + { + if ( !glConfig.textureRGAvailable ) + { + // Keep GL_RGB8. + } + else + { + if ( isSRGB && !glConfig.textureSrgbRG8Available ) + { + // Keep GL_RGB8. + } + else + { + internalFormat = GL_RG8; + } + } + } + else + { + if ( isSRGB && !glConfig.textureSrgbR8Available ) + { + // Keep GL_RGB8. + } + else + { + internalFormat = glConfig.textureRGAvailable ? GL_R8 : GL_RED; + } + } + } + } + + // Make sure we prefer GL_R8 when ARB_texture_rg is available. + ASSERT( !( internalFormat == GL_RED && glConfig.textureRGAvailable ) ); + // Make sure we only use GL_R8 when ARB_texture_rg is available. + ASSERT( !( internalFormat == GL_R8 && !glConfig.textureRGAvailable ) ); + // Make sure we only use GL_RG8 when ARB_texture_rg is available. + ASSERT( !( internalFormat == GL_RG8 && !glConfig.textureRGAvailable ) ); + // Make sure we only use GL_SR8_EXT when EXT_texture_sRGB_R8 is available. + ASSERT( !( internalFormat == GL_R8 && isSRGB && !glConfig.textureSrgbR8Available ) ); + // Make sure we only use GL_SRG8_EXT when EXT_texture_sRGB_RG8 is available. + ASSERT( !( internalFormat == GL_RG8 && isSRGB && !glConfig.textureSrgbRG8Available ) ); + + // Make sure we prefer GL_RGB but don't enforce it. GL_RGB is used when we don't set a format. + if ( internalFormat == GL_RGBA ) + { + Log::Warn( "An explicit format should be used instead of GL_RGB for image %s", name ); + } + + // Make sure we prefer GL_RGBA but don't enforce it. GL_RGBA is used when we don't set a format. + if ( internalFormat == GL_RGBA ) + { + Log::Warn( "An explicit format should be used instead of GL_RGBA for image %s", name ); + } + + if ( image->bits & IF_COMPRESS ) + { + switch ( internalFormat ) + { + case GL_RED: + case GL_RGB8: + internalFormat = GL_COMPRESSED_RGB; + break; + case GL_RGBA8: + internalFormat = GL_COMPRESSED_RGBA; + break; + default: + break; } } @@ -1086,7 +1230,7 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int mipHeight = scaledHeight; mipLayers = numLayers; - for( i = 0; i < numMips; i++ ) { + for( int i = 0; i < numMips; i++ ) { GL_TexImage3D( GL_TEXTURE_3D, i, internalFormat, scaledWidth, scaledHeight, mipLayers, 0, format, GL_UNSIGNED_BYTE, nullptr, isSRGB ); if( mipWidth > 1 ) mipWidth >>= 1; @@ -1099,7 +1243,7 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int if( dataArray ) scaledBuffer = (byte*) ri.Hunk_AllocateTempMemory( sizeof( byte ) * scaledWidth * scaledHeight * 4 ); - for ( i = 0; i < numLayers; i++ ) + for ( int i = 0; i < numLayers; i++ ) { if( dataArray ) data = dataArray[ i ]; @@ -1120,7 +1264,7 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int } if( image->bits & IF_NORMALMAP ) { - c = scaledWidth * scaledHeight; + int c = scaledWidth * scaledHeight; for ( int j = 0; j < c; j++ ) { vec3_t n; @@ -1197,7 +1341,7 @@ void R_UploadImage( const char *name, const byte **dataArray, int numLayers, int mipHeight = scaledHeight; mipLayers = numLayers; - for ( i = 0; i < numMips; i++ ) + for ( int i = 0; i < numMips; i++ ) { mipSize = ((mipWidth + 3) >> 2) * ((mipHeight + 3) >> 2) * blockSize; @@ -1525,7 +1669,7 @@ image_t *R_CreateGlyph( const char *name, const byte *pic, int width, int height image->texture->target = GL_TEXTURE_2D; image->width = width; image->height = height; - image->bits = IF_NOPICMIP; + image->bits = IF_NOPICMIP | IF_ALPHA; image->filterType = filterType_t::FT_LINEAR; image->wrapType = wrapTypeEnum_t::WT_CLAMP; @@ -2390,23 +2534,18 @@ R_CreateDefaultImage static void R_CreateDefaultImage() { constexpr int DEFAULT_SIZE = 128; - int x; byte data[ DEFAULT_SIZE ][ DEFAULT_SIZE ][ 4 ]; byte *dataPtr = &data[0][0][0]; // the default image will be a box, to allow you to see the mapping coordinates memset( data, 32, sizeof( data ) ); - for ( x = 0; x < DEFAULT_SIZE; x++ ) + for ( int x = 0; x < DEFAULT_SIZE; x++ ) { - data[ 0 ][ x ][ 0 ] = data[ 0 ][ x ][ 1 ] = data[ 0 ][ x ][ 2 ] = data[ 0 ][ x ][ 3 ] = 255; - data[ x ][ 0 ][ 0 ] = data[ x ][ 0 ][ 1 ] = data[ x ][ 0 ][ 2 ] = data[ x ][ 0 ][ 3 ] = 255; - - data[ DEFAULT_SIZE - 1 ][ x ][ 0 ] = - data[ DEFAULT_SIZE - 1 ][ x ][ 1 ] = data[ DEFAULT_SIZE - 1 ][ x ][ 2 ] = data[ DEFAULT_SIZE - 1 ][ x ][ 3 ] = 255; - - data[ x ][ DEFAULT_SIZE - 1 ][ 0 ] = - data[ x ][ DEFAULT_SIZE - 1 ][ 1 ] = data[ x ][ DEFAULT_SIZE - 1 ][ 2 ] = data[ x ][ DEFAULT_SIZE - 1 ][ 3 ] = 255; + Vector4Set( data[ 0 ][ x ], 255, 255, 255, 255 ); + Vector4Set( data[ x ][ 0 ], 255, 255, 255, 255 ); + Vector4Set( data[ DEFAULT_SIZE - 1 ][ x ], 255, 255, 255, 255 ); + Vector4Set( data[ x ][ DEFAULT_SIZE - 1 ], 255, 255, 255, 255 ); } imageParams_t imageParams = {}; @@ -2586,7 +2725,7 @@ static void R_CreateBlackCubeImage() } imageParams_t imageParams = {}; - imageParams.bits = IF_NOPICMIP; + imageParams.bits = IF_NOPICMIP | IF_NOALPHA; imageParams.filterType = filterType_t::FT_LINEAR; imageParams.wrapType = wrapTypeEnum_t::WT_EDGE_CLAMP; @@ -2613,7 +2752,7 @@ static void R_CreateWhiteCubeImage() } imageParams_t imageParams = {}; - imageParams.bits = IF_NOPICMIP; + imageParams.bits = IF_NOPICMIP | IF_NOALPHA; imageParams.filterType = filterType_t::FT_LINEAR; imageParams.wrapType = wrapTypeEnum_t::WT_EDGE_CLAMP; @@ -2656,7 +2795,7 @@ static void R_CreateColorGradeImage() } imageParams_t imageParams = {}; - imageParams.bits = IF_NOPICMIP; + imageParams.bits = IF_NOPICMIP | IF_NOALPHA; imageParams.filterType = filterType_t::FT_LINEAR; imageParams.wrapType = wrapTypeEnum_t::WT_EDGE_CLAMP; @@ -2681,7 +2820,7 @@ void R_CreateBuiltinImages() memset( data, 255, sizeof( data ) ); imageParams_t imageParams = {}; - imageParams.bits = IF_NOPICMIP; + imageParams.bits = IF_NOPICMIP | IF_NOALPHA; imageParams.filterType = filterType_t::FT_LINEAR; imageParams.wrapType = wrapTypeEnum_t::WT_REPEAT; @@ -2695,16 +2834,18 @@ void R_CreateBuiltinImages() // generate a default normalmap with a fully opaque heightmap (no displacement) Vector4Set( data, 128, 128, 255, 255 ); - imageParams.bits = IF_NOPICMIP | IF_NORMALMAP; + imageParams.bits = IF_NOPICMIP | IF_NOALPHA | IF_NORMALMAP; tr.flatImage = R_CreateImage( "$flat", ( const byte ** ) &dataPtr, 1, 1, 1, imageParams ); - imageParams.bits = IF_NOPICMIP; - imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP; - - // Don't reuse previously set data, we test the values for selecting the upload format. + /* Generate cinematic frames. + It is empty data to be filled by the cinematic code, but + we fill it with non-zero values so the format detector keeps all color channels. */ memset( data, 255, sizeof( data ) ); + imageParams.bits = IF_NOPICMIP | IF_NOALPHA; + imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP; + size_t numCinematicImages = 0; for ( image_t * &image : tr.cinematicImage ) { diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 9d58e99b3d..8c9abd05d4 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -509,7 +509,9 @@ enum class ssaoMode { IF_BC4 = BIT( 22 ), IF_BC5 = BIT( 23 ), IF_RGBA32UI = BIT( 24 ), - IF_HOMEPATH = BIT( 25 ) + IF_HOMEPATH = BIT( 25 ), + IF_NOALPHA = BIT( 26 ), + IF_COMPRESS = BIT(27) }; enum class filterType_t diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index bffc8d2f72..e83caf5987 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -87,6 +87,11 @@ static Cvar::Cvar r_portalDefaultRange( Cvar::Cvar r_depthShaders( "r_depthShaders", "use depth pre-pass shaders", Cvar::CHEAT, true); +Cvar::Cvar r_compressColormaps( + "r_compressColormaps", "compress uncompressed color maps", Cvar::NONE, false); +Cvar::Cvar r_compressSkyboxes( + "r_compressSkyboxes", "compress uncompressed skyboxes", Cvar::NONE, false); + struct delayedStageTexture_t { bool active; stageType_t type; @@ -1518,6 +1523,25 @@ static bool LoadMap( shaderStage_t *stage, const char *buffer, stageType_t type, } } + if ( r_compressColormaps.Get() ) + { + if ( ! ( shader.registerFlags & RSF_2D ) ) + { + switch ( type ) + { + case stageType_t::ST_COLORMAP: + case stageType_t::ST_DIFFUSEMAP: + case stageType_t::ST_GLOWMAP: + case stageType_t::ST_REFLECTIONMAP: + case stageType_t::ST_SKYBOXMAP: + imageParams.bits |= IF_COMPRESS; + break; + default: + break; + } + } + } + // determine image options if ( stage->overrideNoPicMip || shader.noPicMip || stage->highQuality || stage->forceHighQuality ) { @@ -3719,6 +3743,11 @@ static void ParseSkyParms( const char **text ) imageParams.bits |= IF_SRGB; } + if ( r_compressSkyboxes.Get() ) + { + imageParams.bits |= IF_COMPRESS; + } + shader.sky.outerbox = R_FindCubeImage( prefix, imageParams ); if ( !shader.sky.outerbox ) @@ -6410,6 +6439,12 @@ shader_t *R_FindShader( const char *name, int flags ) imageParams.bits |= IF_SRGB; } + // HACK: Detect color grade images with RSF_NOMIP. + if ( r_compressColormaps.Get() && ! ( flags & RSF_NOMIP ) ) + { + imageParams.bits |= IF_COMPRESS; + } + image = R_FindImageFile( fileName, imageParams ); } @@ -7034,6 +7069,8 @@ void R_InitShaders() Cvar::Latch(r_dpMaterial); Cvar::Latch(r_depthShaders); Cvar::Latch(r_portalDefaultRange); + Cvar::Latch(r_compressColormaps); + Cvar::Latch(r_compressSkyboxes); memset( shaderTableHashTable, 0, sizeof( shaderTableHashTable ) ); memset( shaderHashTable, 0, sizeof( shaderHashTable ) ); diff --git a/src/engine/sys/sdl_glimp.cpp b/src/engine/sys/sdl_glimp.cpp index c15add9f0e..c3c4bc9d10 100644 --- a/src/engine/sys/sdl_glimp.cpp +++ b/src/engine/sys/sdl_glimp.cpp @@ -110,6 +110,8 @@ static Cvar::Cvar r_arb_texture_barrier( "r_arb_texture_barrier", "Use GL_ARB_texture_barrier if available", Cvar::NONE, true ); static Cvar::Cvar r_arb_texture_gather( "r_arb_texture_gather", "Use GL_ARB_texture_gather if available", Cvar::NONE, true ); +static Cvar::Cvar r_arb_texture_rg( "r_arb_texture_rg", + "Use GL_ARB_texture_rg if available", Cvar::NONE, true ); static Cvar::Cvar r_arb_uniform_buffer_object( "r_arb_uniform_buffer_object", "Use GL_ARB_uniform_buffer_object if available", Cvar::NONE, true ); static Cvar::Cvar r_arb_vertex_attrib_binding( "r_arb_vertex_attrib_binding", @@ -124,8 +126,10 @@ static Cvar::Cvar r_ext_texture_float( "r_ext_texture_float", "Use GL_EXT_texture_float if available", Cvar::NONE, true ); static Cvar::Cvar r_ext_texture_integer( "r_ext_texture_integer", "Use GL_EXT_texture_integer if available", Cvar::NONE, true ); -static Cvar::Cvar r_ext_texture_rg( "r_ext_texture_rg", - "Use GL_EXT_texture_rg if available", Cvar::NONE, true ); +static Cvar::Cvar r_ext_texture_srgb_r8( "r_ext_texture_srgb_r8", + "Use GL_EXT_texture_sRGB_R8 if available", Cvar::NONE, true ); +static Cvar::Cvar r_ext_texture_srgb_rg8( "r_ext_texture_srgb_rg8", + "Use GL_EXT_texture_sRGB_RG8 if available", Cvar::NONE, true ); static Cvar::Cvar r_khr_debug( "r_khr_debug", "Use GL_KHR_debug if available", Cvar::NONE, true ); static Cvar::Cvar r_khr_shader_subgroup( "r_khr_shader_subgroup", @@ -2034,6 +2038,7 @@ static void GLimp_InitExtensions() Cvar::Latch( r_arb_sync ); Cvar::Latch( r_arb_texture_barrier ); Cvar::Latch( r_arb_texture_gather ); + Cvar::Latch( r_arb_texture_rg ); Cvar::Latch( r_arb_uniform_buffer_object ); Cvar::Latch( r_arb_vertex_attrib_binding ); Cvar::Latch( r_ext_draw_buffers ); @@ -2041,7 +2046,7 @@ static void GLimp_InitExtensions() Cvar::Latch( r_ext_texture_filter_anisotropic ); Cvar::Latch( r_ext_texture_float ); Cvar::Latch( r_ext_texture_integer ); - Cvar::Latch( r_ext_texture_rg ); + Cvar::Latch( r_ext_texture_srgb_r8 ); Cvar::Latch( r_khr_debug ); Cvar::Latch( r_khr_shader_subgroup ); @@ -2153,7 +2158,7 @@ static void GLimp_InitExtensions() && glConfig.gpuShader4Available; // made required in OpenGL 3.0 - glConfig.textureRGAvailable = LOAD_EXTENSION_WITH_TEST( ExtFlag_CORE, ARB_texture_rg, r_ext_texture_rg.Get() ); + glConfig.textureRGAvailable = LOAD_EXTENSION_WITH_TEST( ExtFlag_CORE, ARB_texture_rg, r_arb_texture_rg.Get() ); { bool textureGatherEnabled = r_arb_texture_gather.Get(); @@ -2223,6 +2228,11 @@ static void GLimp_InitExtensions() // made required in OpenGL 3.0 glConfig.textureCompressionRGTCAvailable = LOAD_EXTENSION( ExtFlag_CORE, ARB_texture_compression_rgtc ); + glConfig.textureSrgbR8Available = LOAD_EXTENSION_WITH_TEST( ExtFlag_NONE, EXT_texture_sRGB_R8, r_ext_texture_srgb_r8.Get() ); + + // Texture - others + glConfig.textureSrgbRG8Available = LOAD_EXTENSION_WITH_TEST( ExtFlag_NONE, EXT_texture_sRGB_RG8, r_ext_texture_srgb_rg8.Get() ); + // Texture - others glConfig.textureAnisotropyAvailable = false; glConfig.textureAnisotropy = 0.0f;