From 7b1aa91571f9c493bce29b7426b80015b8625978 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Wed, 4 Mar 2026 10:54:55 +0100 Subject: [PATCH 01/12] renderer: fix shader dump line numbering - reset line number at GLSL shader start - use custom line number for the GLSL header to avoid line number duplicates - use custom line number for the deform vertex header - reset line count on `#line 0` --- src/engine/renderer/gl_shader.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index f23055feb4..b7e4aeeab4 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -490,10 +490,14 @@ static void AddConst( std::string& str, const std::string& name, float v1, float static std::string GenVersionDeclaration( const std::vector &addedExtensions ) { // Declare version. - std::string str = Str::Format( "#version %d %s\n\n", + std::string str = Str::Format( "#version %d %s\n", glConfig.shadingLanguageVersion, glConfig.shadingLanguageVersion >= 150 ? ( glConfig.glCoreProfile ? "core" : "compatibility" ) : "" ); + str += "#line 1000000000\n"; + + str += "\n"; + // Add supported GLSL extensions. for ( const auto& addedExtension : addedExtensions ) { addExtension( str, addedExtension.available, addedExtension.minGlslVersion, addedExtension.name ); @@ -838,7 +842,10 @@ std::string GLShaderManager::GetDeformShaderName( const int index ) { std::string GLShaderManager::BuildDeformShaderText( const std::string& steps ) { std::string shaderText; - shaderText = steps + "\n"; + shaderText = "\n" + steps + "\n"; + + shaderText += "#line 2000000000\n"; + shaderText += GetShaderText( "deformVertexes_vp.glsl" ); return shaderText; @@ -1194,6 +1201,13 @@ std::string GLShaderManager::ProcessInserts( const std::string& shaderText ) con while ( std::getline( shaderTextStream, line, '\n' ) ) { ++lineCount; + + /* The deform vertex header is prepended to the mainText and is part + of the shaderText, so we should reset line numbering after it. */ + if ( line == "#line 0" ) { + lineCount = 0; + } + const std::string::size_type position = line.find( "#insert" ); if ( position == std::string::npos || line.find_first_not_of( " \t" ) != position ) { out += line + "\n"; @@ -1324,7 +1338,9 @@ void GLShaderManager::InitShader( GLShader* shader ) { if ( shaderType.enabled ) { Com_sprintf( filename, sizeof( filename ), "%s%s.glsl", shaderType.path.c_str(), shaderType.postfix ); - shaderType.mainText = GetShaderText( filename ); + /* The deform vertex header is prepended to the mainText, + so we should reset line numbering after it. */ + shaderType.mainText = "#line 0\n" + GetShaderText( filename ); } } From 074c352f9c76107d5e31cd29ac538504884de2c7 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Wed, 12 Feb 2025 22:45:54 +0300 Subject: [PATCH 02/12] Implement adaptive exposure Add a compute shader that will compute the geometric mean of scene luminance, then map it to an exposure curve in the `cameraEffects` shader. This is controlled by `r_tonemapAdaptiveExposure`. --- src.cmake | 2 + src/engine/renderer/BufferBind.h | 4 + src/engine/renderer/GLUtils.h | 1 + src/engine/renderer/gl_shader.cpp | 50 +++++++++-- src/engine/renderer/gl_shader.h | 52 ++++++++++- .../glsl_source/cameraEffects_fp.glsl | 34 ++++++-- .../glsl_source/clearFrameData_cp.glsl | 53 ++++++++++++ .../renderer/glsl_source/common_cp.glsl | 5 ++ .../glsl_source/luminanceReduction_cp.glsl | 86 +++++++++++++++++++ src/engine/renderer/tr_backend.cpp | 44 ++++++++++ src/engine/renderer/tr_init.cpp | 2 + src/engine/renderer/tr_local.h | 1 + src/engine/renderer/tr_shade.cpp | 7 ++ src/engine/renderer/tr_vbo.cpp | 9 ++ src/engine/sys/sdl_glimp.cpp | 4 + 15 files changed, 338 insertions(+), 16 deletions(-) create mode 100644 src/engine/renderer/glsl_source/clearFrameData_cp.glsl create mode 100644 src/engine/renderer/glsl_source/luminanceReduction_cp.glsl diff --git a/src.cmake b/src.cmake index bd5e8b47e0..d49d88b8f8 100644 --- a/src.cmake +++ b/src.cmake @@ -121,6 +121,8 @@ set(GLSL_EMBED_LIST lighttile_fp.glsl computeLight_fp.glsl reliefMapping_fp.glsl + luminanceReduction_cp.glsl + clearFrameData_cp.glsl # Common vertex shader libraries deformVertexes_vp.glsl diff --git a/src/engine/renderer/BufferBind.h b/src/engine/renderer/BufferBind.h index d6972709bf..85788ceb37 100644 --- a/src/engine/renderer/BufferBind.h +++ b/src/engine/renderer/BufferBind.h @@ -61,6 +61,10 @@ namespace BufferBind { TEX_DATA_STORAGE = 11, STAGING = 12, + // Adaptative exposure + LUMINANCE = 3, + LUMINANCE_STORAGE = 8, + DEBUG = 10, // Atomic diff --git a/src/engine/renderer/GLUtils.h b/src/engine/renderer/GLUtils.h index e7a3a635ed..f7e4c02042 100644 --- a/src/engine/renderer/GLUtils.h +++ b/src/engine/renderer/GLUtils.h @@ -128,6 +128,7 @@ struct GLConfig bool gpuShader4Available; bool gpuShader5Available; bool textureGatherAvailable; + bool adaptiveExposureAvailable; int maxDrawBuffers; float maxTextureAnisotropy; diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index b7e4aeeab4..34d54f08b6 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -60,9 +60,11 @@ GLShader_fxaa *gl_fxaaShader = nullptr; GLShader_motionblur *gl_motionblurShader = nullptr; GLShader_ssao *gl_ssaoShader = nullptr; -GLShader_depthtile1 *gl_depthtile1Shader = nullptr; -GLShader_depthtile2 *gl_depthtile2Shader = nullptr; -GLShader_lighttile *gl_lighttileShader = nullptr; +GLShader_depthtile1* gl_depthtile1Shader = nullptr; +GLShader_depthtile2* gl_depthtile2Shader = nullptr; +GLShader_lighttile* gl_lighttileShader = nullptr; +GLShader_luminanceReduction* gl_luminanceReductionShader = nullptr; +GLShader_clearFrameData* gl_clearFrameDataShader = nullptr; GLShader_generic *gl_genericShader = nullptr; GLShader_genericMaterial *gl_genericShaderMaterial = nullptr; @@ -81,6 +83,7 @@ GLShader_skybox *gl_skyboxShader = nullptr; GLShader_skyboxMaterial *gl_skyboxShaderMaterial = nullptr; GlobalUBOProxy *globalUBOProxy = nullptr; GLShaderManager gl_shaderManager; +GLBuffer luminanceBuffer( "luminance", Util::ordinal( BufferBind::LUMINANCE ), GL_MAP_WRITE_BIT, GL_MAP_INVALIDATE_RANGE_BIT ); namespace // Implementation details { @@ -419,6 +422,9 @@ static const std::vector fragmentVertexAddedExtensions = { where the core variables have different names. */ { glConfig.shaderDrawParametersAvailable, -1, "ARB_shader_draw_parameters" }, { glConfig.SSBOAvailable, 430, "ARB_shader_storage_buffer_object" }, + { glConfig.shadingLanguage420PackAvailable, 420, "ARB_shading_language_420pack" }, + { glConfig.explicitUniformLocationAvailable, 430, "ARB_explicit_uniform_location" }, + { glConfig.shaderAtomicCountersAvailable, 420, "ARB_shader_atomic_counters" }, /* Even though these are part of the GL_KHR_shader_subgroup extension, we need to enable the individual extensions for each feature. GL_KHR_shader_subgroup itself can't be used in the shader. */ @@ -584,6 +590,10 @@ static std::string GenVertexHeader() { AddDefine( str, "BIND_LIGHTMAP_DATA", BufferBind::LIGHTMAP_DATA ); } + if ( glConfig.adaptiveExposureAvailable ) { + AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) ); + } + return str; } @@ -633,6 +643,10 @@ static std::string GenFragmentHeader() { AddDefine( str, "USE_PUSH_BUFFER", 1 ); } + if ( glConfig.adaptiveExposureAvailable ) { + AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) ); + } + return str; } @@ -658,6 +672,11 @@ static std::string GenComputeHeader() { AddDefine( str, "BIND_DEBUG", BufferBind::DEBUG ); } + if ( glConfig.adaptiveExposureAvailable ) { + AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) ); + AddDefine( str, "BIND_LUMINANCE_STORAGE", Util::ordinal( BufferBind::LUMINANCE_STORAGE ) ); + } + if ( glConfig.pushBufferAvailable ) { AddDefine( str, "USE_PUSH_BUFFER", 1 ); } @@ -2623,6 +2642,23 @@ GLShader_lightMappingMaterial::GLShader_lightMappingMaterial() : GLCompileMacro_USE_PHYSICAL_MAPPING( this ) { } +GLShader_luminanceReduction::GLShader_luminanceReduction( GLShaderManager* manager ) : + GLShader( "luminanceReduction", + false, "luminanceReduction" ), + u_ViewWidth( this ), + u_ViewHeight( this ), + u_TonemapParms2( this ) { +} + +void GLShader_luminanceReduction::SetShaderProgramUniforms( ShaderProgramDescriptor* shaderProgram ) { + glUniform1i( glGetUniformLocation( shaderProgram->id, "initialRenderImage" ), 0 ); +} + +GLShader_clearFrameData::GLShader_clearFrameData( GLShaderManager* manager ) : + GLShader( "clearFrameData", + false, "clearFrameData" ) { +} + GLShader_reflection::GLShader_reflection(): GLShader( "reflection", ATTR_POSITION | ATTR_TEXCOORD | ATTR_QTANGENT, false, "reflection_CB", "reflection_CB" ), @@ -2818,11 +2854,13 @@ GLShader_cameraEffects::GLShader_cameraEffects() : u_GlobalLightFactor( this ), u_ColorModulate( this ), u_SRGB( this ), + u_ViewWidth( this ), + u_ViewHeight( this ), u_Tonemap( this ), u_TonemapParms( this ), + u_TonemapAdaptiveExposure( this ), u_Exposure( this ), - u_InverseGamma( this ) -{ + u_InverseGamma( this ) { } void GLShader_cameraEffects::SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) @@ -3082,4 +3120,4 @@ GlobalUBOProxy::GlobalUBOProxy() : u_Tonemap( this ), u_TonemapParms( this ), u_Exposure( this ) { -} \ No newline at end of file +} diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 781438e1b5..69b169c096 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -2804,6 +2804,18 @@ class u_Tonemap : } }; +class u_TonemapAdaptiveExposure : + GLUniform1Bool { + public: + u_TonemapAdaptiveExposure( GLShader* shader ) : + GLUniform1Bool( shader, "u_TonemapAdaptiveExposure", FRAME ) { + } + + void SetUniform_TonemapAdaptiveExposure( bool tonemapAdaptiveExposure ) { + this->SetValue( tonemapAdaptiveExposure ); + } +}; + class u_TonemapParms : GLUniform4f { public: @@ -2816,6 +2828,18 @@ class u_TonemapParms : } }; +class u_TonemapParms2 : + GLUniform4f { + public: + u_TonemapParms2( GLShader* shader ) : + GLUniform4f( shader, "u_TonemapParms2", FRAME ) { + } + + void SetUniform_TonemapParms2( vec4_t tonemapParms2 ) { + this->SetValue( tonemapParms2 ); + } +}; + class u_Exposure : GLUniform1f { public: @@ -3084,6 +3108,22 @@ class GLShader_lightMappingMaterial : GLShader_lightMappingMaterial(); }; +class GLShader_luminanceReduction : + public GLShader, + public u_ViewWidth, + public u_ViewHeight, + public u_TonemapParms2 { + public: + GLShader_luminanceReduction( GLShaderManager* manager ); + void SetShaderProgramUniforms( ShaderProgramDescriptor* shaderProgram ) override; +}; + +class GLShader_clearFrameData : + public GLShader { + public: + GLShader_clearFrameData( GLShaderManager* manager ); +}; + class GLShader_reflection : public GLShader, public u_ColorMapCube, @@ -3257,8 +3297,11 @@ class GLShader_cameraEffects : public u_GlobalLightFactor, public u_ColorModulate, public u_SRGB, + public u_ViewWidth, + public u_ViewHeight, public u_Tonemap, public u_TonemapParms, + public u_TonemapAdaptiveExposure, public u_Exposure, public u_InverseGamma { @@ -3513,9 +3556,11 @@ extern GLShader_fxaa *gl_fxaaShader; extern GLShader_motionblur *gl_motionblurShader; extern GLShader_ssao *gl_ssaoShader; -extern GLShader_depthtile1 *gl_depthtile1Shader; -extern GLShader_depthtile2 *gl_depthtile2Shader; -extern GLShader_lighttile *gl_lighttileShader; +extern GLShader_depthtile1* gl_depthtile1Shader; +extern GLShader_depthtile2* gl_depthtile2Shader; +extern GLShader_lighttile* gl_lighttileShader; +extern GLShader_luminanceReduction* gl_luminanceReductionShader; +extern GLShader_clearFrameData* gl_clearFrameDataShader; extern GLShader_generic *gl_genericShader; extern GLShader_genericMaterial *gl_genericShaderMaterial; @@ -3534,5 +3579,6 @@ extern GLShader_skybox *gl_skyboxShader; extern GLShader_skyboxMaterial *gl_skyboxShaderMaterial; extern GlobalUBOProxy *globalUBOProxy; extern GLShaderManager gl_shaderManager; +extern GLBuffer luminanceBuffer; #endif // GL_SHADER_H diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index 8b243fdb44..a7aff054dd 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -27,12 +27,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA uniform sampler2D u_CurrentMap; #if defined(r_colorGrading) -uniform sampler3D u_ColorMap3D; + uniform sampler3D u_ColorMap3D; #endif -uniform vec4 u_ColorModulate; -uniform float u_GlobalLightFactor; // 1 / tr.identityLight -uniform float u_InverseGamma; +uniform vec4 u_ColorModulate; +uniform float u_GlobalLightFactor; // 1 / tr.identityLight +uniform float u_InverseGamma; uniform bool u_SRGB; void convertToSRGB(inout vec3 color) { @@ -58,11 +58,15 @@ uniform float u_Exposure; // Tone mapping is not available when high-precision float framebuffer isn't enabled or supported. #if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float) +uniform uint u_ViewWidth; +uniform uint u_ViewHeight; + +uniform bool u_Tonemap; +uniform bool u_TonemapAdaptiveExposure; /* x: contrast y: highlightsCompressionSpeed z: shoulderClip w: highlightsCompression */ -uniform bool u_Tonemap; uniform vec4 u_TonemapParms; vec3 TonemapLottes( vec3 color ) { @@ -70,12 +74,21 @@ vec3 TonemapLottes( vec3 color ) { return pow( color, vec3( u_TonemapParms[0] ) ) / ( pow( color, vec3( u_TonemapParms[0] * u_TonemapParms[1] ) ) * u_TonemapParms[2] + u_TonemapParms[3] ); } + +#if defined(HAVE_ARB_explicit_uniform_location) && defined(HAVE_ARB_shader_atomic_counters) +layout(std140, binding = BIND_LUMINANCE) uniform ub_LuminanceUBO { + uint luminanceU; +}; +#endif + +float GetAverageLuminance( const in uint luminance ) { + return float( luminanceU ) / ( 256.0f * u_ViewWidth * u_ViewHeight ); +} #endif DECLARE_OUTPUT(vec4) -void main() -{ +void main() { #insert material_fp // calculate the screen texcoord in the 0.0 to 1.0 range @@ -88,6 +101,13 @@ void main() #if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float) if( u_Tonemap ) { + #if defined(HAVE_ARB_explicit_uniform_location) && defined(HAVE_ARB_shader_atomic_counters) + if ( u_TonemapAdaptiveExposure ) { + const float l = GetAverageLuminance( luminanceU ) - 8; + color.rgb *= clamp( 0.18f / exp2( l * 0.8f + 0.1f ), 0.0f, 2.0f ); + } + #endif + color.rgb = TonemapLottes( color.rgb ); } #endif diff --git a/src/engine/renderer/glsl_source/clearFrameData_cp.glsl b/src/engine/renderer/glsl_source/clearFrameData_cp.glsl new file mode 100644 index 0000000000..1a210e1bde --- /dev/null +++ b/src/engine/renderer/glsl_source/clearFrameData_cp.glsl @@ -0,0 +1,53 @@ +/* +=========================================================================== + +Daemon BSD Source Code +Copyright (c) 2025 Daemon Developers +All rights reserved. + +This file is part of the Daemon BSD Source Code (Daemon Source Code). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=========================================================================== +*/ + +/* clearFrameData_cp.glsl */ + +#insert common_cp + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(std430, binding = BIND_LUMINANCE_STORAGE) writeonly buffer luminanceBuffer { + uint luminance; +}; + +uniform uint u_Frame; + +void main() { + const uint globalInvocationID = GLOBAL_INVOCATION_ID; + if( globalInvocationID >= 1 ) { + return; + } + luminance = 0; +} diff --git a/src/engine/renderer/glsl_source/common_cp.glsl b/src/engine/renderer/glsl_source/common_cp.glsl index 9634075238..87675d1770 100644 --- a/src/engine/renderer/glsl_source/common_cp.glsl +++ b/src/engine/renderer/glsl_source/common_cp.glsl @@ -56,6 +56,11 @@ array must be in the form of uvec4 array[] */ /* Macro combinations for subgroup ops */ +#if defined(HAVE_KHR_shader_subgroup_basic) && defined(HAVE_KHR_shader_subgroup_arithmetic)\ + && defined(HAVE_ARB_shader_atomic_counter_ops) + #define SUBGROUP_ATOMIC +#endif + #if defined(HAVE_KHR_shader_subgroup_basic) && defined(HAVE_KHR_shader_subgroup_arithmetic)\ && defined(HAVE_KHR_shader_subgroup_ballot) && defined(HAVE_ARB_shader_atomic_counter_ops) #define SUBGROUP_STREAM_COMPACTION diff --git a/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl b/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl new file mode 100644 index 0000000000..d25a7057a5 --- /dev/null +++ b/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl @@ -0,0 +1,86 @@ +/* +=========================================================================== + +Daemon BSD Source Code +Copyright (c) 2024-2025 Daemon Developers +All rights reserved. + +This file is part of the Daemon BSD Source Code (Daemon Source Code). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=========================================================================== +*/ + +/* luminanceReduction_cp.glsl */ + +#insert common_cp + +// Keep this to 8x8 because we don't want extra shared mem etc. to be allocated, and to minimize wasted lanes +layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(binding = 0) uniform sampler2D renderImage; + +uniform uint u_ViewWidth; +uniform uint u_ViewHeight; + +/* x: log2( HDRMax ) +y: reserved +z: reserved +w: reserved */ +uniform vec4 u_TonemapParms2; + +layout (binding = BIND_LUMINANCE) uniform atomic_uint atomicLuminance; + +float ColorToLuminance( const in vec3 color ) { + float luminance = dot( color.rgb, vec3( 0.2126f, 0.7152f, 0.0722f ) ); // sRGB luminance + + /* This currently allows values up to 136 in range, which means it can go up to ~3e9, + so we can get the average with a few simple ops in cameraEffects + If we go over that range, we'd either need to do another shader pass or use shared memory/subgroup intrinsics + to get partial averages instead */ + return luminance > 0.0f ? clamp( log2( luminance ) + 8, 0.0, u_TonemapParms2.x + 8 ) : 0.0f; +} + +uint FloatLuminanceToUint( const in float luminance ) { + return uint( luminance * 256 ); +} + +void main() { + const ivec2 position = ivec2( gl_GlobalInvocationID.xy ); + if( position.x >= u_ViewWidth || position.y >= u_ViewHeight ) { + return; + }; + + const float luminance = ColorToLuminance( texelFetch( renderImage, position, 0 ).rgb ); + + #if defined(HAVE_subgroup_atomic) + const float luminanceSum = subgroupInclusiveAdd( luminance ); + + if( subgroupElect() ) { + atomicCounterAddARB( atomicLuminance, FloatLuminanceToUint( luminanceSum ) ); + } + #else + atomicCounterAddARB( atomicLuminance, FloatLuminanceToUint( luminance ) ); + #endif +} diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index d8604cc632..4abd8d1e42 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1633,6 +1633,36 @@ void RB_FXAA() GL_CheckErrors(); } +static void AdaptiveLightingReduction() { + luminanceBuffer.BindBufferBase( GL_SHADER_STORAGE_BUFFER, Util::ordinal( BufferBind::LUMINANCE_STORAGE ) ); + + gl_clearFrameDataShader->BindProgram(); + gl_clearFrameDataShader->DispatchCompute( 1, 1, 1 ); + + luminanceBuffer.BindBufferBase( GL_ATOMIC_COUNTER_BUFFER, Util::ordinal( BufferBind::LUMINANCE ) ); + + gl_luminanceReductionShader->BindProgram(); + + const int width = tr.currentRenderImage[backEnd.currentMainFBO]->width; + const int height = tr.currentRenderImage[backEnd.currentMainFBO]->height; + + uint32_t globalWorkgroupX = ( width + 7 ) / 8; + uint32_t globalWorkgroupY = ( height + 7 ) / 8; + + GL_Bind( tr.currentRenderImage[backEnd.currentMainFBO] ); + + gl_luminanceReductionShader->SetUniform_ViewWidth( width ); + gl_luminanceReductionShader->SetUniform_ViewHeight( height ); + vec4_t parms { log2f( r_toneMappingHDRMax.Get() ) }; + gl_luminanceReductionShader->SetUniform_TonemapParms2( parms ); + + glMemoryBarrier( GL_ATOMIC_COUNTER_BARRIER_BIT ); + + gl_luminanceReductionShader->DispatchCompute( globalWorkgroupX, globalWorkgroupY, 1 ); + + glMemoryBarrier( GL_UNIFORM_BARRIER_BIT ); +} + static void ComputeTonemapParams( const float contrast, const float highlightsCompressionSpeed, const float HDRMax, const float darkAreaPointHDR, const float darkAreaPointLDR, @@ -1686,10 +1716,16 @@ void RB_CameraPostFX() { const bool tonemap = r_toneMapping.Get() && r_highPrecisionRendering.Get() && glConfig.textureFloatAvailable; if ( tonemap ) { + gl_cameraEffectsShader->SetUniform_ViewWidth( tr.currentRenderImage[backEnd.currentMainFBO]->width ); + gl_cameraEffectsShader->SetUniform_ViewHeight( tr.currentRenderImage[backEnd.currentMainFBO]->height ); + vec4_t tonemapParms { r_toneMappingContrast.Get(), r_toneMappingHighlightsCompressionSpeed.Get() }; ComputeTonemapParams( tonemapParms[0], tonemapParms[1], r_toneMappingHDRMax.Get(), r_toneMappingDarkAreaPointHDR.Get(), r_toneMappingDarkAreaPointLDR.Get(), tonemapParms[2], tonemapParms[3] ); gl_cameraEffectsShader->SetUniform_TonemapParms( tonemapParms ); + + gl_cameraEffectsShader->SetUniform_TonemapAdaptiveExposure( + glConfig.adaptiveExposureAvailable && r_toneMappingAdaptiveExposure.Get() ); } gl_cameraEffectsShader->SetUniform_Tonemap( tonemap ); @@ -1704,6 +1740,10 @@ void RB_CameraPostFX() { gl_cameraEffectsShader->SetUniform_ColorMap3DBindless( GL_BindToTMU( 3, tr.colorGradeImage ) ); } + if ( glConfig.adaptiveExposureAvailable && r_toneMappingAdaptiveExposure.Get() ) { + luminanceBuffer.BindBufferBase( GL_UNIFORM_BUFFER ); + } + Tess_InstantScreenSpaceQuad(); GL_CheckErrors(); @@ -2808,6 +2848,10 @@ static void RB_RenderPostProcess() materialSystem.EndFrame(); } + if ( glConfig.adaptiveExposureAvailable && r_toneMappingAdaptiveExposure.Get() ) { + AdaptiveLightingReduction(); + } + TransitionMSAAToMain( GL_COLOR_BUFFER_BIT ); RB_FXAA(); diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 2885749bcd..4de50fc81f 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -187,6 +187,8 @@ Cvar::Cvar r_rendererAPI( "r_rendererAPI", "Renderer API: 0: OpenGL, 1: Vul Cvar::Cvar r_toneMapping( "r_toneMapping", "Use HDR->LDR tonemapping", Cvar::NONE, true ); + Cvar::Cvar r_toneMappingAdaptiveExposure( "r_toneMappingAdaptiveExposure", + "Use adaptive exposure based on scene brightness", Cvar::NONE, false ); Cvar::Cvar r_exposure( "r_exposure", "Exposure (brightness adjustment)", Cvar::NONE, 1.0f ); Cvar::Range> r_toneMappingContrast( diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index c5570f8704..bcc6d94eb9 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -2670,6 +2670,7 @@ enum extern cvar_t *r_gamma; extern Cvar::Cvar r_toneMapping; + extern Cvar::Cvar r_toneMappingAdaptiveExposure; extern Cvar::Cvar r_exposure; extern Cvar::Range> r_toneMappingContrast; extern Cvar::Range> r_toneMappingHighlightsCompressionSpeed; diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index b2c95800e5..ae275f7bc3 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -272,6 +272,11 @@ static void GLSL_InitGPUShadersOrError() gl_lighttileShader->MarkProgramForBuilding(); } + if ( glConfig.adaptiveExposureAvailable ) { + gl_shaderManager.LoadShader( gl_luminanceReductionShader ); + gl_shaderManager.LoadShader( gl_clearFrameDataShader ); + } + if ( glConfig.reflectionMappingAvailable ) { // bumped cubemap reflection for abitrary polygons ( EMBM ) @@ -468,12 +473,14 @@ void GLSL_ShutdownGPUShaders() gl_genericShader = nullptr; gl_genericShaderMaterial = nullptr; globalUBOProxy = nullptr; + gl_clearFrameDataShader = nullptr; gl_cullShader = nullptr; gl_depthReductionShader = nullptr; gl_clearSurfacesShader = nullptr; gl_processSurfacesShader = nullptr; gl_lightMappingShader = nullptr; gl_lightMappingShaderMaterial = nullptr; + gl_luminanceReductionShader = nullptr; gl_reflectionShader = nullptr; gl_reflectionShaderMaterial = nullptr; gl_skyboxShader = nullptr; diff --git a/src/engine/renderer/tr_vbo.cpp b/src/engine/renderer/tr_vbo.cpp index 6db30d4003..3ae6c77d06 100644 --- a/src/engine/renderer/tr_vbo.cpp +++ b/src/engine/renderer/tr_vbo.cpp @@ -756,6 +756,11 @@ void R_InitVBOs() geometryCache.InitGLBuffers(); } + if ( glConfig.adaptiveExposureAvailable ) { + luminanceBuffer.GenBuffer(); + luminanceBuffer.BufferData( 1, nullptr, GL_DYNAMIC_COPY ); + } + if ( glConfig.directStateAccessAvailable && glConfig.uniformBufferObjectAvailable ) { stagingBuffer.InitGLBuffer(); } @@ -837,6 +842,10 @@ void R_ShutdownVBOs() geometryCache.FreeGLBuffers(); } + if ( glConfig.adaptiveExposureAvailable ) { + luminanceBuffer.DelBuffer(); + } + if ( glConfig.directStateAccessAvailable && glConfig.uniformBufferObjectAvailable ) { stagingBuffer.FreeGLBuffer(); } diff --git a/src/engine/sys/sdl_glimp.cpp b/src/engine/sys/sdl_glimp.cpp index 28adcf324b..135da45a31 100644 --- a/src/engine/sys/sdl_glimp.cpp +++ b/src/engine/sys/sdl_glimp.cpp @@ -2594,6 +2594,10 @@ static void GLimp_InitExtensions() && glConfig.SSBOAvailable && glConfig.uniformBufferObjectAvailable; + glConfig.adaptiveExposureAvailable = glConfig.computeShaderAvailable && glConfig.shaderImageLoadStoreAvailable + && glConfig.shaderAtomicCountersAvailable && glConfig.shadingLanguage420PackAvailable + && glConfig.explicitUniformLocationAvailable && glConfig.directStateAccessAvailable; + // This requires GLEW 2.2+, so skip if it's a lower version #if defined(GLEW_KHR_shader_subgroup) // not required by any OpenGL version From 9ea76f32247e273799419795c4fd10717cb9da87 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Sun, 16 Mar 2025 22:38:23 +0300 Subject: [PATCH 03/12] Fix luminance overflow --- src/engine/renderer/gl_shader.cpp | 5 +++-- src/engine/renderer/gl_shader.h | 5 +++-- src/engine/renderer/glsl_source/cameraEffects_fp.glsl | 3 ++- .../renderer/glsl_source/luminanceReduction_cp.glsl | 2 +- src/engine/renderer/tr_backend.cpp | 9 +++++++-- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 34d54f08b6..081aefc9da 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -2642,7 +2642,7 @@ GLShader_lightMappingMaterial::GLShader_lightMappingMaterial() : GLCompileMacro_USE_PHYSICAL_MAPPING( this ) { } -GLShader_luminanceReduction::GLShader_luminanceReduction( GLShaderManager* manager ) : +GLShader_luminanceReduction::GLShader_luminanceReduction() : GLShader( "luminanceReduction", false, "luminanceReduction" ), u_ViewWidth( this ), @@ -2654,7 +2654,7 @@ void GLShader_luminanceReduction::SetShaderProgramUniforms( ShaderProgramDescrip glUniform1i( glGetUniformLocation( shaderProgram->id, "initialRenderImage" ), 0 ); } -GLShader_clearFrameData::GLShader_clearFrameData( GLShaderManager* manager ) : +GLShader_clearFrameData::GLShader_clearFrameData() : GLShader( "clearFrameData", false, "clearFrameData" ) { } @@ -2859,6 +2859,7 @@ GLShader_cameraEffects::GLShader_cameraEffects() : u_Tonemap( this ), u_TonemapParms( this ), u_TonemapAdaptiveExposure( this ), + u_TonemapParms2( this ), u_Exposure( this ), u_InverseGamma( this ) { } diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 69b169c096..fc22f8828a 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -3114,14 +3114,14 @@ class GLShader_luminanceReduction : public u_ViewHeight, public u_TonemapParms2 { public: - GLShader_luminanceReduction( GLShaderManager* manager ); + GLShader_luminanceReduction(); void SetShaderProgramUniforms( ShaderProgramDescriptor* shaderProgram ) override; }; class GLShader_clearFrameData : public GLShader { public: - GLShader_clearFrameData( GLShaderManager* manager ); + GLShader_clearFrameData(); }; class GLShader_reflection : @@ -3302,6 +3302,7 @@ class GLShader_cameraEffects : public u_Tonemap, public u_TonemapParms, public u_TonemapAdaptiveExposure, + public u_TonemapParms2, public u_Exposure, public u_InverseGamma { diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index a7aff054dd..dcd8bdf0f9 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -68,6 +68,7 @@ y: highlightsCompressionSpeed z: shoulderClip w: highlightsCompression */ uniform vec4 u_TonemapParms; +uniform vec4 u_TonemapParms2; vec3 TonemapLottes( vec3 color ) { // Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines" @@ -82,7 +83,7 @@ layout(std140, binding = BIND_LUMINANCE) uniform ub_LuminanceUBO { #endif float GetAverageLuminance( const in uint luminance ) { - return float( luminanceU ) / ( 256.0f * u_ViewWidth * u_ViewHeight ); + return float( luminanceU ) / ( u_TonemapParms2[1] * u_ViewWidth * u_ViewHeight ); } #endif diff --git a/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl b/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl index d25a7057a5..6884e6180c 100644 --- a/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl +++ b/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl @@ -63,7 +63,7 @@ float ColorToLuminance( const in vec3 color ) { } uint FloatLuminanceToUint( const in float luminance ) { - return uint( luminance * 256 ); + return uint( luminance * u_TonemapParms2[1] ); } void main() { diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 4abd8d1e42..156b480bc7 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1643,8 +1643,8 @@ static void AdaptiveLightingReduction() { gl_luminanceReductionShader->BindProgram(); - const int width = tr.currentRenderImage[backEnd.currentMainFBO]->width; - const int height = tr.currentRenderImage[backEnd.currentMainFBO]->height; + const int width = windowConfig.vidWidth; + const int height = windowConfig.vidHeight; uint32_t globalWorkgroupX = ( width + 7 ) / 8; uint32_t globalWorkgroupY = ( height + 7 ) / 8; @@ -1654,6 +1654,7 @@ static void AdaptiveLightingReduction() { gl_luminanceReductionShader->SetUniform_ViewWidth( width ); gl_luminanceReductionShader->SetUniform_ViewHeight( height ); vec4_t parms { log2f( r_toneMappingHDRMax.Get() ) }; + parms[1] = UINT32_MAX / ( width * height * ( uint32_t( parms[0] ) + 8 ) ); gl_luminanceReductionShader->SetUniform_TonemapParms2( parms ); glMemoryBarrier( GL_ATOMIC_COUNTER_BARRIER_BIT ); @@ -1724,6 +1725,10 @@ void RB_CameraPostFX() { r_toneMappingDarkAreaPointHDR.Get(), r_toneMappingDarkAreaPointLDR.Get(), tonemapParms[2], tonemapParms[3] ); gl_cameraEffectsShader->SetUniform_TonemapParms( tonemapParms ); + vec4_t parms{ log2f( r_toneMappingHDRMax.Get() ) }; + parms[1] = UINT32_MAX / ( windowConfig.vidWidth * windowConfig.vidHeight * ( uint32_t( parms[0] ) + 8 ) ); + gl_cameraEffectsShader->SetUniform_TonemapParms2( parms ); + gl_cameraEffectsShader->SetUniform_TonemapAdaptiveExposure( glConfig.adaptiveExposureAvailable && r_toneMappingAdaptiveExposure.Get() ); } From 592ef10e87d80c969c5ce6fe90e78d4b85b1884f Mon Sep 17 00:00:00 2001 From: VReaperV Date: Sat, 22 Mar 2025 10:25:03 +0300 Subject: [PATCH 04/12] Shader ifdef checks for glConfig2.adaptiveExposureAvailable --- src/engine/renderer/gl_shader.cpp | 1 + src/engine/renderer/glsl_source/cameraEffects_fp.glsl | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 081aefc9da..39ba82a52c 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -645,6 +645,7 @@ static std::string GenFragmentHeader() { if ( glConfig.adaptiveExposureAvailable ) { AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) ); + AddDefine( str, "ADAPTIVE_EXPOSURE_AVAILABLE", 1 ); } return str; diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index dcd8bdf0f9..ecb603f6c7 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -76,7 +76,7 @@ vec3 TonemapLottes( vec3 color ) { / ( pow( color, vec3( u_TonemapParms[0] * u_TonemapParms[1] ) ) * u_TonemapParms[2] + u_TonemapParms[3] ); } -#if defined(HAVE_ARB_explicit_uniform_location) && defined(HAVE_ARB_shader_atomic_counters) +#if defined(ADAPTIVE_EXPOSURE_AVAILABLE) layout(std140, binding = BIND_LUMINANCE) uniform ub_LuminanceUBO { uint luminanceU; }; @@ -102,7 +102,7 @@ void main() { #if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float) if( u_Tonemap ) { - #if defined(HAVE_ARB_explicit_uniform_location) && defined(HAVE_ARB_shader_atomic_counters) + #if defined(ADAPTIVE_EXPOSURE_AVAILABLE) if ( u_TonemapAdaptiveExposure ) { const float l = GetAverageLuminance( luminanceU ) - 8; color.rgb *= clamp( 0.18f / exp2( l * 0.8f + 0.1f ), 0.0f, 2.0f ); From ad47299ae4831794e895c9c7a9f2f875a8c2c9e4 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Sun, 30 Mar 2025 23:05:58 +0300 Subject: [PATCH 05/12] Move GetAverageLuminance() to the correct ifdef --- src/engine/renderer/glsl_source/cameraEffects_fp.glsl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index ecb603f6c7..62d2ddfd07 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -80,13 +80,14 @@ vec3 TonemapLottes( vec3 color ) { layout(std140, binding = BIND_LUMINANCE) uniform ub_LuminanceUBO { uint luminanceU; }; -#endif float GetAverageLuminance( const in uint luminance ) { - return float( luminanceU ) / ( u_TonemapParms2[1] * u_ViewWidth * u_ViewHeight ); + return float( luminanceU ) / ( u_TonemapParms2[1] * u_ViewWidth * u_ViewHeight ); } #endif +#endif + DECLARE_OUTPUT(vec4) void main() { From c7881da67de7abbeb44f5ae86569ca385d84fece Mon Sep 17 00:00:00 2001 From: VReaperV Date: Sun, 30 Mar 2025 23:07:06 +0300 Subject: [PATCH 06/12] Fix incorrect ifdef for subgroup atomics --- src/engine/renderer/glsl_source/luminanceReduction_cp.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl b/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl index 6884e6180c..9f90bce09c 100644 --- a/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl +++ b/src/engine/renderer/glsl_source/luminanceReduction_cp.glsl @@ -74,7 +74,7 @@ void main() { const float luminance = ColorToLuminance( texelFetch( renderImage, position, 0 ).rgb ); - #if defined(HAVE_subgroup_atomic) + #if defined(SUBGROUP_ATOMIC) const float luminanceSum = subgroupInclusiveAdd( luminance ); if( subgroupElect() ) { From dc2033575a795f531b87f5aaa18c2d63b3b38b42 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Wed, 4 Mar 2026 12:06:56 +0100 Subject: [PATCH 07/12] Fix deleting the unused u_Frame in clearFrame --- src/engine/renderer/glsl_source/clearFrameData_cp.glsl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/engine/renderer/glsl_source/clearFrameData_cp.glsl b/src/engine/renderer/glsl_source/clearFrameData_cp.glsl index 1a210e1bde..873f6d31ea 100644 --- a/src/engine/renderer/glsl_source/clearFrameData_cp.glsl +++ b/src/engine/renderer/glsl_source/clearFrameData_cp.glsl @@ -42,8 +42,6 @@ layout(std430, binding = BIND_LUMINANCE_STORAGE) writeonly buffer luminanceBuffe uint luminance; }; -uniform uint u_Frame; - void main() { const uint globalInvocationID = GLOBAL_INVOCATION_ID; if( globalInvocationID >= 1 ) { From 377a7d30c2515b4494ebaf74c6a84bf94ee4acfd Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 26 Feb 2026 10:52:47 +0100 Subject: [PATCH 08/12] Fix UBOProxy --- src/engine/renderer/gl_shader.cpp | 2 ++ src/engine/renderer/gl_shader.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 39ba82a52c..0af67d5143 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -3121,5 +3121,7 @@ GlobalUBOProxy::GlobalUBOProxy() : u_InverseGamma( this ), u_Tonemap( this ), u_TonemapParms( this ), + u_TonemapAdaptiveExposure( this ), + u_TonemapParms2( this ), u_Exposure( this ) { } diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index fc22f8828a..3cb10d0fb3 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -3533,6 +3533,8 @@ class GlobalUBOProxy : public u_InverseGamma, public u_Tonemap, public u_TonemapParms, + public u_TonemapAdaptiveExposure, + public u_TonemapParms2, public u_Exposure { public: From 9726bfe2283e4517e9a22165c0391ed0e8a42154 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 26 Feb 2026 10:22:51 +0100 Subject: [PATCH 09/12] ifdef ADAPTIVE_EXPOSURE_AVAILABLE --- src/engine/renderer/glsl_source/cameraEffects_fp.glsl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index 62d2ddfd07..0451423082 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -62,7 +62,9 @@ uniform uint u_ViewWidth; uniform uint u_ViewHeight; uniform bool u_Tonemap; +#if defined(ADAPTIVE_EXPOSURE_AVAILABLE) uniform bool u_TonemapAdaptiveExposure; +#endif /* x: contrast y: highlightsCompressionSpeed z: shoulderClip From ff5ff677d0f49e3537a6c380875079737f6c7f18 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 26 Feb 2026 10:26:19 +0100 Subject: [PATCH 10/12] SetUniform_TonemapAdaptiveExposure --- src/engine/renderer/tr_backend.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 156b480bc7..f711e9c4ff 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1728,11 +1728,13 @@ void RB_CameraPostFX() { vec4_t parms{ log2f( r_toneMappingHDRMax.Get() ) }; parms[1] = UINT32_MAX / ( windowConfig.vidWidth * windowConfig.vidHeight * ( uint32_t( parms[0] ) + 8 ) ); gl_cameraEffectsShader->SetUniform_TonemapParms2( parms ); + } + gl_cameraEffectsShader->SetUniform_Tonemap( tonemap ); + if ( glConfig.adaptiveExposureAvailable ) { gl_cameraEffectsShader->SetUniform_TonemapAdaptiveExposure( - glConfig.adaptiveExposureAvailable && r_toneMappingAdaptiveExposure.Get() ); + r_toneMappingAdaptiveExposure.Get() ); } - gl_cameraEffectsShader->SetUniform_Tonemap( tonemap ); // This shader is run last, so let it render to screen instead of // tr.mainFBO From c5a5037a23ef1a5ee10f3460aeb6f02cf30ccf97 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 26 Feb 2026 10:27:07 +0100 Subject: [PATCH 11/12] u_TonemapParms2 --- src/engine/renderer/glsl_source/cameraEffects_fp.glsl | 2 ++ src/engine/renderer/tr_backend.cpp | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index 0451423082..f69cff0311 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -70,7 +70,9 @@ y: highlightsCompressionSpeed z: shoulderClip w: highlightsCompression */ uniform vec4 u_TonemapParms; +#if defined(ADAPTIVE_EXPOSURE_AVAILABLE) uniform vec4 u_TonemapParms2; +#endif vec3 TonemapLottes( vec3 color ) { // Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines" diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index f711e9c4ff..09842e694a 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1725,9 +1725,12 @@ void RB_CameraPostFX() { r_toneMappingDarkAreaPointHDR.Get(), r_toneMappingDarkAreaPointLDR.Get(), tonemapParms[2], tonemapParms[3] ); gl_cameraEffectsShader->SetUniform_TonemapParms( tonemapParms ); - vec4_t parms{ log2f( r_toneMappingHDRMax.Get() ) }; - parms[1] = UINT32_MAX / ( windowConfig.vidWidth * windowConfig.vidHeight * ( uint32_t( parms[0] ) + 8 ) ); - gl_cameraEffectsShader->SetUniform_TonemapParms2( parms ); + if ( glConfig.adaptiveExposureAvailable ) { + vec4_t parms{ log2f( r_toneMappingHDRMax.Get() ) }; + parms[1] = UINT32_MAX / ( windowConfig.vidWidth * windowConfig.vidHeight * ( uint32_t( parms[0] ) + 8 ) ); + + gl_cameraEffectsShader->SetUniform_TonemapParms2( parms ); + } } gl_cameraEffectsShader->SetUniform_Tonemap( tonemap ); From 5fcbb99cd3f0fcb9b38944e1bb8b010a3f6f384e Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 26 Feb 2026 10:40:11 +0100 Subject: [PATCH 12/12] Apply exposure when there is tone mapping or not, the right time --- src/engine/renderer/glsl_source/cameraEffects_fp.glsl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl index f69cff0311..701594860d 100644 --- a/src/engine/renderer/glsl_source/cameraEffects_fp.glsl +++ b/src/engine/renderer/glsl_source/cameraEffects_fp.glsl @@ -103,8 +103,6 @@ void main() { vec4 color = texture2D(u_CurrentMap, st); color *= u_GlobalLightFactor; - color.rgb *= u_Exposure; - #if defined(r_highPrecisionRendering) && defined(HAVE_ARB_texture_float) if( u_Tonemap ) { #if defined(ADAPTIVE_EXPOSURE_AVAILABLE) @@ -114,9 +112,15 @@ void main() { } #endif + color.rgb *= u_Exposure; + color.rgb = TonemapLottes( color.rgb ); } + else #endif + { + color.rgb *= u_Exposure; + } color.rgb = clamp( color.rgb, vec3( 0.0f ), vec3( 1.0f ) );