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 f23055feb4..0af67d5143 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. */ @@ -490,10 +496,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 ); @@ -580,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; } @@ -629,6 +643,11 @@ static std::string GenFragmentHeader() { AddDefine( str, "USE_PUSH_BUFFER", 1 ); } + if ( glConfig.adaptiveExposureAvailable ) { + AddDefine( str, "BIND_LUMINANCE", Util::ordinal( BufferBind::LUMINANCE ) ); + AddDefine( str, "ADAPTIVE_EXPOSURE_AVAILABLE", 1 ); + } + return str; } @@ -654,6 +673,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 ); } @@ -838,7 +862,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 +1221,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 +1358,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 ); } } @@ -2607,6 +2643,23 @@ GLShader_lightMappingMaterial::GLShader_lightMappingMaterial() : GLCompileMacro_USE_PHYSICAL_MAPPING( this ) { } +GLShader_luminanceReduction::GLShader_luminanceReduction() : + 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() : + GLShader( "clearFrameData", + false, "clearFrameData" ) { +} + GLShader_reflection::GLShader_reflection(): GLShader( "reflection", ATTR_POSITION | ATTR_TEXCOORD | ATTR_QTANGENT, false, "reflection_CB", "reflection_CB" ), @@ -2802,11 +2855,14 @@ 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_TonemapParms2( this ), u_Exposure( this ), - u_InverseGamma( this ) -{ + u_InverseGamma( this ) { } void GLShader_cameraEffects::SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) @@ -3065,5 +3121,7 @@ GlobalUBOProxy::GlobalUBOProxy() : u_InverseGamma( this ), u_Tonemap( this ), u_TonemapParms( this ), + u_TonemapAdaptiveExposure( this ), + u_TonemapParms2( 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..3cb10d0fb3 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(); + void SetShaderProgramUniforms( ShaderProgramDescriptor* shaderProgram ) override; +}; + +class GLShader_clearFrameData : + public GLShader { + public: + GLShader_clearFrameData(); +}; + class GLShader_reflection : public GLShader, public u_ColorMapCube, @@ -3257,8 +3297,12 @@ 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_TonemapParms2, public u_Exposure, public u_InverseGamma { @@ -3489,6 +3533,8 @@ class GlobalUBOProxy : public u_InverseGamma, public u_Tonemap, public u_TonemapParms, + public u_TonemapAdaptiveExposure, + public u_TonemapParms2, public u_Exposure { public: @@ -3513,9 +3559,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 +3582,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..701594860d 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,24 +58,43 @@ 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; +#if defined(ADAPTIVE_EXPOSURE_AVAILABLE) +uniform bool u_TonemapAdaptiveExposure; +#endif /* x: contrast y: highlightsCompressionSpeed z: shoulderClip w: highlightsCompression */ -uniform bool u_Tonemap; 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" return pow( color, vec3( u_TonemapParms[0] ) ) / ( pow( color, vec3( u_TonemapParms[0] * u_TonemapParms[1] ) ) * u_TonemapParms[2] + u_TonemapParms[3] ); } + +#if defined(ADAPTIVE_EXPOSURE_AVAILABLE) +layout(std140, binding = BIND_LUMINANCE) uniform ub_LuminanceUBO { + uint luminanceU; +}; + +float GetAverageLuminance( const in uint luminance ) { + return float( luminanceU ) / ( u_TonemapParms2[1] * u_ViewWidth * u_ViewHeight ); +} +#endif + #endif DECLARE_OUTPUT(vec4) -void main() -{ +void main() { #insert material_fp // calculate the screen texcoord in the 0.0 to 1.0 range @@ -84,13 +103,24 @@ 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) + 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 *= u_Exposure; + color.rgb = TonemapLottes( color.rgb ); } + else #endif + { + color.rgb *= u_Exposure; + } color.rgb = clamp( color.rgb, vec3( 0.0f ), vec3( 1.0f ) ); 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..873f6d31ea --- /dev/null +++ b/src/engine/renderer/glsl_source/clearFrameData_cp.glsl @@ -0,0 +1,51 @@ +/* +=========================================================================== + +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; +}; + +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..9f90bce09c --- /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 * u_TonemapParms2[1] ); +} + +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(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..09842e694a 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1633,6 +1633,37 @@ 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 = windowConfig.vidWidth; + const int height = windowConfig.vidHeight; + + 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() ) }; + parms[1] = UINT32_MAX / ( width * height * ( uint32_t( parms[0] ) + 8 ) ); + 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,13 +1717,28 @@ 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 ); + + 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 ); + if ( glConfig.adaptiveExposureAvailable ) { + gl_cameraEffectsShader->SetUniform_TonemapAdaptiveExposure( + r_toneMappingAdaptiveExposure.Get() ); + } + // This shader is run last, so let it render to screen instead of // tr.mainFBO R_BindNullFBO(); @@ -1704,6 +1750,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 +2858,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