From 08fb51a57eacb400c6d27787e485b146ccd7f5d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Thu, 15 Oct 2026 08:49:11 +0000 Subject: [PATCH 01/20] Refactor scene render pipeline state Move jitter, reprojection, mip LOD bias, and upscaling setup out of `EveSpaceScene` and into the render driver so render state is prepared explicitly per view. This also splits execution into old/new render routes, adds shared feature-gated buffer allocation helpers, and exposes a post-process jitter helper for TAA. The change also wires in reflection quality/probe settings and keeps cached upscaling info in sync when contexts are created or destroyed. --- trinity/Eve/EveSpaceScene.cpp | 87 +--- trinity/Eve/EveSpaceScene.h | 11 +- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 414 +++++++++++++++++- trinity/Eve/EveSpaceSceneRenderDriver.h | 48 +- .../Eve/EveSpaceSceneRenderDriver_Blue.cpp | 29 ++ .../PostProcess/Tr2PostProcessRenderer.cpp | 12 + trinity/PostProcess/Tr2PostProcessRenderer.h | 1 + 7 files changed, 502 insertions(+), 100 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index f5211d826..406e53b2c 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -217,8 +217,8 @@ EveSpaceScene::EveSpaceScene( IRoot* lockobj ) : m_projectionLast( IdentityMatrix() ), m_jitteredProjection( IdentityMatrix() ), m_reprojectionMatrix( IdentityMatrix() ), - m_jitter( 0.f, 0.f, 0.f, 0.f ), - m_upscalingAmount( 1.0f ) + m_upscalingAmount( 1.0f ), + m_mipLodBias( 0.0f ) { TriPoolAllocator* allocator = Tr2Renderer::GetPoolAllocator(); m_primaryBatches[TRIBATCHTYPE_OPAQUE] = CCP_NEW( "EveSpaceScene/m_batches" ) TriRenderBatchAccumulator( allocator ); @@ -1255,47 +1255,6 @@ bool EveSpaceScene::RenderDistortionBatches( BatchMap& batches, const Tr2Texture return true; } -void EveSpaceScene::Jitter( Tr2RenderContext& renderContext ) -{ - m_projection = Tr2Renderer::GetProjectionTransform(); - - auto upscalingInfo = renderContext.GetPrimaryRenderContext().GetUpscalingInfo( Tr2Renderer::GetUpscalingContextID() ); - if( upscalingInfo.technique != Tr2UpscalingAL::NONE && upscalingInfo.temporal ) - { - m_jitter.x = upscalingInfo.jitterX; - m_jitter.y = upscalingInfo.jitterY; - m_jitterMatrix = TranslationMatrix( Vector3( m_jitter.x, m_jitter.y, 0 ) ); - m_jitteredProjection = m_projection * m_jitterMatrix; - } - else if( m_sceneDefaultPostProcess && m_sceneDefaultPostProcess->GetTaaIfAvailable() != nullptr ) - { - auto rtWidth = renderContext.m_esm.GetRenderTargetWidth(); - auto rtHeight = renderContext.m_esm.GetRenderTargetHeight(); - const Vector2 samplingPatterns[] = { Vector2( .125f, -.375f ), - Vector2( -.125f, .375f ), - Vector2( .375f, .125f ), - Vector2( -.375f, -.125f ) }; - - auto frame = renderContext.GetPrimaryRenderContext().GetRecordingFrameNumber(); - auto samplingIndex = frame % ( sizeof( samplingPatterns ) / sizeof( samplingPatterns[0] ) ); - - m_jitter.x = 2.0f * samplingPatterns[samplingIndex].x / float( rtWidth ); - m_jitter.y = 2.0f * samplingPatterns[samplingIndex].y / float( rtHeight ); - - auto currJitterOffset = Vector3( m_jitter.x, m_jitter.y, 0 ); - - m_jitterMatrix = TranslationMatrix( currJitterOffset ); - m_jitteredProjection = m_projection * m_jitterMatrix; - } - else - { - m_jitterMatrix = IdentityMatrix(); - m_jitteredProjection = m_projection; - m_jitter.x = m_jitter.y = 0.0f; - } -} - - // -------------------------------------------------------------------------------------- // Description: // Set up rendering states, frustum and gather all batches. @@ -1330,12 +1289,7 @@ void EveSpaceScene::BeginRender( bool enableDistortion, Tr2RenderContext& render { m_dataTextureMgr->SetVariables(); } - - Jitter( renderContext ); - - Tr2Renderer::SetProjectionTransform( m_jitteredProjection ); - m_reprojectionMatrix = Inverse( m_projection ) * Inverse( Tr2Renderer::GetViewTransform() ) * m_viewLast * m_projectionLast; - + m_velocityMapDirty = false; { @@ -3114,10 +3068,10 @@ void EveSpaceScene::PopulatePerFramePSData( PerFramePSData& data, Tr2ShadowMap* data.ViewportSize.y = renderContext.m_esm.GetDeviceViewport().m_height; data.Time = Tr2Renderer::GetAnimationTime(); - data.Upscaling = 1.0f; + data.Upscaling = m_upscalingAmount; data.FrameIndex = (uint32_t)Tr2Renderer::GetCurrentFrameCounter(); - data.Jittering = m_jitter != Vector4( 0, 0, 0, 0 ); + data.Jittering = LengthSq( m_jitterMatrix.GetTranslation() ) > 0.0f ? 1 : 0; data.ShadowQuality = 1 << (uint32_t)m_shadowQuality; @@ -3142,24 +3096,8 @@ void EveSpaceScene::PopulatePerFramePSData( PerFramePSData& data, Tr2ShadowMap* data.ProjectionToView.x = projection._43; data.ProjectionToView.y = projection._33; - data.SceneMipLodBias = 0.0f; - m_upscalingAmount = 1.0f; - - auto upscalingInfo = renderContext.GetPrimaryRenderContext().GetUpscalingInfo( Tr2Renderer::GetUpscalingContextID() ); - if( upscalingInfo.technique != Tr2UpscalingAL::NONE ) - { - m_upscalingAmount = upscalingInfo.upscalingAmount; - data.SceneMipLodBias = upscalingInfo.mipLevelBias; - if( !upscalingInfo.temporal && m_sceneDefaultPostProcess ) - { - data.SceneMipLodBias += m_sceneDefaultPostProcess->GetMipLodBias(); - } - data.Upscaling = m_upscalingAmount; - } - else if( m_sceneDefaultPostProcess ) - { - data.SceneMipLodBias = m_sceneDefaultPostProcess->GetMipLodBias(); - } + data.SceneMipLodBias = m_mipLodBias; + data.Upscaling = m_upscalingAmount; if( shadowMap ) { @@ -4296,3 +4234,14 @@ void RegisterWithVariableStore( const EveSpaceScene::ShadowResources& shadowReso shadowResources.pointLightShadowMap.IsValid() ? shadowResources.pointLightShadowMap : gpuResourcePool.GetPersistentTexture( "EmptyShadowUint", 1, 1, ImageIO::PIXEL_FORMAT_R8_UINT, Tr2GpuUsage::SHADER_RESOURCE, &initData ) ); GlobalStore().RegisterVariable( "ShadowMapAtlas", shadowResources.pointLightShadowDepth ); } + +void EveSpaceScene::SetRenderingInfo( Matrix jitterMatrix, float mipLodBias, float upscaling ) +{ + m_projection = Tr2Renderer::GetProjectionTransform(); + m_jitterMatrix = jitterMatrix; + m_jitteredProjection = m_projection * m_jitterMatrix; + m_reprojectionMatrix = Inverse( m_projection ) * Inverse( Tr2Renderer::GetViewTransform() ) * m_viewLast * m_projectionLast; + m_upscalingAmount = upscaling; + m_mipLodBias = mipLodBias; + Tr2Renderer::SetProjectionTransform( m_jitteredProjection ); +} diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 0effaab68..e4c7a91d3 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -149,11 +149,13 @@ BLUE_CLASS( EveSpaceScene ) : void BeginRender( bool enableDistortion, Tr2RenderContext& renderContext ); void EndRender( Tr2RenderContext & renderContext ); void Render3DUI( Tr2RenderContext & renderContext ); - void PopulateAndApplyPerFrameData( Tr2RenderContext & renderContext ); + void PopulateAndApplyPerFrameData( Tr2RenderContext& renderContext ); void ApplyUpscalingToPerFrameData( uint32_t width, uint32_t height, Tr2RenderContext& renderContext ); void GatherBatches( bool includeDistortions, Tr2RenderContext& renderContext ); void PrepareRaytracedShadows( Tr2RenderContext & renderContext ); + + void SetRenderingInfo( Matrix jitterMatrix, float mipLodBias, float upscaling ); ////////////////////////////////////////////////////////////////////////////////////// // IInitialize @@ -337,6 +339,7 @@ BLUE_CLASS( EveSpaceScene ) : Vector4 sphere = Vector4( 0, 0, 0, 0 ); float pixelSize = 0.0; }; + EvePlanetPerObjData m_planetPerObjData; Tr2ShaderBufferPtr m_planetPerObjBuffer; @@ -625,9 +628,7 @@ BLUE_CLASS( EveSpaceScene ) : Tr2ShLightingManagerPtr m_shLightingManager; float m_upscalingAmount; - Vector4 m_jitter; // xy: projection offset, zw: pixel offset - - void Jitter( Tr2RenderContext & renderContext ); + float m_mipLodBias; Tr2ImpostorManagerPtr m_impostorManager; @@ -704,7 +705,7 @@ BLUE_CLASS( EveSpaceScene ) : // reprojection matrix Matrix m_reprojectionMatrix; - + std::unique_ptr m_instancedMeshManager; friend class EveSpaceSceneRenderDriver; diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index fe3c1de73..c2328a285 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -13,6 +13,7 @@ #include "../Particle/Tr2GpuParticleSystem.h" #include "../Tr2TextureReference.h" #include "Resources/TriTextureRes.h" +#include "Tr2ReflectionProbe.h" CCP_STATS_DECLARE( updateDynamicLightLists, "Trinity/EveSpaceScene/updateDynamicLights", true, CST_TIME, "Time took to gather dynamic lights for EveSpaceScene" ); @@ -172,6 +173,7 @@ void EveSpaceSceneRenderDriver::ReleaseResources( TriStorage s ) { DeleteUpscalingContext( m_upscalingContext ); m_upscalingContext = nullptr; + m_upscalingInfo = Tr2UpscalingAL::UpscalingInfo(); } } @@ -202,12 +204,15 @@ void EveSpaceSceneRenderDriver::SetupUpscaling( const TextureSize2D& displaySize params.depthFormat = Tr2RenderContextEnum::DSFMT_D32F; m_upscalingContext = renderContext.CreateUpscalingContext( params, m_upscalingContext ? m_upscalingContext->GetID() : Tr2UpscalingAL::INVALID_CONTEXT_ID ); + // m_upscalingContext may not have been successfully created, so we need to check it before calling GetUpscalingInfo + m_upscalingInfo = renderContext.GetPrimaryRenderContext().GetUpscalingInfo( m_upscalingContext ? m_upscalingContext->GetID() : Tr2UpscalingAL::INVALID_CONTEXT_ID ); } } else if( m_upscalingContext ) { DeleteUpscalingContext( m_upscalingContext ); m_upscalingContext = nullptr; + m_upscalingInfo = Tr2UpscalingAL::UpscalingInfo(); } } @@ -295,8 +300,24 @@ void EveSpaceSceneRenderDriver::PropagateSettings() { m_scene->m_volumetricsRenderer->SetQuality( m_settings.volumetricQuality ); } + + if( m_settings.reflectionQuality != ReflectionQuality::Disabled && m_reflectionProbe == nullptr ) + { + m_reflectionProbe.CreateInstance(); + } + else if( m_settings.reflectionQuality == ReflectionQuality::Disabled && m_reflectionProbe ) + { + m_reflectionProbe = nullptr; + } } +void EveSpaceSceneRenderDriver::PropagateRenderningInfo( Matrix jitterMatrix, float mipLodBias, float upscalingAmount ) +{ + if( m_scene ) + { + m_scene->SetRenderingInfo( jitterMatrix, mipLodBias, upscalingAmount ); + } +} bool EveSpaceSceneRenderDriver::Validate( const Span& destDimensions, const Span& outputs, Be::Time realTime, Be::Time simTime ) { @@ -366,9 +387,8 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::RenderSSAO( const Tr2Text { renderContext.SetReadOnlyDepth( true ); - auto upscalingInfo = renderContext.GetPrimaryRenderContext().GetUpscalingInfo( m_upscalingContext ? m_upscalingContext->GetID() : Tr2UpscalingAL::INVALID_CONTEXT_ID ); auto scenePostProcess = m_scene->m_sceneDefaultPostProcess; - bool temporal = upscalingInfo.temporal || ( scenePostProcess != nullptr && scenePostProcess->GetTaaIfAvailable() != nullptr ); + bool temporal = m_upscalingInfo.temporal || ( scenePostProcess != nullptr && scenePostProcess->GetTaaIfAvailable() != nullptr ); ssao = m_ssao->Filter( depthMap, normalMap, m_gpuResourcePool, renderContext, temporal ); renderContext.SetReadOnlyDepth( false ); @@ -377,10 +397,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::RenderSSAO( const Tr2Text { return ssao; } - else - { - return GetEmptySSAO( m_gpuResourcePool ); - } + return GetEmptySSAO( m_gpuResourcePool ); } void EveSpaceSceneRenderDriver::SetCameraToRenderer( Tr2RenderContext& renderContext ) const @@ -392,6 +409,12 @@ void EveSpaceSceneRenderDriver::SetCameraToRenderer( Tr2RenderContext& renderCon Tr2Renderer::SetViewTransform( view->GetTransform() ); } +void EveSpaceSceneRenderDriver::SetCameraToRenderer( TriViewPtr view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const +{ + projection->SetProjection( renderContext ); + Tr2Renderer::SetViewTransform( view->GetTransform() ); +} + TextureSize2D EveSpaceSceneRenderDriver::GetRenderSize( const TextureSize2D& displaySize ) const { if( m_upscalingContext ) @@ -426,9 +449,342 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina return; } + if( m_newRoute ) + { + RenderNew( destinations, outputs, realTime, simTime, rootTimer, renderContext ); + } + else + { + RenderOld( destinations, outputs, realTime, simTime, rootTimer, renderContext ); + } +} + +Matrix EveSpaceSceneRenderDriver::GetJitterMatrix( uint32_t renderWidth, uint32_t renderHeight, Tr2RenderContext& renderContext ) const +{ + Matrix jitterMatrix; + + if( m_upscalingContext ) + { + float jitterX = 0.0f; + float jitterY = 0.0f; + m_upscalingContext->GetJitter( jitterX, jitterY ); + jitterMatrix = TranslationMatrix( Vector3( jitterX, jitterY, 0 ) ); + } + else if( m_postProcess && m_scene && m_scene->GetPostProcess() && m_scene->GetPostProcess()->GetTaaIfAvailable() != nullptr ) + { + auto frameIndex = renderContext.GetPrimaryRenderContext().GetRenderedFrameNumber(); + jitterMatrix = m_postProcess->GetJitter( frameIndex, renderWidth, renderHeight ); + } + else + { + jitterMatrix = IdentityMatrix(); + } + + return jitterMatrix; +} + +float EveSpaceSceneRenderDriver::GetMipLodBias() const +{ + float mipLodBias = 0.0f; + USE_MAIN_THREAD_RENDER_CONTEXT(); + + if( m_scene ) + { + if( auto postprocess = m_scene->GetPostProcess() ) + { + mipLodBias += postprocess->GetMipLodBias(); + } + } + + if( m_upscalingContext ) + { + mipLodBias += m_upscalingInfo.mipLevelBias; + } + return mipLodBias; +} +void EveSpaceSceneRenderDriver::RenderNew( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +{ GlobalStore().RegisterVariable( "EveSpaceSceneReflectionCorrectionLookupTable", m_reflectionCorrectionEnabled ? m_reflectionCorrectionMap : m_blackReflectionCorrectionMap ); + + const TextureSize2D displaySize = destinations.data->GetDesc(); + const TextureSize2D renderSize = GetRenderSize( displaySize ); + m_scene->m_viewLast = m_viewLast; + m_scene->m_projectionLast = m_projectionLast; + + if( m_camera ) + { + m_camera->Update( simTime ); + } + + auto projection = m_camera ? m_camera->GetProjection() : m_projection; + auto view = m_camera ? m_camera->GetViewMatrix() : m_view; + + PropagateSettings(); + + GlobalStore().RegisterVariable( "SSAOMap", GetEmptySSAO( m_gpuResourcePool ) ); + + renderContext.m_esm.SetInvertedDepthTest( true ); + ON_BLOCK_EXIT( [&] { renderContext.m_esm.SetInvertedDepthTest( false ); } ); + + // Here we would render the reflection pass + + // render the main scene + static const FeatureSet mainFeatures = { true, true, true, true, true, true, true }; + + SetCameraToRenderer( renderContext ); + float upscalingAmount = m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f; + auto jitterMatrix = GetJitterMatrix( renderSize.width, renderSize.height, renderContext ); + auto mipLodBias = GetMipLodBias(); + + RenderInfo renderInfo = { view, projection, jitterMatrix, mipLodBias, upscalingAmount, GetRenderSize( destinations.data->GetDesc() ), mainFeatures, Tr2RenderReason::TR2RENDERREASON_NORMAL }; + + std::vector tempOutputs = { + { BlueSharedString( "DepthMap" ), {} }, + { BlueSharedString( "BackBuffer" ), {} }, + { BlueSharedString( "VelocityMap" ), {} }, + { BlueSharedString( "OpaqueColorMap" ), {} } + }; + const Span tempOutputsSpan = { tempOutputs.data(), tempOutputs.size() }; + + renderContext.m_esm.SetRenderTarget( 0, *destinations.data ); + renderContext.m_esm.SetDepthStencilBuffer( {} ); + + GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", Tr2TextureAL{} ); + + RenderView( renderInfo, destinations, tempOutputsSpan, realTime, simTime, rootTimer, renderContext ); + + { + auto depthBuffer = FindNamedOutput( tempOutputsSpan, "DepthMap" ); + auto velocityMap = FindNamedOutput( tempOutputsSpan, "VelocityMap" ); + auto customBackBuffer = FindNamedOutput( tempOutputsSpan, "BackBuffer" ); + auto opaqueBackBuffer = FindNamedOutput( tempOutputsSpan, "OpaqueColorMap" ); + TimeSection postProcessSection( m_timers.postProcess, "PostProcess", rootTimer, renderContext ); + m_postProcess->Execute( *destinations.data, std::move( customBackBuffer->texture ), depthBuffer->texture, std::move( velocityMap->texture ), std::move( opaqueBackBuffer->texture ), m_scene, m_upscalingContext, m_gpuResourcePool, renderContext ); + } + + m_viewLast = m_scene->m_viewLast; + m_projectionLast = m_scene->m_projectionLast; + m_scene->PopulateAndApplyPerFrameData( renderContext ); + + SetCameraToRenderer( renderContext ); + { + TimeSection ui3dSection( m_timers.ui3d, "Render3dUI", rootTimer, renderContext ); + m_scene->Render3DUI( renderContext ); + } + + for( auto& toolScene : m_toolsScenes ) + { + if( ITr2UpdateablePtr updateable = BlueCastPtr( toolScene ) ) + { + updateable->Update( realTime, simTime ); + } + toolScene->Render( renderContext ); + } + + GlobalStore().RegisterVariable( "SSAOMap", GetEmptySSAO( m_gpuResourcePool ) ); + GlobalStore().RegisterVariable( "DepthMap", Tr2TextureAL{} ); + + if( m_settings.showFPS ) + { + m_fpsRenderer->Execute( realTime, simTime, renderContext ); + } +} + +void EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +{ + auto featureSet = renderInfo.featureSet; + auto renderSize = renderInfo.renderSize; + auto view = renderInfo.view; + auto projection = renderInfo.projection; + + auto velocityMap = GetVelocityMapIfNeeded( renderSize, outputs, featureSet ); + auto backBuffer = FindNamedOutput( outputs, "BackBuffer" ); + if( !backBuffer || !backBuffer->texture.IsValid() ) + { + SetNamedOutput(outputs, "BackBuffer", m_gpuResourcePool.GetTempTexture( "backBuffer", renderSize, m_internalPixelFormat, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE )); + backBuffer = FindNamedOutput( outputs, "BackBuffer" ); + } + + auto depthBuffer = m_gpuResourcePool.GetTempTexture( "depthBuffer", renderSize, ImageIO::PIXEL_FORMAT_D32_FLOAT, Tr2GpuUsage::DEPTH_STENCIL | Tr2GpuUsage::SHADER_RESOURCE ); + + SetNamedOutput( outputs, "DepthMap", depthBuffer ); + + renderContext.m_esm.PushRenderTarget( 0 ); + if( featureSet.velocityMap ) + { + renderContext.m_esm.PushRenderTarget( 1 ); + } + renderContext.m_esm.PushDepthStencilBuffer(); + ON_BLOCK_EXIT( [&] { + renderContext.m_esm.PopDepthStencilBuffer(); + if( featureSet.velocityMap ) + { + renderContext.m_esm.PopRenderTarget( 1 ); + } + renderContext.m_esm.PopRenderTarget( 0 ); + } ); + + if( m_background ) + { + // render background effect to back buffer before rendering the scene + BeginRenderPass( renderContext, { backBuffer->texture }, {} ); + renderContext.Clear( Tr2RenderContextEnum::CLEARFLAGS_TARGET, m_settings.clearColor, 0 ); + + const Tr2TextureAL& backgroundTarget = backBuffer->texture; + m_background->Execute( { &backgroundTarget, 1 }, {}, realTime, simTime, rootTimer, renderContext ); + } + // set the + BeginRenderPass( renderContext, { backBuffer->texture }, depthBuffer ); + renderContext.Clear( m_background ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); + + SetCameraToRenderer( view, projection, renderContext ); + PropagateRenderningInfo( renderInfo.jitterMatrix, renderInfo.mipLodBias, renderInfo.upscalingAmount ); + + + { + TimeSection updateSection( m_timers.update, "Update", rootTimer, renderContext ); + m_scene->Update( realTime, simTime ); + } + UpdateGpuParticleSystem( renderContext ); + + auto prevVisualizeMethod = m_scene->m_visualizeMethod; + m_scene->m_visualizeMethod = m_settings.visualizeMethod; + ON_BLOCK_EXIT( [&] { m_scene->m_visualizeMethod = prevVisualizeMethod; } ); + + { + TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); + + if( g_eveSpaceSceneDynamicLighting ) + { + if( auto lightManager = Tr2LightManager::GetOrCreateInstance( "res:/graphics/effect/managed/space/system/computelightlists.fx" ) ) + { + lightManager->SetVariableStore(); + lightManager->SetLightingQuality( m_settings.lightingQuality ); + } + } + else + { + Tr2LightManager::DeleteInstance(); + } + + m_scene->BeginRender( m_settings.enableDistortion, renderContext ); + } + + GlobalStore().RegisterVariable( "DepthMap", depthBuffer ); + + SetNamedOutput( outputs, "DepthMap", depthBuffer ); + + Tr2GpuResourcePool::Texture distortionMap = GetDistortionMapIfNeeded( renderSize, featureSet ); + + bool hasBackgroundDistortionBatches = false; + { + TimeSection backgroundSection( m_timers.background, "BackgroundPass", rootTimer, renderContext ); + hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, renderContext ); + } + if( m_settings.enableDistortion && hasBackgroundDistortionBatches ) + { + ApplyDistortion( backBuffer->texture, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); + + renderContext.m_esm.SetDepthStencilBuffer( depthBuffer ); + } + distortionMap = {}; + + Tr2GpuResourcePool::Texture normalMap = GetNormalMapIfNeeded( renderSize, outputs, featureSet ); + Tr2GpuResourcePool::Texture customStencil = GetCustomStencilMapIfNeeded( renderSize, outputs ); + { + TimeSection depthPassSection( m_timers.depthPass, "DepthPass", rootTimer, renderContext ); + m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, renderContext, m_depthPassTechnique ); + } + GlobalStore().RegisterVariable( "SpaceSceneNormalMap", normalMap ); + GlobalStore().RegisterVariable( "SpaceSceneCustomStencil", customStencil ); + ON_BLOCK_EXIT( [&] { GlobalStore().RegisterVariable( "SpaceSceneCustomStencil", Tr2TextureAL{} ); } ); + + Tr2GpuResourcePool::Texture opaqueBackBuffer; + EveSpaceScene::ShadowResources shadowResources; + if( m_scene->m_display && m_mainPassRenderingEnabled ) + { + { + TimeSection shadowsSection( m_timers.shadows, "Shadows", rootTimer, renderContext ); + shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, renderContext ); + } + TimeSection ssaoSection( m_timers.ssao, "SSAO", rootTimer, renderContext ); + auto ssao = RenderSSAO( depthBuffer, normalMap, renderContext ); + ssaoSection.Stop(); + + GlobalStore().RegisterVariable( "SSAOMap", ssao ); + + if( auto lightManager = Tr2LightManager::GetInstance() ) + { + GPU_REGION( renderContext, "Lighting" ); + TRINITY_STATS_SCOPED_TIME( updateDynamicLightLists ); + renderContext.SetReadOnlyDepth( true ); + lightManager->UpdateLists( depthBuffer, renderContext ); + renderContext.SetReadOnlyDepth( false ); + } + + opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs, featureSet ); + + RegisterWithVariableStore( shadowResources, m_gpuResourcePool ); + bool hasDistortionBatches = false; + distortionMap = GetDistortionMapIfNeeded( renderSize, featureSet ); + { + TimeSection mainPassSection( m_timers.mainPass, "MainPass", rootTimer, renderContext ); + hasDistortionBatches = m_scene->RenderMainPass( backBuffer->texture, depthBuffer, distortionMap, velocityMap, opaqueBackBuffer, m_gpuResourcePool, renderContext ); + } + RegisterWithVariableStore( {}, m_gpuResourcePool ); + + if( m_settings.enableDistortion && hasDistortionBatches ) + { + ApplyDistortion( backBuffer->texture, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); + renderContext.m_esm.SetDepthStencilBuffer( depthBuffer ); + } + distortionMap = {}; + GlobalStore().RegisterVariable( "SSAOMap", Tr2TextureAL{} ); + } + else + { + // Even if we skip main pass rendering, we might need opaqueBackBuffer for upscaling or TAA + opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs, featureSet ); + if( opaqueBackBuffer.IsValid() ) + { + renderContext.RenderPassHint( { Tr2LoadAction::DONT_CARE, Tr2StoreAction::STORE }, {} ); + BeginRenderPass( renderContext, { opaqueBackBuffer.Get() }, {} ); + renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_FULLSCREEN ); + Tr2Renderer::DrawTexture( renderContext, backBuffer->texture ); + } + } + if( m_sceneOverlay ) + { + Tr2TextureAL targets[] = { backBuffer->texture, velocityMap.Get() }; + m_sceneOverlay->Execute( { targets, 2 }, {}, realTime, simTime, rootTimer, renderContext ); + } + GlobalStore().RegisterVariable( "SpaceSceneNormalMap", Tr2TextureAL{} ); + normalMap = {}; + + BeginRenderPass( renderContext, { backBuffer->texture }, depthBuffer ); + { + renderContext.SetReadOnlyDepth( true ); + m_scene->RunLensflareOcclusionQueries( depthBuffer, renderContext ); + renderContext.SetReadOnlyDepth( false ); + } + { + TimeSection endRenderSection( m_timers.endRender, "EndRender", rootTimer, renderContext ); + m_scene->EndRender( renderContext ); + } + m_viewLast = m_scene->m_viewLast; + m_projectionLast = m_scene->m_projectionLast; + m_scene->PopulateAndApplyPerFrameData( renderContext ); + + GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", Tr2TextureAL{} ); +} + +void EveSpaceSceneRenderDriver::RenderOld( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +{ + + GlobalStore().RegisterVariable( "EveSpaceSceneReflectionCorrectionLookupTable", m_reflectionCorrectionEnabled ? m_reflectionCorrectionMap : m_blackReflectionCorrectionMap ); + static const FeatureSet mainFeatures = { true, true, true, true, true, true, true }; m_scene->m_viewLast = m_viewLast; m_scene->m_projectionLast = m_projectionLast; @@ -458,8 +814,6 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina renderContext.m_esm.PopRenderTarget( 0 ); } ); - Tr2Renderer::SetUpscalingContextID( m_upscalingContext ? m_upscalingContext->GetID() : Tr2UpscalingAL::INVALID_CONTEXT_ID ); - auto customBackBuffer = m_gpuResourcePool.GetTempTexture( "customBackBuffer", renderSize, m_internalPixelFormat, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); if( m_background ) @@ -476,6 +830,9 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina renderContext.Clear( m_background ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); SetCameraToRenderer( renderContext ); + float upscalingAmount = m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f; + PropagateRenderningInfo( GetJitterMatrix( renderSize.width, renderSize.height, renderContext ), GetMipLodBias(), upscalingAmount ); + { TimeSection updateSection( m_timers.update, "Update", rootTimer, renderContext ); m_scene->Update( realTime, simTime ); @@ -512,8 +869,8 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina SetNamedOutput( outputs, "DepthMap", depthBuffer ); - Tr2GpuResourcePool::Texture distortionMap = GetDistortionMapIfNeeded( renderSize ); - Tr2GpuResourcePool::Texture velocityMap = GetVelocityMapIfNeeded( renderSize, outputs ); + Tr2GpuResourcePool::Texture distortionMap = GetDistortionMapIfNeeded( renderSize, mainFeatures ); + Tr2GpuResourcePool::Texture velocityMap = GetVelocityMapIfNeeded( renderSize, outputs, mainFeatures ); bool hasBackgroundDistortionBatches = false; { @@ -528,7 +885,7 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina } distortionMap = {}; - Tr2GpuResourcePool::Texture normalMap = GetNormalMapIfNeeded( renderSize, outputs ); + Tr2GpuResourcePool::Texture normalMap = GetNormalMapIfNeeded( renderSize, outputs, mainFeatures ); Tr2GpuResourcePool::Texture customStencil = GetCustomStencilMapIfNeeded( renderSize, outputs ); { TimeSection depthPassSection( m_timers.depthPass, "DepthPass", rootTimer, renderContext ); @@ -561,11 +918,11 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina renderContext.SetReadOnlyDepth( false ); } - opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs ); + opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs, mainFeatures ); RegisterWithVariableStore( shadowResources, m_gpuResourcePool ); bool hasDistortionBatches = false; - distortionMap = GetDistortionMapIfNeeded( renderSize ); + distortionMap = GetDistortionMapIfNeeded( renderSize, mainFeatures ); { TimeSection mainPassSection( m_timers.mainPass, "MainPass", rootTimer, renderContext ); hasDistortionBatches = m_scene->RenderMainPass( customBackBuffer, depthBuffer, distortionMap, velocityMap, opaqueBackBuffer, m_gpuResourcePool, renderContext ); @@ -583,7 +940,7 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina else { // Even if we skip main pass rendering, we might need opaqueBackBuffer for upscaling or TAA - opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs ); + opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs, mainFeatures ); if( opaqueBackBuffer.IsValid() ) { renderContext.RenderPassHint( { Tr2LoadAction::DONT_CARE, Tr2StoreAction::STORE }, {} ); @@ -657,20 +1014,24 @@ void EveSpaceSceneRenderDriver::UpdateGpuParticleSystem( Tr2RenderContext& rende } } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetDistortionMapIfNeeded( const TextureSize2D& size ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetDistortionMapIfNeeded( const TextureSize2D& size, const FeatureSet& featureSet ) { - if( m_settings.enableDistortion ) + if( m_settings.enableDistortion && featureSet.distortion ) { return m_gpuResourcePool.GetTempTexture( "distortionMap", size, ImageIO::PIXEL_FORMAT_B8G8R8A8_UNORM, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); } return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) { - USE_MAIN_THREAD_RENDER_CONTEXT(); - auto upscalingInfo = renderContext.GetUpscalingInfo( m_upscalingContext ? m_upscalingContext->GetID() : Tr2UpscalingAL::INVALID_CONTEXT_ID ); - bool needsVelocityMap = m_settings.antiAliasingQuality != AntiAliasingQuality::Disabled || ( upscalingInfo.technique != Tr2UpscalingAL::NONE && upscalingInfo.temporal ); + if( !featureSet.antiAliasing && !featureSet.upscaling && !m_upscalingInfo.temporal ) + { + return {}; + } + + USE_MAIN_THREAD_RENDER_CONTEXT(); + bool needsVelocityMap = m_settings.antiAliasingQuality != AntiAliasingQuality::Disabled || m_upscalingInfo.temporal; auto namedOutput = FindNamedOutput( outputs, "VelocityMap" ); Tr2GpuResourcePool::Texture velocityMap; if( needsVelocityMap || m_settings.forceVelocityMap || namedOutput ) @@ -685,8 +1046,12 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( c return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) { + if( !featureSet.ssao && !featureSet.shadows ) + { + return {}; + } auto namedOutput = FindNamedOutput( outputs, "NormalMap" ); if( ( ( m_settings.aoQuality != AmbientOcclusionQuality::Disabled || m_settings.shadowQuality == ShadowQuality::SHADOW_RAYTRACED ) && Tr2Renderer::GetShaderModel() != TR2SM_3_0_LO ) || m_settings.forceNormalMap || namedOutput ) { @@ -715,8 +1080,13 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetCustomStencilMapIfNeed return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) { + if( !featureSet.upscaling && !featureSet.antiAliasing ) + { + return {}; + } + auto namedOutput = FindNamedOutput( outputs, "OpaqueColorMap" ); bool needsOpaqueBackBuffer = m_upscalingContext != nullptr || m_settings.antiAliasingQuality >= AntiAliasingQuality::Medium || m_settings.forceOpaqueBuffer || namedOutput; if( !needsOpaqueBackBuffer ) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 722c242f0..ca4e85374 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -20,6 +20,7 @@ BLUE_DECLARE( TriStepRenderFps ); BLUE_DECLARE_IVECTOR( ITr2Scene ); BLUE_DECLARE( EveCamera ); BLUE_DECLARE( Tr2Sprite2dScene ); +BLUE_DECLARE( Tr2ReflectionProbe ); @@ -59,6 +60,7 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : PostProcess::Quality postProcessingQuality = PostProcess::Quality::HIGH; Tr2VolumerticQuality volumetricQuality = Tr2VolumerticQuality::High; LightingQuality lightingQuality = LightingQuality::HIGH; + ReflectionQuality reflectionQuality = ReflectionQuality::High; Color clearColor = Color( 0.0f, 0.0f, 0.0f, 1.0f ); @@ -71,6 +73,30 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : EveSpaceScene::EveVisualizeMethod visualizeMethod = EveSpaceScene::VM_NONE; }; + struct FeatureSet + { + bool upscaling = true; + bool antiAliasing = true; + bool distortion = true; + bool reflections = true; + bool volumetrics = true; + bool ssao = true; + bool shadows = true; + bool velocityMap = true; + }; + + struct RenderInfo + { + TriViewPtr view; + TriProjectionPtr projection; + Matrix jitterMatrix; + float mipLodBias; + float upscalingAmount; + TextureSize2D renderSize; + FeatureSet featureSet; + Tr2RenderReason reason; + }; + EveSpaceSceneRenderDriver( IRoot* lockobj = nullptr ); ~EveSpaceSceneRenderDriver(); @@ -95,16 +121,25 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : EXPOSE_TO_BLUE(); private: + + void RenderOld( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + void RenderNew( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + void RenderView( const RenderInfo& renderInfo, const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetupUpscaling( const TextureSize2D& displaySize ); void PropagateSettings(); + void PropagateRenderningInfo( Matrix jitterMatrix, float mipLodBias, float upscaling ); void SetCameraToRenderer( Tr2RenderContext & renderContext ) const; + void SetCameraToRenderer( TriViewPtr view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const; TextureSize2D GetRenderSize( const TextureSize2D& displaySize ) const; - Tr2GpuResourcePool::Texture GetDistortionMapIfNeeded( const TextureSize2D& size ); - Tr2GpuResourcePool::Texture GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs ); - Tr2GpuResourcePool::Texture GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs ); + Matrix GetJitterMatrix( uint32_t renderWidth, uint32_t renderHeight, Tr2RenderContext& renderContext ) const; + float GetMipLodBias( ) const; + + Tr2GpuResourcePool::Texture GetDistortionMapIfNeeded( const TextureSize2D& size, const FeatureSet& featureSet ); + Tr2GpuResourcePool::Texture GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); + Tr2GpuResourcePool::Texture GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); Tr2GpuResourcePool::Texture GetCustomStencilMapIfNeeded( const TextureSize2D& size, const Span& outputs ); - Tr2GpuResourcePool::Texture GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs ); + Tr2GpuResourcePool::Texture GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); void UpdateGpuParticleSystem( Tr2RenderContext & renderContext ); @@ -122,13 +157,16 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : TriProjectionPtr m_projection; TriViewPtr m_view; + EveCameraPtr m_camera; ImageIO::PixelFormat m_internalPixelFormat = ImageIO::PIXEL_FORMAT_R16G16B16A16_FLOAT; Tr2UpscalingContextAL* m_upscalingContext = nullptr; + Tr2UpscalingAL::UpscalingInfo m_upscalingInfo; Settings m_settings; + Tr2ReflectionProbePtr m_reflectionProbe; Tr2EffectPtr m_distortionEffect; PITr2SceneVector m_toolsScenes; @@ -167,6 +205,8 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : Tr2ProfileTimer shadows; Tr2ProfileTimer ssao; } m_timers; + + bool m_newRoute = false; }; TYPEDEF_BLUECLASS( EveSpaceSceneRenderDriver ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp b/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp index 802e44197..b3d7c1fbf 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp @@ -50,6 +50,15 @@ const Be::VarChooser TriRMChooser[] = { { 0 } }; +Be::VarChooser ReflectionQualityChooser[] = { + { "Disabled", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::Disabled ), "" }, + { "Low", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::Low ), "" }, + { "Medium", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::Medium ), "" }, + { "High", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::High ), "" }, + { "Ultra", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::Ultra ), "" }, + { 0 } +}; + } extern Be::VarChooser EveVisualizerChooser[]; extern const Be::VarChooser Tr2VolumetricQuality_Chooser[]; @@ -59,6 +68,7 @@ BLUE_REGISTER_ENUM_EX( "EveSpaceSceneRenderDriverAntiAliasingQuality", EveSpaceS BLUE_REGISTER_ENUM_EX( "EveSpaceSceneRenderDriverAmbientOcclusionQuality", EveSpaceSceneRenderDriver::AmbientOcclusionQuality, AmbientOcclusionQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); BLUE_REGISTER_ENUM_EX( "ShadowQuality", ShadowQuality, ShadowQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); BLUE_REGISTER_ENUM_EX( "LightingQuality", LightingQuality, LightingQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); +BLUE_REGISTER_ENUM_EX( "ReflectionQuality", EveSpaceSceneRenderDriver::ReflectionQuality, ReflectionQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); @@ -76,6 +86,18 @@ const Be::ClassInfo* EveSpaceSceneRenderDriver::ExposeToBlue() "", Be::READWRITE ) + MAP_ATTRIBUTE( + "newRoute", + m_newRoute, + "", + Be::READWRITE ) + + MAP_ATTRIBUTE( + "reflectionProbe", + m_reflectionProbe, + "", + Be::READWRITE ) + MAP_ATTRIBUTE( "enableRendering", m_enableRendering, @@ -159,6 +181,13 @@ const Be::ClassInfo* EveSpaceSceneRenderDriver::ExposeToBlue() Be::READWRITE | Be::ENUM, ShadowQualityChooser ) + MAP_ATTRIBUTE_WITH_CHOOSER( + "reflectionQuality", + m_settings.reflectionQuality, + "Reflection quality setting. One of trinity.EveSpaceSceneRenderDriverReflectionQuality enum", + Be::READWRITE | Be::ENUM, + ReflectionQualityChooser ) + MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_settings.lightingQuality, diff --git a/trinity/PostProcess/Tr2PostProcessRenderer.cpp b/trinity/PostProcess/Tr2PostProcessRenderer.cpp index c603241c3..6bac9526d 100644 --- a/trinity/PostProcess/Tr2PostProcessRenderer.cpp +++ b/trinity/PostProcess/Tr2PostProcessRenderer.cpp @@ -644,6 +644,18 @@ void Tr2PostProcessRenderer::SetPostProcessingQuality( PostProcess::Quality qual m_quality = quality; } +Matrix Tr2PostProcessRenderer::GetJitter(uint64_t frameIndex, uint32_t renderWidth, uint32_t renderHeight) const +{ + const Vector2 samplingPatterns[] = { Vector2( .125f, -.375f ), + Vector2( -.125f, .375f ), + Vector2( .375f, .125f ), + Vector2( -.375f, -.125f ) }; + + auto samplingIndex = frameIndex % ( sizeof( samplingPatterns ) / sizeof( samplingPatterns[0] ) ); + const auto& sample = samplingPatterns[samplingIndex]; + return TranslationMatrix( Vector3( 2.0f * sample.x / static_cast( renderWidth ), 2.0f * sample.y / static_cast( renderHeight ), 0.f ) ); +} + void Tr2PostProcessRenderer::Execute( const Tr2TextureAL& destination, diff --git a/trinity/PostProcess/Tr2PostProcessRenderer.h b/trinity/PostProcess/Tr2PostProcessRenderer.h index f02aae6cd..2cdefcc9d 100644 --- a/trinity/PostProcess/Tr2PostProcessRenderer.h +++ b/trinity/PostProcess/Tr2PostProcessRenderer.h @@ -134,6 +134,7 @@ BLUE_CLASS( Tr2PostProcessRenderer ) : PostProcess::Quality GetPostProcessingQuality() const; void SetPostProcessingQuality( PostProcess::Quality quality ); + Matrix GetJitter( uint64_t frameIndex, uint32_t renderWidth, uint32_t renderHeight ) const; private: struct DownsampleData From 5171744faf18e8361194b9abc53a522245a0b72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Fri, 16 Oct 2026 15:58:04 +0000 Subject: [PATCH 02/20] Unify space scene view and reflection Unify space scene view rendering and drive reflection faces through it Collapse RenderOld/RenderNew into a single RenderView that renders one view (the main view or a reflection probe face) and returns its targets. Execute now does the per-frame work (settings, light manager, render target push/pop, post process, 3D UI, tools, FPS) once. m_newRoute only chooses who renders the reflection faces: the driver through RenderView, or the scene's RenderReflectionPass from within the main view. Replace the main-view checks in the scene and the driver with explicit SceneRender::ViewInfo flags (sceneObjects, temporal, reflection, subsurfaceScattering, gpuParticles, lensflares), built by ViewInfo::MainView and ViewInfo::ReflectionFace. RenderInfo now carries the ViewInfo, so distortion is stored in one place instead of two, and a FeatureSet::background and overlay flags gate the background and overlay render nodes. GatherBatches is split into GatherSceneObjectRenderables and GatherReflectionRenderables, and reflection shadows and reflection fog move into RenderReflectionShadows and RegisterReflectionFog. Share the reflection rules between the old and new paths: the reflection-in-reflection intensity damping, the reflection shadow setup, and the probe's finishing step (Tr2ReflectionProbe::FinishFaces, now also used by EndRenderPass, replacing the Filter overload and AdvanceFrame). The volumetrics reflection flag is set per view in BeginView instead of being set and reset in separate places. The main view now also honours the caller's named outputs on the new route. --- trinity/Eve/EveSpaceScene.cpp | 442 +++++++++++------ trinity/Eve/EveSpaceScene.h | 87 +++- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 551 +++++++--------------- trinity/Eve/EveSpaceSceneRenderDriver.h | 30 +- trinity/Tr2ReflectionProbe.cpp | 91 +++- trinity/Tr2ReflectionProbe.h | 14 +- 6 files changed, 673 insertions(+), 542 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index 406e53b2c..7de132429 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1257,28 +1257,28 @@ bool EveSpaceScene::RenderDistortionBatches( BatchMap& batches, const Tr2Texture // -------------------------------------------------------------------------------------- // Description: -// Set up rendering states, frustum and gather all batches. +// Work that does not depend on the view being rendered. Safe to call before every +// view; only the first call in a frame does anything, so the frame's first (full) +// Update has already run by then. // -------------------------------------------------------------------------------------- -void EveSpaceScene::BeginRender( bool enableDistortion, Tr2RenderContext& renderContext ) +void EveSpaceScene::BeginFrame( Tr2RenderContext& renderContext ) { - TRINITY_STATS_ZONE( __FUNCTION__ ); - if( !m_display ) { return; } - renderContext.AddGpuMarker( __FUNCTION__ ); - - if( m_visualizeMethod != VM_NONE ) + const uint64_t frame = renderContext.GetPrimaryRenderContextPointer()->GetRecordingFrameNumber(); + if( frame == m_lastBeginFrame ) { - if( !m_visualizerEffects[m_visualizeMethod].effect ) - { - m_visualizerEffects[m_visualizeMethod].effect.CreateInstance(); - const char* path = VISUALIZER_EFFECT_PATH[m_visualizeMethod]; - m_visualizerEffects[m_visualizeMethod].effect->SetEffectPathName( path ); - } + return; } + m_lastBeginFrame = frame; + + TRINITY_STATS_ZONE( __FUNCTION__ ); + renderContext.AddGpuMarker( __FUNCTION__ ); + + EnsureVisualizerEffect(); if( m_shLightingManager ) { @@ -1289,98 +1289,187 @@ void EveSpaceScene::BeginRender( bool enableDistortion, Tr2RenderContext& render { m_dataTextureMgr->SetVariables(); } - - m_velocityMapDirty = false; + ResolveLightingOverrides(); + + // fog settings and the environment map accumulate over time, so they must only advance once per frame + if( m_volumetricsRenderer ) { - std::vector overrides; - m_componentRegistry->ProcessComponents( [&overrides]( IEveLightingOverride* component ) -> void { - overrides.push_back( component->GetOverrides() ); - } ); - sort( begin( overrides ), end( overrides ), []( const auto& a, const auto& b ) { - return a.priority > b.priority; - } ); + m_volumetricsRenderer->UpdateFogSettings( *m_componentRegistry, m_updateContext ); + UpdateVariableStore(); + m_volumetricsRenderer->UpdateFogEnvironmentMap( renderContext ); + } +} - IEveLightingOverride::OverrideInfo baseline; - baseline.priority = (PostProcessEnums::Priority)-1; - baseline.intensity = 1; - auto sunColor = m_useSunColorWithDynamicLights && g_eveSpaceSceneDynamicLighting ? m_sunColorWithDynamicLights : m_sunColor; - baseline.value.sunIntensity = std::max( { sunColor.r, sunColor.g, sunColor.b } ); - if( baseline.value.sunIntensity != 0 ) - { - baseline.value.sunColor = sunColor * ( 1.f / baseline.value.sunIntensity ); - } - else - { - baseline.value.sunColor = sunColor; - } - baseline.value.backgroundIntensity = m_nebulaIntensity; - baseline.value.reflectionIntensity = m_reflectionIntensity; - overrides.push_back( baseline ); - auto over = SimplePriorityBlend( overrides ); - m_currentSunColor = over.sunColor * over.sunIntensity; - m_currentNebulaIntensity = over.backgroundIntensity; - m_currentReflectionIntensity = over.reflectionIntensity; +// -------------------------------------------------------------------------------------- +// Description: +// Per-view setup: lighting values, states, and frustum dependent gathering of batches +// and lights. Called for the main view and for each reflection face, after BeginFrame. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::BeginView( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) +{ + TRINITY_STATS_ZONE( __FUNCTION__ ); + + if( !m_display ) + { + return; } + renderContext.AddGpuMarker( __FUNCTION__ ); + + m_velocityMapDirty = false; + + ApplyViewLighting( view ); + + // Set for every view rather than set and reset in pairs, so a view never inherits + // the previous view's value. It is read whenever the per-frame data is populated. if( m_volumetricsRenderer ) { - m_volumetricsRenderer->UpdateFogSettings( *m_componentRegistry, m_updateContext ); - UpdateVariableStore(); - m_volumetricsRenderer->UpdateFogEnvironmentMap( renderContext ); + m_volumetricsRenderer->SetIsReflectionProbe( view.reflection ); } - renderContext.m_esm.BeginManagedRendering(); + renderContext.m_esm.BeginManagedRendering( view.cullMode ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_OPAQUE ); - GatherBatches( enableDistortion, renderContext ); + GatherBatches( view, renderContext ); - if( m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows ) + // reflection views use the reflection shadow map, which is not raytraced + if( !view.reflection && m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows ) { PrepareRaytracedShadows( renderContext ); } UpdateVariableStore(); - if( auto lightManager = Tr2LightManager::GetInstance() ) + GatherDynamicLights( renderContext ); + PrepareLensflares(); + + PopulatePerFramePSData( m_perFramePS, renderContext ); + PopulatePerFrameVSData( m_perFrameVS, renderContext ); + ApplyPerFrameData( renderContext ); +} + +void EveSpaceScene::EnsureVisualizerEffect() +{ + if( m_visualizeMethod != VM_NONE && !m_visualizerEffects[m_visualizeMethod].effect ) + { + m_visualizerEffects[m_visualizeMethod].effect.CreateInstance(); + const char* path = VISUALIZER_EFFECT_PATH[m_visualizeMethod]; + m_visualizerEffects[m_visualizeMethod].effect->SetEffectPathName( path ); + } +} + +// -------------------------------------------------------------------------------------- +// Description: +// Blend the lighting overrides into the per-frame base values. Views derive their +// m_current* values from these in ApplyViewLighting. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::ResolveLightingOverrides() +{ + std::vector overrides; + m_componentRegistry->ProcessComponents( [&overrides]( IEveLightingOverride* component ) -> void { + overrides.push_back( component->GetOverrides() ); + } ); + sort( begin( overrides ), end( overrides ), []( const auto& a, const auto& b ) { + return a.priority > b.priority; + } ); + + IEveLightingOverride::OverrideInfo baseline; + baseline.priority = (PostProcessEnums::Priority)-1; + baseline.intensity = 1; + auto sunColor = m_useSunColorWithDynamicLights && g_eveSpaceSceneDynamicLighting ? m_sunColorWithDynamicLights : m_sunColor; + baseline.value.sunIntensity = std::max( { sunColor.r, sunColor.g, sunColor.b } ); + if( baseline.value.sunIntensity != 0 ) { - TRINITY_STATS_SCOPED_TIME( gatherDynamicLights ); + baseline.value.sunColor = sunColor * ( 1.f / baseline.value.sunIntensity ); + } + else + { + baseline.value.sunColor = sunColor; + } + baseline.value.backgroundIntensity = m_nebulaIntensity; + baseline.value.reflectionIntensity = m_reflectionIntensity; + overrides.push_back( baseline ); - lightManager->SetShadowQuality( m_shadowQuality, renderContext.GetPrimaryRenderContextPointer()->GetRecordingFrameNumber() ); - lightManager->Clear( renderContext ); - lightManager->SetFrustum( m_updateContext.GetFrustum() ); - lightManager->AdjustLightCutoff( m_updateContext.GetLodFactor() ); + auto over = SimplePriorityBlend( overrides ); + m_baseSunColor = over.sunColor * over.sunIntensity; + m_baseNebulaIntensity = over.backgroundIntensity; + m_baseReflectionIntensity = over.reflectionIntensity; +} - auto lightOwners = m_componentRegistry->GetComponents(); +// -------------------------------------------------------------------------------------- +// Description: +// Lower the reflection intensity for the objects rendered into the reflection (so the +// reflections in the reflections don't get brighter and brighter). We cap it at 0.8 +// for no good reason, just felt like a good number to cap it at. +// -------------------------------------------------------------------------------------- +static float GetReflectionIntensityInReflections( float reflectionIntensity ) +{ + if( reflectionIntensity == 0.0f ) + { + return reflectionIntensity; + } + return min( 0.8f, 1.0f / ( reflectionIntensity * reflectionIntensity ) ); +} - Tr2ParallelFor( Tr2BlockedRange( 0, lightOwners.size(), 20 ), [&]( Tr2BlockedRange range ) { - for( auto i = range.begin(); i != range.end(); ++i ) - { - ITr2LightOwnerPtr lightOwner = lightOwners[i]; - lightOwner->GetLights( *lightManager ); - } - } ); +// -------------------------------------------------------------------------------------- +// Description: +// Derive the lighting values for the current view from the per-frame base values. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::ApplyViewLighting( const SceneRender::ViewInfo& view ) +{ + m_currentSunColor = m_baseSunColor; + m_currentNebulaIntensity = m_baseNebulaIntensity; + m_currentReflectionIntensity = view.reflection ? GetReflectionIntensityInReflections( m_baseReflectionIntensity ) : m_baseReflectionIntensity; +} - lightManager->ResolveLightData(); +// -------------------------------------------------------------------------------------- +// Description: +// Gather the dynamic lights visible in the current frustum. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::GatherDynamicLights( Tr2RenderContext& renderContext ) +{ + auto lightManager = Tr2LightManager::GetInstance(); + if( !lightManager ) + { + return; } + TRINITY_STATS_SCOPED_TIME( gatherDynamicLights ); + + lightManager->SetShadowQuality( m_shadowQuality, renderContext.GetPrimaryRenderContextPointer()->GetRecordingFrameNumber() ); + lightManager->Clear( renderContext ); + lightManager->SetFrustum( m_updateContext.GetFrustum() ); + lightManager->AdjustLightCutoff( m_updateContext.GetLodFactor() ); + + auto lightOwners = m_componentRegistry->GetComponents(); + + Tr2ParallelFor( Tr2BlockedRange( 0, lightOwners.size(), 20 ), [&]( Tr2BlockedRange range ) { + for( auto i = range.begin(); i != range.end(); ++i ) + { + ITr2LightOwnerPtr lightOwner = lightOwners[i]; + lightOwner->GetLights( *lightManager ); + } + } ); + + lightManager->ResolveLightData(); +} + +void EveSpaceScene::PrepareLensflares() +{ // the lensflares need a special pre-render update for( auto it = m_lensflares.cbegin(); it != m_lensflares.cend(); ++it ) { ( *it )->PrepareRender( m_updateContext.GetFrustum() ); } - - PopulatePerFramePSData( m_perFramePS, renderContext ); - PopulatePerFrameVSData( m_perFrameVS, renderContext ); - ApplyPerFrameData( renderContext ); } // -------------------------------------------------------------------------------------- // Description: // Gather all batches into the m_primaryBatches BatchMap. // -------------------------------------------------------------------------------------- -void EveSpaceScene::GatherBatches( bool includeDistortions, Tr2RenderContext& renderContext ) +void EveSpaceScene::GatherBatches( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -1390,11 +1479,65 @@ void EveSpaceScene::GatherBatches( bool includeDistortions, Tr2RenderContext& re Tr2RenderableSortList transparentObjects; const Matrix& identity = IdentityMatrix(); + // Reflection views only get the dynamic objects once the probe is ready for them + const bool gatherObjects = view.sceneObjects || ( m_dynamicObjectReflectionEnabled && m_reflectionProbe && m_reflectionProbe->ReadyForDynamicObjectReflections() ); + { TRINITY_STATS_ZONE( "UpdateVisibility" ); - Tr2ParallelDo( m_objects.begin(), m_objects.end(), [&]( IEveSpaceObject2* obj ) { - obj->UpdateVisibility( m_updateContext, identity ); + if( gatherObjects ) + { + Tr2ParallelDo( m_objects.begin(), m_objects.end(), [&]( IEveSpaceObject2* obj ) { + obj->UpdateVisibility( m_updateContext, identity ); + } ); + } + + Tr2ParallelDo( m_planets.begin(), m_planets.end(), [&]( EvePlanet* obj ) { + obj->UpdateZOnlyVisibility( m_updateContext ); } ); + } + + if( view.sceneObjects ) + { + GatherSceneObjectRenderables( allObjects, renderables, renderContext ); + } + else if( gatherObjects ) + { + GatherReflectionRenderables( renderables ); + } + + UpdateQuadRenderer( m_updateContext.GetFrustum(), allObjects, renderContext ); + Tr2QuadRenderer::Instance()->GetBatches( TRIBATCHTYPE_OPAQUE, m_primaryBatches[TRIBATCHTYPE_OPAQUE] ); + Tr2QuadRenderer::Instance()->GetBatches( TRIBATCHTYPE_ADDITIVE, m_primaryBatches[TRIBATCHTYPE_ADDITIVE] ); + + GetAllBatchesFromRenderables( renderables, transparentObjects, view.distortion, m_primaryBatches, view.reason ); + PrepareTransparentBatch( transparentObjects, m_primaryBatches, view.reason ); + + if( gatherObjects ) + { + m_instancedMeshManager->CollectMeshes( *m_componentRegistry ); + + // passing the reason filters out instanced meshes that should not be reflected + m_instancedMeshManager->GetBatches( m_updateContext.GetFrustum(), m_updateContext.GetInvLodFactor(), { { TRIBATCHTYPE_OPAQUE, *m_primaryBatches[TRIBATCHTYPE_OPAQUE] }, { TRIBATCHTYPE_DECAL, *m_primaryBatches[TRIBATCHTYPE_DECAL] }, { TRIBATCHTYPE_ADDITIVE, *m_primaryBatches[TRIBATCHTYPE_ADDITIVE] }, { TRIBATCHTYPE_DEPTH, *m_primaryBatches[TRIBATCHTYPE_DEPTH] }, { TRIBATCHTYPE_DISTORTION, *m_primaryBatches[TRIBATCHTYPE_DISTORTION] } }, view.reason ); + + m_instancedMeshManager->ReportUsedScreenSizes(); + } + + FinalizeBatches( m_primaryBatches ); + + UpdateShLighting( allObjects ); +} + +// -------------------------------------------------------------------------------------- +// Description: +// Gather the renderables of the scene's objects, camera attachments and static +// particles, updating the impostors on the way. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::GatherSceneObjectRenderables( std::vector& allObjects, std::vector& renderables, Tr2RenderContext& renderContext ) +{ + const Matrix& identity = IdentityMatrix(); + + { + TRINITY_STATS_ZONE( "UpdateVisibility" ); m_cameraAttachmentParent->SetTransform( Tr2Renderer::GetInverseViewTransform() ); m_cameraAttachmentParent->UpdateSyncronous( m_updateContext ); @@ -1405,10 +1548,6 @@ void EveSpaceScene::GatherBatches( bool includeDistortions, Tr2RenderContext& re staticParticles->UpdateVisibility( m_updateContext ); } ); - Tr2ParallelDo( m_planets.begin(), m_planets.end(), [&]( EvePlanet* obj ) { - obj->UpdateZOnlyVisibility( m_updateContext ); - } ); - // until we have proper support for multiple lensflares we just do it in a list... for( auto& lensflare : m_lensflares ) { @@ -1455,23 +1594,23 @@ void EveSpaceScene::GatherBatches( bool includeDistortions, Tr2RenderContext& re { ( *it )->GetRenderables( m_updateContext.GetFrustum(), renderables ); } +} - UpdateQuadRenderer( m_updateContext.GetFrustum(), allObjects, renderContext ); - Tr2QuadRenderer::Instance()->GetBatches( TRIBATCHTYPE_OPAQUE, m_primaryBatches[TRIBATCHTYPE_OPAQUE] ); - Tr2QuadRenderer::Instance()->GetBatches( TRIBATCHTYPE_ADDITIVE, m_primaryBatches[TRIBATCHTYPE_ADDITIVE] ); - - GetAllBatchesFromRenderables( renderables, transparentObjects, includeDistortions, m_primaryBatches ); - PrepareTransparentBatch( transparentObjects, m_primaryBatches ); - - m_instancedMeshManager->CollectMeshes( *m_componentRegistry ); - - m_instancedMeshManager->GetBatches( m_updateContext.GetFrustum(), m_updateContext.GetInvLodFactor(), { { TRIBATCHTYPE_OPAQUE, *m_primaryBatches[TRIBATCHTYPE_OPAQUE] }, { TRIBATCHTYPE_DECAL, *m_primaryBatches[TRIBATCHTYPE_DECAL] }, { TRIBATCHTYPE_ADDITIVE, *m_primaryBatches[TRIBATCHTYPE_ADDITIVE] }, { TRIBATCHTYPE_DEPTH, *m_primaryBatches[TRIBATCHTYPE_DEPTH] }, { TRIBATCHTYPE_DISTORTION, *m_primaryBatches[TRIBATCHTYPE_DISTORTION] } } ); - - m_instancedMeshManager->ReportUsedScreenSizes(); - - FinalizeBatches( m_primaryBatches ); - - UpdateShLighting( allObjects ); +// -------------------------------------------------------------------------------------- +// Description: +// Gather the visible renderables from the component registry. Each renderable decides +// in GetBatches (via TR2RENDERREASON_REFLECTION / ShouldReflect) whether it shows up. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::GatherReflectionRenderables( std::vector& renderables ) +{ + renderables.reserve( m_componentRegistry->ComponentCount() ); + m_componentRegistry->ProcessComponents( + [&]( ITr2Renderable* renderable ) -> void { + if( renderable->IsVisible( m_updateContext ) ) + { + renderables.push_back( renderable ); + } + } ); } void EveSpaceScene::PrepareRaytracedShadows( Tr2RenderContext& renderContext ) @@ -1725,14 +1864,8 @@ void EveSpaceScene::RenderReflectionPass( Tr2GpuResourcePool& gpuResourcePool, T m_volumetricsRenderer->SetIsReflectionProbe( false ); } ); - // lower the reflection intensity for the objects rendered into the reflection - // (so the reflections in the reflections don't get brighter and brighter) - // we cap it at 0.8 for no good reason, just felt like a good number to cap the reflection intensity in the reflections auto tmp = m_currentReflectionIntensity; - if( m_currentReflectionIntensity != 0.0f ) - { - m_currentReflectionIntensity = min( 0.8f, 1.0f / ( m_currentReflectionIntensity * m_currentReflectionIntensity ) ); - } + m_currentReflectionIntensity = GetReflectionIntensityInReflections( m_currentReflectionIntensity ); { // Override DepthMap in the variable store @@ -1883,28 +2016,7 @@ void EveSpaceScene::RenderReflectionPass( Tr2GpuResourcePool& gpuResourcePool, T renderContext.RenderBatches( m_secondaryBatches[TRIBATCHTYPE_OPAQUE], BlueSharedString( "Depth" ) ); renderContext.RenderBatches( m_secondaryBatches[TRIBATCHTYPE_DEPTH], BlueSharedString( "Depth" ) ); - EveSpaceScene::ShadowResources shadowResources; - - if( m_enableShadows && m_shadowQuality != ShadowQuality::SHADOW_DISABLED && m_reflectionShadowMap ) - { - // We have so many ways to turn off shadows... - bool reallyHaveShadows = false; - if( m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED ) - { - reallyHaveShadows = m_rtManager != nullptr; - } - else - { - reallyHaveShadows = m_cascadedShadowMap != nullptr; - } - - if( reallyHaveShadows ) - { - renderContext.m_esm.SetInvertedCullMode( false ); - shadowResources = SetupCascadedShadows( TR2RENDERREASON_REFLECTION, *m_reflectionShadowMap, currentFrustum, m_reflectionProbe->GetDepthBuffer( i ), gpuResourcePool, renderContext ); - renderContext.m_esm.SetInvertedCullMode( true ); - } - } + EveSpaceScene::ShadowResources shadowResources = RenderReflectionShadows( m_reflectionProbe->GetDepthBuffer( i ), gpuResourcePool, renderContext ); // TODO: raytraced shadows here RegisterWithVariableStore( shadowResources, gpuResourcePool ); @@ -1954,7 +2066,7 @@ void EveSpaceScene::RenderReflectionPass( Tr2GpuResourcePool& gpuResourcePool, T // Returns: // boolean indicating whether background distortion is required. // -------------------------------------------------------------------------------------- -bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, Tr2RenderContext& renderContext ) +bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -2013,7 +2125,7 @@ bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr else { renderContext.RenderPassHint( { Tr2LoadAction::LOAD, Tr2StoreAction::STORE }, { Tr2LoadAction::LOAD, Tr2StoreAction::STORE } ); - hasBackgroundDistortionBatches = RenderBackgroundPassObjects( depthMap, distortionMap, renderContext, BACKGROUND_RENDER_COLOR ); + hasBackgroundDistortionBatches = RenderBackgroundPassObjects( depthMap, distortionMap, renderContext, view.reflection ? BACKGROUND_RENDER_REFLECTION : BACKGROUND_RENDER_COLOR ); } if( !m_planets.empty() ) { @@ -2150,7 +2262,7 @@ bool EveSpaceScene::RenderBackgroundPassObjects( const Tr2TextureAL& depthMap, c // Description: // Render opaque objects to populate a readable depth stencil. // -------------------------------------------------------------------------------------- -void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName ) +void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -2159,7 +2271,7 @@ void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2Text return; } - renderContext.m_esm.BeginManagedRendering(); + renderContext.m_esm.BeginManagedRendering( view.cullMode ); { // Update mesh morphs auto& meshMorphs = m_componentRegistry->GetComponents(); @@ -2567,13 +2679,40 @@ void EveSpaceScene::FinishRenderingShadowMapForLights( Tr2RenderContext& renderC ApplyPerFrameData( renderContext ); } -EveSpaceScene::ShadowResources EveSpaceScene::RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ) +// -------------------------------------------------------------------------------------- +// Description: +// Reflection views get cascaded shadows from the reflection shadow map, whenever the +// main view has shadows. +// -------------------------------------------------------------------------------------- +EveSpaceScene::ShadowResources EveSpaceScene::RenderReflectionShadows( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ) +{ + // We have so many ways to turn off shadows... + const bool haveShadows = m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED ? m_rtManager != nullptr : m_cascadedShadowMap != nullptr; + if( !m_enableShadows || !m_reflectionShadowMap || !haveShadows || m_shadowQuality == ShadowQuality::SHADOW_DISABLED ) + { + return {}; + } + + // the shadow map is not rendered into the cubemap, so it must not use the inverted cull mode + bool prevCullInversion = renderContext.m_esm.IsCullModeInverted(); + renderContext.m_esm.SetInvertedCullMode( false ); + ShadowResources result = SetupCascadedShadows( TR2RENDERREASON_REFLECTION, *m_reflectionShadowMap, m_updateContext.GetFrustum(), depthMap, gpuResourcePool, renderContext ); + renderContext.m_esm.SetInvertedCullMode( prevCullInversion ); + return result; +} + +EveSpaceScene::ShadowResources EveSpaceScene::RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) { if( !m_display || !m_enableShadows ) { return {}; } + if( view.reflection ) + { + return RenderReflectionShadows( depthMap, gpuResourcePool, renderContext ); + } + EveSpaceScene::ShadowResources result; if( m_cascadedShadowMap && ( m_shadowQuality == ShadowQuality::SHADOW_LOW || m_shadowQuality == ShadowQuality::SHADOW_HIGH ) ) { @@ -2645,13 +2784,14 @@ bool EveSpaceScene::RenderMainPass( const Tr2TextureAL& velocityMap, const Tr2TextureAL& opaqueColorMap, Tr2GpuResourcePool& gpuResourcePool, + const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); bool hasForegroundDistortionBatches = false; - renderContext.m_esm.BeginManagedRendering(); + renderContext.m_esm.BeginManagedRendering( view.cullMode ); if( !m_display ) { @@ -2701,14 +2841,24 @@ bool EveSpaceScene::RenderMainPass( GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", opaqueColorMap ); } - m_sssss->SetupScreenSpaceSubSurfaceScattering( renderContext, m_primaryBatches[TRIBATCHTYPE_OPAQUE], colorMap, opaqueColorMap, depthMap, gpuResourcePool ); + if( view.subsurfaceScattering ) + { + m_sssss->SetupScreenSpaceSubSurfaceScattering( renderContext, m_primaryBatches[TRIBATCHTYPE_OPAQUE], colorMap, opaqueColorMap, depthMap, gpuResourcePool ); + } Tr2Renderer::SetProjectionTransform( m_jitteredProjection ); PopulateAndApplyPerFrameData( renderContext ); - auto [froxelFog, volumetricSlices] = RenderVolumetrics( depthMap, gpuResourcePool, renderContext ); - GlobalStore().RegisterVariable( "EveSceneFogVolumeMap", volumetricSlices ); - GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", froxelFog ); + if( view.temporal ) + { + auto [froxelFog, volumetricSlices] = RenderVolumetrics( depthMap, gpuResourcePool, renderContext ); + GlobalStore().RegisterVariable( "EveSceneFogVolumeMap", volumetricSlices ); + GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", froxelFog ); + } + else + { + RegisterReflectionFog( depthMap, gpuResourcePool, renderContext ); + } RenderTransparentBatches( m_primaryBatches, renderContext ); if( distortionMap.IsValid() ) @@ -2717,7 +2867,7 @@ bool EveSpaceScene::RenderMainPass( } //GPU particles - if( GetGpuParticleSystem() ) + if( view.gpuParticles && GetGpuParticleSystem() ) { GetGpuParticleSystem()->Render( renderContext ); } @@ -2731,6 +2881,22 @@ bool EveSpaceScene::RenderMainPass( return hasForegroundDistortionBatches; } +// -------------------------------------------------------------------------------------- +// Description: +// Non-temporal views use the reflection fog, so they don't disturb the main view's fog +// history. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::RegisterReflectionFog( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ) +{ + Tr2GpuResourcePool::Texture froxelFog; + if( m_volumetricsRenderer ) + { + froxelFog = m_volumetricsRenderer->RenderFogIntoReflectionMap( renderContext, gpuResourcePool, depthMap.GetWidth(), depthMap.GetHeight(), m_sunData.DirWorld, m_currentSunColor, m_updateContext.GetOrigin(), Tr2Renderer::GetViewTransform(), Tr2Renderer::GetReversedDepthProjectionTransform() ); + } + GlobalStore().RegisterVariable( "EveSceneFogVolumeMap", Tr2VolumetricsRenderer::GetEmptyFogTexture( gpuResourcePool ) ); + GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", froxelFog ); +} + void EveSpaceScene::RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ) { for( auto& lensflare : m_lensflares ) @@ -2744,7 +2910,7 @@ void EveSpaceScene::RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, // Description: // Render lensflares. Clear primary batches and frame dependant object lists. // -------------------------------------------------------------------------------------- -void EveSpaceScene::EndRender( Tr2RenderContext& renderContext ) +void EveSpaceScene::EndRender( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -2757,7 +2923,7 @@ void EveSpaceScene::EndRender( Tr2RenderContext& renderContext ) Tr2QuadRenderer::Instance()->DoneRendering( renderContext ); - renderContext.m_esm.BeginManagedRendering(); + renderContext.m_esm.BeginManagedRendering( view.cullMode ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_OPAQUE ); PopulatePerFramePSData( m_perFramePS, renderContext ); @@ -2789,7 +2955,7 @@ void EveSpaceScene::EndRender( Tr2RenderContext& renderContext ) } // lensflares - if( !m_lensflares.empty() ) + if( !m_lensflares.empty() && view.lensflares ) { GPU_REGION( renderContext, "Lens Flares" ); // lensflares @@ -2805,7 +2971,8 @@ void EveSpaceScene::EndRender( Tr2RenderContext& renderContext ) m_secondaryBatches[TRIBATCHTYPE_ADDITIVE], TRIBATCHTYPE_ADDITIVE, Tr2EffectStateManager::RM_ALPHA_ADDITIVE, - renderContext ); + renderContext, + view.reason ); renderContext.SetReadOnlyDepth( false ); } } @@ -2816,8 +2983,11 @@ void EveSpaceScene::EndRender( Tr2RenderContext& renderContext ) ClearBatches( m_primaryBatches ); // Store the view transform from this frame - m_viewLast = Tr2Renderer::GetViewTransform(); - m_projectionLast = m_projection; + if( view.temporal ) + { + m_viewLast = Tr2Renderer::GetViewTransform(); + m_projectionLast = m_projection; + } ClearVariableStore(); diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index e4c7a91d3..61af84574 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -96,6 +96,59 @@ BLUE_DECLARE_VECTOR( Tr2PostProcessAttributes ); class EveInstancedMeshManager; +namespace SceneRender +{ +// Describes the view being rendered and what it renders. Built by the render driver with +// MainView or ReflectionFace and passed to every per-view call; the scene reads these +// flags rather than deciding per call site what a kind of view gets.S +struct ViewInfo +{ + // Passed on to GetBatches, so renderables can filter themselves (e.g. ShouldReflect) + Tr2RenderReason reason = TR2RENDERREASON_NORMAL; + Tr2RenderContextEnum::CullMode cullMode = Tr2RenderContextEnum::CULLMODE_CW; + bool distortion = true; + // Gather the scene's objects, camera attachments, static particles and impostors. + // Otherwise only the reflectable renderables in the component registry are gathered. + bool sceneObjects = true; + // The view feeds the next frame: view history for reprojection, temporal volumetrics + // and lens flare occlusion queries + bool temporal = true; + // Render the scene as a reflection probe sees it: damped reflection intensity, + // reflection background and the reflection shadow map + bool reflection = false; + bool subsurfaceScattering = true; + bool gpuParticles = true; + bool lensflares = true; + + static ViewInfo MainView( bool distortion ); + static ViewInfo ReflectionFace(); +}; + +ViewInfo ViewInfo::MainView( bool distortion ) +{ + ViewInfo info; + info.distortion = distortion; + return info; +} + +ViewInfo ViewInfo::ViewInfo::ReflectionFace() +{ + ViewInfo view; + view.reason = TR2RENDERREASON_REFLECTION; + // Faces are rendered into a left handed cubemap, so the caller inverts the cull mode + // and the scene must not override it + view.cullMode = Tr2RenderContextEnum::CULLMODE_NONE; + view.distortion = false; + view.sceneObjects = false; + // faces must not disturb the main view's history + view.temporal = false; + view.reflection = true; + view.subsurfaceScattering = false; + view.gpuParticles = false; + view.lensflares = g_lensflaresInReflections && g_eveReflectionMode == EntityComponents::REFLECTION_SETTING_ULTRA; + return view; +} +} BLUE_CLASS( EveSpaceScene ) : @@ -132,7 +185,7 @@ BLUE_CLASS( EveSpaceScene ) : Tr2GpuResourcePool::Texture pointLightShadowDepth; }; - ShadowResources RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); + ShadowResources RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); bool RenderMainPass( const Tr2TextureAL& colorMap, const Tr2TextureAL& depthMap, @@ -140,19 +193,21 @@ BLUE_CLASS( EveSpaceScene ) : const Tr2TextureAL& velocityMap, const Tr2TextureAL& opaqueColorMap, Tr2GpuResourcePool& gpuResourcePool, + const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); void RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ); - void RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName = BlueSharedString( "Depth" ) ); + void RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName = BlueSharedString( "Depth" ) ); - bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, Tr2RenderContext& renderContext ); + bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); void RenderReflectionPass( Tr2GpuResourcePool & gpuResourcePool, Tr2RenderContext & renderContext ); - void BeginRender( bool enableDistortion, Tr2RenderContext& renderContext ); - void EndRender( Tr2RenderContext & renderContext ); + void BeginFrame( Tr2RenderContext& renderContext ); + void BeginView( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); + void EndRender( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); void Render3DUI( Tr2RenderContext & renderContext ); void PopulateAndApplyPerFrameData( Tr2RenderContext& renderContext ); void ApplyUpscalingToPerFrameData( uint32_t width, uint32_t height, Tr2RenderContext& renderContext ); - void GatherBatches( bool includeDistortions, Tr2RenderContext& renderContext ); + void GatherBatches( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); void PrepareRaytracedShadows( Tr2RenderContext & renderContext ); void SetRenderingInfo( Matrix jitterMatrix, float mipLodBias, float upscaling ); @@ -418,7 +473,7 @@ BLUE_CLASS( EveSpaceScene ) : EveUpdateContext m_updateContext; PEveDistanceFieldVector m_distanceFields; - // Primary batches, gathered in BeginRender and + // Primary batches, gathered in BeginView and // cleared in EndRender BatchMap m_primaryBatches; // Secondary batches used to render planets, stars, shadowed objects @@ -495,6 +550,12 @@ BLUE_CLASS( EveSpaceScene ) : Color m_currentSunColor; float m_currentNebulaIntensity; + // Lighting resolved once per frame in ResolveLightingOverrides. The m_current* values + // are derived from these per view in ApplyViewLighting. + Color m_baseSunColor = Color( 1.0f, 1.0f, 1.0f, 1.0f ); + float m_baseNebulaIntensity = 1.0f; + float m_baseReflectionIntensity = 1.0f; + float m_fogStart; // Depth at which fogging starts float m_fogEnd; // Depth at which fog does not get stronger float m_fogMax; // Maximum strength of fog, range [0,1], at m_fogEnd distance. @@ -600,6 +661,16 @@ BLUE_CLASS( EveSpaceScene ) : virtual void ReleaseResources( TriStorage s ); virtual bool OnPrepareResources(); + void EnsureVisualizerEffect(); + void ResolveLightingOverrides(); + void ApplyViewLighting( const SceneRender::ViewInfo& view ); + void GatherDynamicLights( Tr2RenderContext& renderContext ); + void PrepareLensflares(); + void GatherSceneObjectRenderables( std::vector& allObjects, std::vector& renderables, Tr2RenderContext& renderContext ); + void GatherReflectionRenderables( std::vector& renderables ); + ShadowResources RenderReflectionShadows( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); + void RegisterReflectionFog( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); + void UpdateShLighting( const std::vector& objectsNotReceivingShadow ); @@ -694,6 +765,8 @@ BLUE_CLASS( EveSpaceScene ) : // Frame index when Update was last called uint64_t m_lastUpdateFrame = std::numeric_limits::max(); + // Frame index when BeginFrame last did its work + uint64_t m_lastBeginFrame = std::numeric_limits::max(); public: // raytracing diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index c2328a285..31ccd87eb 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -409,10 +409,10 @@ void EveSpaceSceneRenderDriver::SetCameraToRenderer( Tr2RenderContext& renderCon Tr2Renderer::SetViewTransform( view->GetTransform() ); } -void EveSpaceSceneRenderDriver::SetCameraToRenderer( TriViewPtr view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const +void EveSpaceSceneRenderDriver::SetCameraToRenderer( const Matrix& view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const { projection->SetProjection( renderContext ); - Tr2Renderer::SetViewTransform( view->GetTransform() ); + Tr2Renderer::SetViewTransform( view ); } TextureSize2D EveSpaceSceneRenderDriver::GetRenderSize( const TextureSize2D& displaySize ) const @@ -449,77 +449,18 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina return; } - if( m_newRoute ) - { - RenderNew( destinations, outputs, realTime, simTime, rootTimer, renderContext ); - } - else - { - RenderOld( destinations, outputs, realTime, simTime, rootTimer, renderContext ); - } -} - -Matrix EveSpaceSceneRenderDriver::GetJitterMatrix( uint32_t renderWidth, uint32_t renderHeight, Tr2RenderContext& renderContext ) const -{ - Matrix jitterMatrix; - - if( m_upscalingContext ) - { - float jitterX = 0.0f; - float jitterY = 0.0f; - m_upscalingContext->GetJitter( jitterX, jitterY ); - jitterMatrix = TranslationMatrix( Vector3( jitterX, jitterY, 0 ) ); - } - else if( m_postProcess && m_scene && m_scene->GetPostProcess() && m_scene->GetPostProcess()->GetTaaIfAvailable() != nullptr ) - { - auto frameIndex = renderContext.GetPrimaryRenderContext().GetRenderedFrameNumber(); - jitterMatrix = m_postProcess->GetJitter( frameIndex, renderWidth, renderHeight ); - } - else - { - jitterMatrix = IdentityMatrix(); - } - - return jitterMatrix; -} - -float EveSpaceSceneRenderDriver::GetMipLodBias() const -{ - float mipLodBias = 0.0f; - USE_MAIN_THREAD_RENDER_CONTEXT(); - - if( m_scene ) - { - if( auto postprocess = m_scene->GetPostProcess() ) - { - mipLodBias += postprocess->GetMipLodBias(); - } - } - - if( m_upscalingContext ) - { - mipLodBias += m_upscalingInfo.mipLevelBias; - } - return mipLodBias; -} - -void EveSpaceSceneRenderDriver::RenderNew( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) -{ GlobalStore().RegisterVariable( "EveSpaceSceneReflectionCorrectionLookupTable", m_reflectionCorrectionEnabled ? m_reflectionCorrectionMap : m_blackReflectionCorrectionMap ); - - const TextureSize2D displaySize = destinations.data->GetDesc(); - const TextureSize2D renderSize = GetRenderSize( displaySize ); m_scene->m_viewLast = m_viewLast; m_scene->m_projectionLast = m_projectionLast; - + if( m_camera ) { m_camera->Update( simTime ); } - auto projection = m_camera ? m_camera->GetProjection() : m_projection; - auto view = m_camera ? m_camera->GetViewMatrix() : m_view; + const TextureSize2D displaySize = destinations.data->GetDesc(); + const TextureSize2D renderSize = GetRenderSize( displaySize ); PropagateSettings(); @@ -528,46 +469,45 @@ void EveSpaceSceneRenderDriver::RenderNew( const Span& desti renderContext.m_esm.SetInvertedDepthTest( true ); ON_BLOCK_EXIT( [&] { renderContext.m_esm.SetInvertedDepthTest( false ); } ); - // Here we would render the reflection pass - - // render the main scene - static const FeatureSet mainFeatures = { true, true, true, true, true, true, true }; - - SetCameraToRenderer( renderContext ); - float upscalingAmount = m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f; - auto jitterMatrix = GetJitterMatrix( renderSize.width, renderSize.height, renderContext ); - auto mipLodBias = GetMipLodBias(); + renderContext.m_esm.PushRenderTarget( 0 ); + renderContext.m_esm.PushRenderTarget( 1 ); + renderContext.m_esm.PushDepthStencilBuffer(); + ON_BLOCK_EXIT( [&] { + renderContext.m_esm.PopDepthStencilBuffer(); + renderContext.m_esm.PopRenderTarget( 1 ); + renderContext.m_esm.PopRenderTarget( 0 ); + } ); - RenderInfo renderInfo = { view, projection, jitterMatrix, mipLodBias, upscalingAmount, GetRenderSize( destinations.data->GetDesc() ), mainFeatures, Tr2RenderReason::TR2RENDERREASON_NORMAL }; + PrepareLightManager(); - std::vector tempOutputs = { - { BlueSharedString( "DepthMap" ), {} }, - { BlueSharedString( "BackBuffer" ), {} }, - { BlueSharedString( "VelocityMap" ), {} }, - { BlueSharedString( "OpaqueColorMap" ), {} } + // The new route renders the reflection faces through RenderView. The old route lets + // the scene render them from within the main view (see RenderView). + if( m_newRoute ) + { + TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); + RenderReflectionFaces( realTime, simTime, rootTimer, renderContext ); + } + + const RenderInfo mainView = { + m_camera ? m_camera->GetViewMatrix()->GetTransform() : m_view->GetTransform(), + m_camera ? m_camera->GetProjection() : m_projection, + GetJitterMatrix( renderSize.width, renderSize.height, renderContext ), + GetMipLodBias(), + m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f, + renderSize, + FeatureSet{}, + SceneRender::ViewInfo::MainView( m_settings.enableDistortion ) }; - const Span tempOutputsSpan = { tempOutputs.data(), tempOutputs.size() }; + auto colorTarget = m_gpuResourcePool.GetTempTexture( "customBackBuffer", renderSize, m_internalPixelFormat, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); + ViewTargets targets = RenderView( mainView, std::move( colorTarget ), outputs, realTime, simTime, rootTimer, renderContext ); renderContext.m_esm.SetRenderTarget( 0, *destinations.data ); renderContext.m_esm.SetDepthStencilBuffer( {} ); - GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", Tr2TextureAL{} ); - - RenderView( renderInfo, destinations, tempOutputsSpan, realTime, simTime, rootTimer, renderContext ); - { - auto depthBuffer = FindNamedOutput( tempOutputsSpan, "DepthMap" ); - auto velocityMap = FindNamedOutput( tempOutputsSpan, "VelocityMap" ); - auto customBackBuffer = FindNamedOutput( tempOutputsSpan, "BackBuffer" ); - auto opaqueBackBuffer = FindNamedOutput( tempOutputsSpan, "OpaqueColorMap" ); TimeSection postProcessSection( m_timers.postProcess, "PostProcess", rootTimer, renderContext ); - m_postProcess->Execute( *destinations.data, std::move( customBackBuffer->texture ), depthBuffer->texture, std::move( velocityMap->texture ), std::move( opaqueBackBuffer->texture ), m_scene, m_upscalingContext, m_gpuResourcePool, renderContext ); + m_postProcess->Execute( *destinations.data, std::move( targets.color ), targets.depth, std::move( targets.velocity ), std::move( targets.opaqueColor ), m_scene, m_upscalingContext, m_gpuResourcePool, renderContext ); } - - m_viewLast = m_scene->m_viewLast; - m_projectionLast = m_scene->m_projectionLast; - m_scene->PopulateAndApplyPerFrameData( renderContext ); - SetCameraToRenderer( renderContext ); { TimeSection ui3dSection( m_timers.ui3d, "Render3dUI", rootTimer, renderContext ); @@ -592,62 +532,91 @@ void EveSpaceSceneRenderDriver::RenderNew( const Span& desti } } -void EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +Matrix EveSpaceSceneRenderDriver::GetJitterMatrix( uint32_t renderWidth, uint32_t renderHeight, Tr2RenderContext& renderContext ) const { - auto featureSet = renderInfo.featureSet; - auto renderSize = renderInfo.renderSize; - auto view = renderInfo.view; - auto projection = renderInfo.projection; + Matrix jitterMatrix; - auto velocityMap = GetVelocityMapIfNeeded( renderSize, outputs, featureSet ); - auto backBuffer = FindNamedOutput( outputs, "BackBuffer" ); - if( !backBuffer || !backBuffer->texture.IsValid() ) + if( m_upscalingInfo.temporal ) { - SetNamedOutput(outputs, "BackBuffer", m_gpuResourcePool.GetTempTexture( "backBuffer", renderSize, m_internalPixelFormat, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE )); - backBuffer = FindNamedOutput( outputs, "BackBuffer" ); + float jitterX = 0.0f; + float jitterY = 0.0f; + m_upscalingContext->GetJitter( jitterX, jitterY ); + jitterMatrix = TranslationMatrix( Vector3( jitterX, jitterY, 0 ) ); + } + else if( m_postProcess && m_scene && m_scene->GetPostProcess() && m_scene->GetPostProcess()->GetTaaIfAvailable() != nullptr ) + { + auto frameIndex = renderContext.GetPrimaryRenderContext().GetRenderedFrameNumber(); + jitterMatrix = m_postProcess->GetJitter( frameIndex, renderWidth, renderHeight ); + } + else + { + jitterMatrix = IdentityMatrix(); } - auto depthBuffer = m_gpuResourcePool.GetTempTexture( "depthBuffer", renderSize, ImageIO::PIXEL_FORMAT_D32_FLOAT, Tr2GpuUsage::DEPTH_STENCIL | Tr2GpuUsage::SHADER_RESOURCE ); - - SetNamedOutput( outputs, "DepthMap", depthBuffer ); + return jitterMatrix; +} - renderContext.m_esm.PushRenderTarget( 0 ); - if( featureSet.velocityMap ) +float EveSpaceSceneRenderDriver::GetMipLodBias() const +{ + float mipLodBias = 0.0f; + USE_MAIN_THREAD_RENDER_CONTEXT(); + + if( m_scene ) { - renderContext.m_esm.PushRenderTarget( 1 ); - } - renderContext.m_esm.PushDepthStencilBuffer(); - ON_BLOCK_EXIT( [&] { - renderContext.m_esm.PopDepthStencilBuffer(); - if( featureSet.velocityMap ) + if( auto postprocess = m_scene->GetPostProcess() ) { - renderContext.m_esm.PopRenderTarget( 1 ); + mipLodBias += postprocess->GetMipLodBias(); } - renderContext.m_esm.PopRenderTarget( 0 ); - } ); + } + + // 0 if no upscaling is enabled + mipLodBias += m_upscalingInfo.mipLevelBias; + return mipLodBias; +} - if( m_background ) +// -------------------------------------------------------------------------------------- +// Description: +// Render the scene from one view (the main view or a reflection probe face) into +// colorTarget. Returns the targets the view rendered, for post processing or for +// submitting to the reflection probe. +// -------------------------------------------------------------------------------------- +EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, Tr2GpuResourcePool::Texture colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +{ + const FeatureSet& featureSet = renderInfo.featureSet; + const SceneRender::ViewInfo& sceneView = renderInfo.sceneView; + const TextureSize2D& renderSize = renderInfo.renderSize; + + ViewTargets targets; + targets.color = std::move( colorTarget ); + const Tr2TextureAL& backBuffer = targets.color; + + const bool renderBackgroundNode = m_background && featureSet.background; + if( renderBackgroundNode ) { // render background effect to back buffer before rendering the scene - BeginRenderPass( renderContext, { backBuffer->texture }, {} ); + BeginRenderPass( renderContext, { backBuffer }, {} ); renderContext.Clear( Tr2RenderContextEnum::CLEARFLAGS_TARGET, m_settings.clearColor, 0 ); - const Tr2TextureAL& backgroundTarget = backBuffer->texture; - m_background->Execute( { &backgroundTarget, 1 }, {}, realTime, simTime, rootTimer, renderContext ); + m_background->Execute( { &backBuffer, 1 }, {}, realTime, simTime, rootTimer, renderContext ); } - // set the - BeginRenderPass( renderContext, { backBuffer->texture }, depthBuffer ); - renderContext.Clear( m_background ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); - SetCameraToRenderer( view, projection, renderContext ); + targets.depth = m_gpuResourcePool.GetTempTexture( "depthBuffer", renderSize, ImageIO::PIXEL_FORMAT_D32_FLOAT, Tr2GpuUsage::DEPTH_STENCIL | Tr2GpuUsage::SHADER_RESOURCE ); + const Tr2TextureAL& depthBuffer = targets.depth; + + BeginRenderPass( renderContext, { backBuffer }, depthBuffer ); + renderContext.Clear( renderBackgroundNode ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); + + SetCameraToRenderer( renderInfo.view, renderInfo.projection, renderContext ); PropagateRenderningInfo( renderInfo.jitterMatrix, renderInfo.mipLodBias, renderInfo.upscalingAmount ); - { TimeSection updateSection( m_timers.update, "Update", rootTimer, renderContext ); m_scene->Update( realTime, simTime ); } - UpdateGpuParticleSystem( renderContext ); + if( sceneView.gpuParticles ) + { + UpdateGpuParticleSystem( renderContext ); + } auto prevVisualizeMethod = m_scene->m_visualizeMethod; m_scene->m_visualizeMethod = m_settings.visualizeMethod; @@ -655,37 +624,31 @@ void EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const { TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); - - if( g_eveSpaceSceneDynamicLighting ) - { - if( auto lightManager = Tr2LightManager::GetOrCreateInstance( "res:/graphics/effect/managed/space/system/computelightlists.fx" ) ) - { - lightManager->SetVariableStore(); - lightManager->SetLightingQuality( m_settings.lightingQuality ); - } - } - else - { - Tr2LightManager::DeleteInstance(); - } - - m_scene->BeginRender( m_settings.enableDistortion, renderContext ); + m_scene->BeginFrame( renderContext ); + m_scene->BeginView( sceneView, renderContext ); } + // Old route: the scene renders the reflection faces itself, from within the main view + if( featureSet.reflections && !m_newRoute ) + { + TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); + m_scene->RenderReflectionPass( m_gpuResourcePool, renderContext ); + } + GlobalStore().RegisterVariable( "DepthMap", targets.depth ); - GlobalStore().RegisterVariable( "DepthMap", depthBuffer ); - - SetNamedOutput( outputs, "DepthMap", depthBuffer ); + SetNamedOutput( outputs, "DepthMap", targets.depth ); - Tr2GpuResourcePool::Texture distortionMap = GetDistortionMapIfNeeded( renderSize, featureSet ); + Tr2GpuResourcePool::Texture distortionMap = GetDistortionMapIfNeeded( renderSize, sceneView.distortion ); + targets.velocity = GetVelocityMapIfNeeded( renderSize, outputs, featureSet ); + const Tr2TextureAL& velocityMap = targets.velocity; bool hasBackgroundDistortionBatches = false; { TimeSection backgroundSection( m_timers.background, "BackgroundPass", rootTimer, renderContext ); - hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, renderContext ); + hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, sceneView, renderContext ); } - if( m_settings.enableDistortion && hasBackgroundDistortionBatches ) + if( sceneView.distortion && hasBackgroundDistortionBatches ) { - ApplyDistortion( backBuffer->texture, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); + ApplyDistortion( backBuffer, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); renderContext.m_esm.SetDepthStencilBuffer( depthBuffer ); } @@ -695,24 +658,23 @@ void EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const Tr2GpuResourcePool::Texture customStencil = GetCustomStencilMapIfNeeded( renderSize, outputs ); { TimeSection depthPassSection( m_timers.depthPass, "DepthPass", rootTimer, renderContext ); - m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, renderContext, m_depthPassTechnique ); + m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, sceneView, renderContext, m_depthPassTechnique ); } GlobalStore().RegisterVariable( "SpaceSceneNormalMap", normalMap ); GlobalStore().RegisterVariable( "SpaceSceneCustomStencil", customStencil ); ON_BLOCK_EXIT( [&] { GlobalStore().RegisterVariable( "SpaceSceneCustomStencil", Tr2TextureAL{} ); } ); - Tr2GpuResourcePool::Texture opaqueBackBuffer; EveSpaceScene::ShadowResources shadowResources; if( m_scene->m_display && m_mainPassRenderingEnabled ) { { TimeSection shadowsSection( m_timers.shadows, "Shadows", rootTimer, renderContext ); - shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, renderContext ); + shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, sceneView, renderContext ); } TimeSection ssaoSection( m_timers.ssao, "SSAO", rootTimer, renderContext ); - auto ssao = RenderSSAO( depthBuffer, normalMap, renderContext ); + auto ssao = featureSet.ssao ? RenderSSAO( depthBuffer, normalMap, renderContext ) : GetEmptySSAO( m_gpuResourcePool ); ssaoSection.Stop(); - + GlobalStore().RegisterVariable( "SSAOMap", ssao ); if( auto lightManager = Tr2LightManager::GetInstance() ) @@ -724,20 +686,20 @@ void EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const renderContext.SetReadOnlyDepth( false ); } - opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs, featureSet ); + targets.opaqueColor = GetOpaqueColorMapIfNeeded( renderSize, outputs, featureSet ); RegisterWithVariableStore( shadowResources, m_gpuResourcePool ); bool hasDistortionBatches = false; - distortionMap = GetDistortionMapIfNeeded( renderSize, featureSet ); + distortionMap = GetDistortionMapIfNeeded( renderSize, sceneView.distortion ); { TimeSection mainPassSection( m_timers.mainPass, "MainPass", rootTimer, renderContext ); - hasDistortionBatches = m_scene->RenderMainPass( backBuffer->texture, depthBuffer, distortionMap, velocityMap, opaqueBackBuffer, m_gpuResourcePool, renderContext ); + hasDistortionBatches = m_scene->RenderMainPass( backBuffer, depthBuffer, distortionMap, velocityMap, targets.opaqueColor, m_gpuResourcePool, sceneView, renderContext ); } RegisterWithVariableStore( {}, m_gpuResourcePool ); - if( m_settings.enableDistortion && hasDistortionBatches ) + if( sceneView.distortion && hasDistortionBatches ) { - ApplyDistortion( backBuffer->texture, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); + ApplyDistortion( backBuffer, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); renderContext.m_esm.SetDepthStencilBuffer( depthBuffer ); } distortionMap = {}; @@ -746,24 +708,26 @@ void EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const else { // Even if we skip main pass rendering, we might need opaqueBackBuffer for upscaling or TAA - opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs, featureSet ); - if( opaqueBackBuffer.IsValid() ) + targets.opaqueColor = GetOpaqueColorMapIfNeeded( renderSize, outputs, featureSet ); + if( targets.opaqueColor.IsValid() ) { renderContext.RenderPassHint( { Tr2LoadAction::DONT_CARE, Tr2StoreAction::STORE }, {} ); - BeginRenderPass( renderContext, { opaqueBackBuffer.Get() }, {} ); + BeginRenderPass( renderContext, { targets.opaqueColor.Get() }, {} ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_FULLSCREEN ); - Tr2Renderer::DrawTexture( renderContext, backBuffer->texture ); + Tr2Renderer::DrawTexture( renderContext, backBuffer ); } } - if( m_sceneOverlay ) + if( m_sceneOverlay && featureSet.overlay ) { - Tr2TextureAL targets[] = { backBuffer->texture, velocityMap.Get() }; - m_sceneOverlay->Execute( { targets, 2 }, {}, realTime, simTime, rootTimer, renderContext ); + Tr2TextureAL overlayTargets[] = { backBuffer, velocityMap }; + m_sceneOverlay->Execute( { overlayTargets, 2 }, {}, realTime, simTime, rootTimer, renderContext ); } GlobalStore().RegisterVariable( "SpaceSceneNormalMap", Tr2TextureAL{} ); normalMap = {}; - BeginRenderPass( renderContext, { backBuffer->texture }, depthBuffer ); + BeginRenderPass( renderContext, { backBuffer }, depthBuffer ); + // occlusion query results are read by the next frame + if( sceneView.temporal ) { renderContext.SetReadOnlyDepth( true ); m_scene->RunLensflareOcclusionQueries( depthBuffer, renderContext ); @@ -771,236 +735,87 @@ void EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const } { TimeSection endRenderSection( m_timers.endRender, "EndRender", rootTimer, renderContext ); - m_scene->EndRender( renderContext ); + m_scene->EndRender( sceneView, renderContext ); } + // non-temporal views leave the scene's history untouched, so this is a no-op for them m_viewLast = m_scene->m_viewLast; m_projectionLast = m_scene->m_projectionLast; m_scene->PopulateAndApplyPerFrameData( renderContext ); GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", Tr2TextureAL{} ); + + return targets; } -void EveSpaceSceneRenderDriver::RenderOld( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +void EveSpaceSceneRenderDriver::RenderReflectionFaces( Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { - - GlobalStore().RegisterVariable( "EveSpaceSceneReflectionCorrectionLookupTable", m_reflectionCorrectionEnabled ? m_reflectionCorrectionMap : m_blackReflectionCorrectionMap ); - static const FeatureSet mainFeatures = { true, true, true, true, true, true, true }; - - m_scene->m_viewLast = m_viewLast; - m_scene->m_projectionLast = m_projectionLast; - - if( m_camera ) - { - m_camera->Update( simTime ); - } - - const TextureSize2D displaySize = destinations.data->GetDesc(); - const TextureSize2D renderSize = GetRenderSize( displaySize ); - - PropagateSettings(); - - GlobalStore().RegisterVariable( "SSAOMap", GetEmptySSAO( m_gpuResourcePool ) ); - - renderContext.m_esm.SetInvertedDepthTest( true ); - ON_BLOCK_EXIT( [&] { renderContext.m_esm.SetInvertedDepthTest( false ); } ); - - - renderContext.m_esm.PushRenderTarget( 0 ); - renderContext.m_esm.PushRenderTarget( 1 ); - renderContext.m_esm.PushDepthStencilBuffer(); - ON_BLOCK_EXIT( [&] { - renderContext.m_esm.PopDepthStencilBuffer(); - renderContext.m_esm.PopRenderTarget( 1 ); - renderContext.m_esm.PopRenderTarget( 0 ); - } ); - - auto customBackBuffer = m_gpuResourcePool.GetTempTexture( "customBackBuffer", renderSize, m_internalPixelFormat, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); - - if( m_background ) + Tr2ReflectionProbe* probe = m_reflectionProbe; + if( !probe || !probe->IsValid() || !m_scene->m_display ) { - BeginRenderPass( renderContext, { customBackBuffer }, {} ); - renderContext.Clear( Tr2RenderContextEnum::CLEARFLAGS_TARGET, m_settings.clearColor, 0 ); - - m_background->Execute( { &customBackBuffer.Get(), 1 }, {}, realTime, simTime, rootTimer, renderContext ); + return; } - auto depthBuffer = m_gpuResourcePool.GetTempTexture( "depthBuffer", renderSize, ImageIO::PIXEL_FORMAT_D32_FLOAT, Tr2GpuUsage::DEPTH_STENCIL | Tr2GpuUsage::SHADER_RESOURCE ); + GPU_REGION( renderContext, "Reflection" ); - BeginRenderPass( renderContext, { customBackBuffer }, depthBuffer ); - renderContext.Clear( m_background ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); + // faces are rendered without TAA/upscaling, SSAO, velocity or the render nodes + FeatureSet reflectionFeatures; + reflectionFeatures.upscaling = false; + reflectionFeatures.antiAliasing = false; + reflectionFeatures.reflections = false; // no reflections rendered in the reflection faces, this can be removed when m_newRoute is removed + reflectionFeatures.ssao = false; + reflectionFeatures.velocityMap = false; + reflectionFeatures.background = false; + reflectionFeatures.overlay = false; + const SceneRender::ViewInfo faceView = SceneRender::ViewInfo::ReflectionFace(); SetCameraToRenderer( renderContext ); - float upscalingAmount = m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f; - PropagateRenderningInfo( GetJitterMatrix( renderSize.width, renderSize.height, renderContext ), GetMipLodBias(), upscalingAmount ); - - { - TimeSection updateSection( m_timers.update, "Update", rootTimer, renderContext ); - m_scene->Update( realTime, simTime ); - } - UpdateGpuParticleSystem( renderContext ); - - auto prevVisualizeMethod = m_scene->m_visualizeMethod; - m_scene->m_visualizeMethod = m_settings.visualizeMethod; - ON_BLOCK_EXIT( [&] { m_scene->m_visualizeMethod = prevVisualizeMethod; } ); - - { - TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); - - if( g_eveSpaceSceneDynamicLighting ) - { - if( auto lightManager = Tr2LightManager::GetOrCreateInstance( "res:/graphics/effect/managed/space/system/computelightlists.fx" ) ) - { - lightManager->SetVariableStore(); - lightManager->SetLightingQuality( m_settings.lightingQuality ); - } - } - else - { - Tr2LightManager::DeleteInstance(); - } - - m_scene->BeginRender( m_settings.enableDistortion, renderContext ); - } - { - TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); - m_scene->RenderReflectionPass( m_gpuResourcePool, renderContext ); - } - GlobalStore().RegisterVariable( "DepthMap", depthBuffer ); + probe->UpdatePosition( Tr2Renderer::GetViewPosition() ); + const Vector3 viewDirection = Tr2Renderer::GetInverseViewTransform().GetZ(); + const Matrix faceProjection = probe->GetFaceProjection( Tr2Renderer::GetProjectionTransform() ); - SetNamedOutput( outputs, "DepthMap", depthBuffer ); + TriProjectionPtr projection; + projection.CreateInstance(); + projection->CustomProjection( faceProjection ); - Tr2GpuResourcePool::Texture distortionMap = GetDistortionMapIfNeeded( renderSize, mainFeatures ); - Tr2GpuResourcePool::Texture velocityMap = GetVelocityMapIfNeeded( renderSize, outputs, mainFeatures ); + const unsigned faceSize = probe->GetFaceSize(); + const TextureSize2D faceRenderSize = probe->GetFaceTarget( 0 ).GetDesc(); - bool hasBackgroundDistortionBatches = false; - { - TimeSection backgroundSection( m_timers.background, "BackgroundPass", rootTimer, renderContext ); - hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, renderContext ); - } - if( m_settings.enableDistortion && hasBackgroundDistortionBatches ) - { - ApplyDistortion( customBackBuffer, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); + const bool prevCullInversion = renderContext.m_esm.IsCullModeInverted(); + renderContext.m_esm.SetInvertedCullMode( true ); + ON_BLOCK_EXIT( [&] { renderContext.m_esm.SetInvertedCullMode( prevCullInversion ); } ); - renderContext.m_esm.SetDepthStencilBuffer( depthBuffer ); - } - distortionMap = {}; - - Tr2GpuResourcePool::Texture normalMap = GetNormalMapIfNeeded( renderSize, outputs, mainFeatures ); - Tr2GpuResourcePool::Texture customStencil = GetCustomStencilMapIfNeeded( renderSize, outputs ); - { - TimeSection depthPassSection( m_timers.depthPass, "DepthPass", rootTimer, renderContext ); - m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, renderContext, m_depthPassTechnique ); - } - GlobalStore().RegisterVariable( "SpaceSceneNormalMap", normalMap ); - GlobalStore().RegisterVariable( "SpaceSceneCustomStencil", customStencil ); - ON_BLOCK_EXIT( [&] { GlobalStore().RegisterVariable( "SpaceSceneCustomStencil", Tr2TextureAL{} ); } ); - - Tr2GpuResourcePool::Texture opaqueBackBuffer; - EveSpaceScene::ShadowResources shadowResources; - if( m_scene->m_display && m_mainPassRenderingEnabled ) + for( unsigned face = probe->GetStartFace(); face < probe->GetEndFace(); ++face ) { - { - TimeSection shadowsSection( m_timers.shadows, "Shadows", rootTimer, renderContext ); - shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, renderContext ); - } - TimeSection ssaoSection( m_timers.ssao, "SSAO", rootTimer, renderContext ); - auto ssao = RenderSSAO( depthBuffer, normalMap, renderContext ); - ssaoSection.Stop(); + std::string regionName = "Reflection Face " + std::to_string( face ); + GPU_REGION( renderContext, regionName.c_str() ); - GlobalStore().RegisterVariable( "SSAOMap", ssao ); + Matrix view = probe->GetFaceViewTransform( face, probe->GetPosition() ); - if( auto lightManager = Tr2LightManager::GetInstance() ) - { - GPU_REGION( renderContext, "Lighting" ); - TRINITY_STATS_SCOPED_TIME( updateDynamicLightLists ); - renderContext.SetReadOnlyDepth( true ); - lightManager->UpdateLists( depthBuffer, renderContext ); - renderContext.SetReadOnlyDepth( false ); - } - - opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs, mainFeatures ); - - RegisterWithVariableStore( shadowResources, m_gpuResourcePool ); - bool hasDistortionBatches = false; - distortionMap = GetDistortionMapIfNeeded( renderSize, mainFeatures ); - { - TimeSection mainPassSection( m_timers.mainPass, "MainPass", rootTimer, renderContext ); - hasDistortionBatches = m_scene->RenderMainPass( customBackBuffer, depthBuffer, distortionMap, velocityMap, opaqueBackBuffer, m_gpuResourcePool, renderContext ); - } - RegisterWithVariableStore( {}, m_gpuResourcePool ); + RenderInfo renderInfo = { view, projection, IdentityMatrix(), 0.0f, 1.0f, faceRenderSize, reflectionFeatures, faceView }; - if( m_settings.enableDistortion && hasDistortionBatches ) - { - ApplyDistortion( customBackBuffer, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); - renderContext.m_esm.SetDepthStencilBuffer( depthBuffer ); - } - distortionMap = {}; - GlobalStore().RegisterVariable( "SSAOMap", Tr2TextureAL{} ); - } - else - { - // Even if we skip main pass rendering, we might need opaqueBackBuffer for upscaling or TAA - opaqueBackBuffer = GetOpaqueColorMapIfNeeded( renderSize, outputs, mainFeatures ); - if( opaqueBackBuffer.IsValid() ) - { - renderContext.RenderPassHint( { Tr2LoadAction::DONT_CARE, Tr2StoreAction::STORE }, {} ); - BeginRenderPass( renderContext, { opaqueBackBuffer }, {} ); - renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_FULLSCREEN ); - Tr2Renderer::DrawTexture( renderContext, customBackBuffer ); - } - } - if( m_sceneOverlay ) - { - Tr2TextureAL targets[] = { customBackBuffer.Get(), velocityMap.Get() }; - m_sceneOverlay->Execute( { targets, 2 }, {}, realTime, simTime, rootTimer, renderContext ); - } - GlobalStore().RegisterVariable( "SpaceSceneNormalMap", Tr2TextureAL{} ); - normalMap = {}; + auto faceTarget = m_gpuResourcePool.GetTempTexture( "reflectionFace", faceSize, faceSize, probe->GetFaceTarget( face ).GetFormat(), Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); + ViewTargets targets = RenderView( renderInfo, std::move( faceTarget ), {}, realTime, simTime, rootTimer, renderContext ); - BeginRenderPass( renderContext, { customBackBuffer }, depthBuffer ); - { - renderContext.SetReadOnlyDepth( true ); - m_scene->RunLensflareOcclusionQueries( depthBuffer, renderContext ); - renderContext.SetReadOnlyDepth( false ); - } - { - TimeSection endRenderSection( m_timers.endRender, "EndRender", rootTimer, renderContext ); - m_scene->EndRender( renderContext ); + probe->SubmitFace( face, targets.color, renderContext ); } - m_viewLast = m_scene->m_viewLast; - m_projectionLast = m_scene->m_projectionLast; - m_scene->PopulateAndApplyPerFrameData( renderContext ); - renderContext.m_esm.SetRenderTarget( 0, *destinations.data ); - renderContext.m_esm.SetDepthStencilBuffer( {} ); - - GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", Tr2TextureAL{} ); - - { - TimeSection postProcessSection( m_timers.postProcess, "PostProcess", rootTimer, renderContext ); - m_postProcess->Execute( *destinations.data, std::move( customBackBuffer ), depthBuffer, std::move( velocityMap ), std::move( opaqueBackBuffer ), m_scene, m_upscalingContext, m_gpuResourcePool, renderContext ); - } + probe->FinishFaces( viewDirection, renderContext ); SetCameraToRenderer( renderContext ); - { - TimeSection ui3dSection( m_timers.ui3d, "Render3dUI", rootTimer, renderContext ); - m_scene->Render3DUI( renderContext ); - } +} - for( auto& toolScene : m_toolsScenes ) +void EveSpaceSceneRenderDriver::PrepareLightManager() +{ + if( g_eveSpaceSceneDynamicLighting ) { - if( ITr2UpdateablePtr updateable = BlueCastPtr( toolScene ) ) + if( auto lightManager = Tr2LightManager::GetOrCreateInstance( "res:/graphics/effect/managed/space/system/computelightlists.fx" ) ) { - updateable->Update( realTime, simTime ); + lightManager->SetVariableStore(); + lightManager->SetLightingQuality( m_settings.lightingQuality ); } - toolScene->Render( renderContext ); } - - GlobalStore().RegisterVariable( "SSAOMap", GetEmptySSAO( m_gpuResourcePool ) ); - GlobalStore().RegisterVariable( "DepthMap", Tr2TextureAL{} ); - - if( m_settings.showFPS ) + else { - m_fpsRenderer->Execute( realTime, simTime, renderContext ); + Tr2LightManager::DeleteInstance(); } } @@ -1014,9 +829,9 @@ void EveSpaceSceneRenderDriver::UpdateGpuParticleSystem( Tr2RenderContext& rende } } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetDistortionMapIfNeeded( const TextureSize2D& size, const FeatureSet& featureSet ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetDistortionMapIfNeeded( const TextureSize2D& size, bool distortion ) { - if( m_settings.enableDistortion && featureSet.distortion ) + if( distortion ) { return m_gpuResourcePool.GetTempTexture( "distortionMap", size, ImageIO::PIXEL_FORMAT_B8G8R8A8_UNORM, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); } @@ -1025,7 +840,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetDistortionMapIfNeeded( Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) { - if( !featureSet.antiAliasing && !featureSet.upscaling && !m_upscalingInfo.temporal ) + if( !featureSet.velocityMap || ( !featureSet.antiAliasing && !featureSet.upscaling && !m_upscalingInfo.temporal ) ) { return {}; } diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index ca4e85374..2f1bce875 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -73,28 +73,33 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : EveSpaceScene::EveVisualizeMethod visualizeMethod = EveSpaceScene::VM_NONE; }; + // Driver side features of a view. What the scene renders is described by the + // EveSpaceScene::ViewInfo in RenderInfo. struct FeatureSet { bool upscaling = true; bool antiAliasing = true; - bool distortion = true; bool reflections = true; bool volumetrics = true; bool ssao = true; bool shadows = true; bool velocityMap = true; + // special background rendering (f.ex texture background) + bool background = true; + // special overlay rendering (f.ex texture overlay) + bool overlay = true; }; struct RenderInfo { - TriViewPtr view; + const Matrix& view; TriProjectionPtr projection; Matrix jitterMatrix; float mipLodBias; float upscalingAmount; TextureSize2D renderSize; FeatureSet featureSet; - Tr2RenderReason reason; + SceneRender::ViewInfo sceneView; }; EveSpaceSceneRenderDriver( IRoot* lockobj = nullptr ); @@ -122,26 +127,35 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : private: - void RenderOld( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); - void RenderNew( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); - void RenderView( const RenderInfo& renderInfo, const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + // Targets rendered by RenderView + struct ViewTargets + { + Tr2GpuResourcePool::Texture color; + Tr2GpuResourcePool::Texture depth; + Tr2GpuResourcePool::Texture velocity; + Tr2GpuResourcePool::Texture opaqueColor; + }; + + ViewTargets RenderView( const RenderInfo& renderInfo, Tr2GpuResourcePool::Texture colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + void RenderReflectionFaces( Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetupUpscaling( const TextureSize2D& displaySize ); void PropagateSettings(); void PropagateRenderningInfo( Matrix jitterMatrix, float mipLodBias, float upscaling ); void SetCameraToRenderer( Tr2RenderContext & renderContext ) const; - void SetCameraToRenderer( TriViewPtr view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const; + void SetCameraToRenderer( const Matrix& view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const; TextureSize2D GetRenderSize( const TextureSize2D& displaySize ) const; Matrix GetJitterMatrix( uint32_t renderWidth, uint32_t renderHeight, Tr2RenderContext& renderContext ) const; float GetMipLodBias( ) const; - Tr2GpuResourcePool::Texture GetDistortionMapIfNeeded( const TextureSize2D& size, const FeatureSet& featureSet ); + Tr2GpuResourcePool::Texture GetDistortionMapIfNeeded( const TextureSize2D& size, bool distortion ); Tr2GpuResourcePool::Texture GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); Tr2GpuResourcePool::Texture GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); Tr2GpuResourcePool::Texture GetCustomStencilMapIfNeeded( const TextureSize2D& size, const Span& outputs ); Tr2GpuResourcePool::Texture GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); void UpdateGpuParticleSystem( Tr2RenderContext & renderContext ); + void PrepareLightManager(); Tr2GpuResourcePool::Texture RenderSSAO( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2RenderContext& renderContext ); diff --git a/trinity/Tr2ReflectionProbe.cpp b/trinity/Tr2ReflectionProbe.cpp index 3b7224ce3..3388db3a3 100644 --- a/trinity/Tr2ReflectionProbe.cpp +++ b/trinity/Tr2ReflectionProbe.cpp @@ -111,15 +111,7 @@ void Tr2ReflectionProbe::InitRenderPass( Tr2RenderContext& renderContext ) renderContext.m_esm.SetViewport( m_intermediateSize, m_intermediateSize, 0, 0, 0, 1 ); - // Square projection matrix, with near and far clip planes from the current projection - Matrix newProjection = IdentityMatrix(); - const Matrix& currentProjection = Tr2Renderer::GetProjectionTransform(); - newProjection.m[2][2] = currentProjection.m[2][2]; - newProjection.m[2][3] = -1.0f; - newProjection.m[3][2] = currentProjection.m[3][2]; - newProjection.m[3][3] = 0.0f; - - Tr2Renderer::SetProjectionTransform( newProjection ); + Tr2Renderer::SetProjectionTransform( GetFaceProjection( Tr2Renderer::GetProjectionTransform() ) ); // We need to invert cull-mode because of the left-handed coordinates of the cube rendertarget m_prevCullInversion = renderContext.m_esm.IsCullModeInverted(); @@ -151,6 +143,27 @@ void Tr2ReflectionProbe::StartRenderFace( unsigned face, Tr2RenderContext& rende renderContext.m_esm.SetRenderTarget( 0, *m_renderTargets[face] ); renderContext.m_esm.SetDepthStencilBuffer( m_stencilMaps[face] ); + Tr2Renderer::SetViewTransform( GetFaceViewTransform( face, m_position ) ); +} + +unsigned Tr2ReflectionProbe::GetFaceSize() const +{ + return static_cast( m_intermediateSize ); +} + +Matrix Tr2ReflectionProbe::GetFaceProjection( const Matrix& currentProjection ) const +{ + // Square projection matrix, with near and far clip planes from the current projection + Matrix newProjection = IdentityMatrix(); + newProjection.m[2][2] = currentProjection.m[2][2]; + newProjection.m[2][3] = -1.0f; + newProjection.m[3][2] = currentProjection.m[3][2]; + newProjection.m[3][3] = 0.0f; + return newProjection; +} + +Matrix Tr2ReflectionProbe::GetFaceViewTransform( unsigned face, const Vector3& position ) const +{ static const Vector3 faceDirections[] = { Vector3( 0, 1, 0 ), Vector3( 1, 0, 0 ), //+X Vector3( 0, 1, 0 ), @@ -171,7 +184,48 @@ void Tr2ReflectionProbe::StartRenderFace( unsigned face, Tr2RenderContext& rende const Vector3& up = faceDirections[face * 2]; const Vector3& at = faceDirections[face * 2 + 1]; - Tr2Renderer::SetViewTransform( LookAtMatrix( m_position, m_position + at, up ) * xInverter ); + return LookAtMatrix( position, position + at, up ) * xInverter; +} + +void Tr2ReflectionProbe::SubmitFace( unsigned face, const Tr2TextureAL& source, Tr2RenderContext& renderContext ) +{ + if( face >= 6 || !source.IsValid() || !IsValid() ) + { + return; + } + + GPU_REGION( renderContext, "Reflection External Face Copy" ); + CR( m_renderTargetCube->GetRenderTarget().CopySubresourceRegion( Tr2TextureSubresource( face, 0 ), source, Tr2TextureSubresource( 0, 0 ), renderContext ) ); +} + +void Tr2ReflectionProbe::UpdatePosition( const Vector3& viewPosition ) +{ + if( !m_lockPosition ) + { + m_position = viewPosition; + } +} + +const Vector3& Tr2ReflectionProbe::GetPosition() const +{ + return m_position; +} + +const Tr2TextureAL& Tr2ReflectionProbe::GetFaceTarget( unsigned face ) const +{ + return m_renderTargets[face]->GetRenderTarget(); +} + +void Tr2ReflectionProbe::FinishFaces( const Vector3& viewDirection, Tr2RenderContext& renderContext ) +{ + Filter( viewDirection, renderContext ); + m_hasData = true; + m_onePassDone = true; + + if( m_renderFrequency != ALL_SIDES_PER_FRAME ) + { + m_currentFrame = ( m_currentFrame + 1 ) % 6; + } } void Tr2ReflectionProbe::EndRenderPass( Tr2RenderContext& renderContext ) @@ -193,14 +247,7 @@ void Tr2ReflectionProbe::EndRenderPass( Tr2RenderContext& renderContext ) Tr2Renderer::PopViewTransform(); renderContext.m_esm.PopViewport(); - Filter( renderContext ); - m_hasData = true; - m_onePassDone = true; - - if( m_renderFrequency != ALL_SIDES_PER_FRAME ) - { - m_currentFrame = ( m_currentFrame + 1 ) % 6; - } + FinishFaces( Tr2Renderer::GetInverseViewTransform().GetZ(), renderContext ); } Tr2RenderTargetPtr Tr2ReflectionProbe::GetReflection() @@ -344,7 +391,7 @@ void Tr2ReflectionProbe::DestroyRenderTargets() m_currentFrame = 0; } -void Tr2ReflectionProbe::Filter( Tr2RenderContext& renderContext ) +void Tr2ReflectionProbe::Filter( const Vector3& viewDirection, Tr2RenderContext& renderContext ) { if( !IsValid() ) return; @@ -356,11 +403,11 @@ void Tr2ReflectionProbe::Filter( Tr2RenderContext& renderContext ) { m_preFilterEffect->SetParameter( s_backLightColorName, Vector4( m_backlightColor ) ); m_preFilterEffect->SetParameter( s_backLightContrastName, m_backlightContrast ); - m_preFilterEffect->SetParameter( s_viewDirectionName, Tr2Renderer::GetInverseViewTransform().GetZ() ); + m_preFilterEffect->SetParameter( s_viewDirectionName, viewDirection ); m_copyMipEffect->SetParameter( s_backLightColorName, Vector4( m_backlightColor ) ); m_copyMipEffect->SetParameter( s_backLightContrastName, m_backlightContrast ); - m_copyMipEffect->SetParameter( s_viewDirectionName, Tr2Renderer::GetInverseViewTransform().GetZ() ); + m_copyMipEffect->SetParameter( s_viewDirectionName, viewDirection ); } Tr2Renderer::RunComputeShader( m_preFilterEffect, FILTER_SIZE * 2 / 8, FILTER_SIZE * 2 / 8, 6, renderContext ); @@ -385,7 +432,7 @@ void Tr2ReflectionProbe::RunFilter() USE_MAIN_THREAD_RENDER_CONTEXT(); DestroyRenderTargets(); DoPrepareResources( PIXEL_FORMAT_R16G16B16A16_FLOAT, renderContext ); - Filter( renderContext ); + Filter( Tr2Renderer::GetInverseViewTransform().GetZ(), renderContext ); } bool Tr2ReflectionProbe::IsHollyWoodModeOn() const diff --git a/trinity/Tr2ReflectionProbe.h b/trinity/Tr2ReflectionProbe.h index ba1883391..aa44c42e0 100644 --- a/trinity/Tr2ReflectionProbe.h +++ b/trinity/Tr2ReflectionProbe.h @@ -60,9 +60,21 @@ BLUE_CLASS( Tr2ReflectionProbe ) : return m_stencilMaps[face]; } + // External path: faces are rendered elsewhere (e.g. render driver) and submitted here. + // Source textures must match GetFaceSize() and the probe's intermediate format. + unsigned GetFaceSize() const; + Matrix GetFaceViewTransform( unsigned face, const Vector3& position ) const; + Matrix GetFaceProjection( const Matrix& currentProjection ) const; + void SubmitFace( unsigned face, const Tr2TextureAL& source, Tr2RenderContext& renderContext ); + void UpdatePosition( const Vector3& viewPosition ); + const Vector3& GetPosition() const; + const Tr2TextureAL& GetFaceTarget( unsigned face ) const; + // Filters the rendered faces and advances to the next faces. Called by both paths. + void FinishFaces( const Vector3& viewDirection, Tr2RenderContext& renderContext ); + private: void RunFilter(); - void Filter( Tr2RenderContext & renderContext ); + void Filter( const Vector3& viewDirection, Tr2RenderContext & renderContext ); bool DoPrepareResources( ImageIO::PixelFormat targetFormat, Tr2PrimaryRenderContext & renderContext ); void DestroyRenderTargets(); From 1d4b63d176aa8eda2388823dad04408130084c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Tue, 20 Oct 2026 12:00:41 +0000 Subject: [PATCH 03/20] EveSpaceScene rendering spIit Split EveSpaceScene::Update into UpdateFrame (animation, object updates and post process attributes, once per frame) and UpdateView (frustum and detail thresholds from the current camera). Execute now does the frame's work explicitly, in order: bind the main view's color target, update the scene from the main camera (UpdateSceneFrame, which also updates the GPU particles), BeginFrame, the reflection faces, then the main view. BeginFrame no longer guards itself with a frame counter. --- trinity/Eve/EveSpaceScene.cpp | 140 +++++++++++------ trinity/Eve/EveSpaceScene.h | 37 +---- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 144 +++++++++--------- trinity/Eve/EveSpaceSceneRenderDriver.h | 18 +-- .../Eve/EveSpaceSceneRenderDriver_Blue.cpp | 23 --- .../PostProcess/Tr2PostProcessRenderer.cpp | 2 +- trinity/PostProcess/Tr2PostProcessRenderer.h | 2 +- 7 files changed, 183 insertions(+), 183 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index 7de132429..07c7c986f 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -145,7 +145,33 @@ TriFrustum CreatePickingFrustum() return pickFrustum; } } +namespace SceneRender +{ +ViewInfo ViewInfo::MainView( bool distortion ) +{ + ViewInfo info; + info.distortion = distortion; + return info; +} +ViewInfo ViewInfo::ReflectionFace() +{ + ViewInfo view; + view.reason = TR2RENDERREASON_REFLECTION; + // Faces are rendered into a left handed cubemap, so the caller inverts the cull mode + // and the scene must not override it + view.cullMode = Tr2RenderContextEnum::CULLMODE_NONE; + view.distortion = false; + view.sceneObjects = false; + // faces must not disturb the main view's history + view.temporal = false; + view.reflection = true; + view.subsurfaceScattering = false; + view.gpuParticles = false; + view.lensflares = g_lensflaresInReflections && g_eveReflectionMode == EntityComponents::REFLECTION_SETTING_ULTRA; + return view; +} +} struct ShadowReceiver { @@ -452,34 +478,23 @@ void EveSpaceScene::Update( Be::Time realTime, Be::Time simTime ) if( frame == m_lastUpdateFrame ) { // already updated this frame - - TriFrustum frustum; - frustum.DeriveFrustum( &Tr2Renderer::GetViewTransform(), &Tr2Renderer::GetViewPosition(), &Tr2Renderer::GetProjectionTransform(), Tr2Renderer::GetViewport() ); - - m_updateContext.SetFrustum( frustum ); - m_updateContext.SetHighDetailThreshold( g_eveSpaceSceneHighDetailThreshold / m_upscalingAmount ); - m_updateContext.SetMediumDetailThreshold( g_eveSpaceSceneMediumDetailThreshold / m_upscalingAmount ); - m_updateContext.SetLowDetailThreshold( g_eveSpaceSceneLowDetailThreshold / m_upscalingAmount ); - m_updateContext.SetVisibilityThreshold( g_eveSpaceSceneVisibilityThreshold / m_upscalingAmount ); - m_updateContext.SetLodFactor( g_eveSpaceSceneLODFactor / m_upscalingAmount ); - m_updateContext.m_raytracingEnabled = m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows; - - // update the combined postprocess attributes - UpdatePostProcessAttributes(); + UpdateView(); return; } m_lastUpdateFrame = frame; } - if( !m_update ) - { - return; - } + UpdateFrame( realTime, simTime ); +} - m_updateContext.SetTime( simTime ); - m_updateContext.UpdateOrigin( m_ballpark ); - m_updateContext.SetDataTextureManager( m_dataTextureMgr ); +// -------------------------------------------------------------------------------------- +// Description: +// Per-view part of the update: called for every view after the frame's Update, so +// culling and post process attributes follow the view's camera. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::UpdateView() +{ TriFrustum frustum; frustum.DeriveFrustum( &Tr2Renderer::GetViewTransform(), &Tr2Renderer::GetViewPosition(), &Tr2Renderer::GetProjectionTransform(), Tr2Renderer::GetViewport() ); @@ -490,6 +505,29 @@ void EveSpaceScene::Update( Be::Time realTime, Be::Time simTime ) m_updateContext.SetVisibilityThreshold( g_eveSpaceSceneVisibilityThreshold / m_upscalingAmount ); m_updateContext.SetLodFactor( g_eveSpaceSceneLODFactor / m_upscalingAmount ); m_updateContext.m_raytracingEnabled = m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows; +} + +// -------------------------------------------------------------------------------------- +// Description: +// Per-frame part of the update: animates and updates all objects, using the current +// camera for LOD. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::UpdateFrame( Be::Time realTime, Be::Time simTime ) +{ + if( !m_update ) + { + return; + } + + m_updateContext.SetTime( simTime ); + m_updateContext.UpdateOrigin( m_ballpark ); + m_updateContext.SetDataTextureManager( m_dataTextureMgr ); + + // objects use the frustum and thresholds for LOD while they update + UpdateView(); + + // update the combined postprocess attributes + UpdatePostProcessAttributes(); { TRINITY_STATS_ZONE( "UpdateBackgroundObjects" ); @@ -1257,9 +1295,8 @@ bool EveSpaceScene::RenderDistortionBatches( BatchMap& batches, const Tr2Texture // -------------------------------------------------------------------------------------- // Description: -// Work that does not depend on the view being rendered. Safe to call before every -// view; only the first call in a frame does anything, so the frame's first (full) -// Update has already run by then. +// Work that does not depend on the view being rendered. Called once per frame, after +// the frame's Update and before the first BeginView. // -------------------------------------------------------------------------------------- void EveSpaceScene::BeginFrame( Tr2RenderContext& renderContext ) { @@ -1268,13 +1305,6 @@ void EveSpaceScene::BeginFrame( Tr2RenderContext& renderContext ) return; } - const uint64_t frame = renderContext.GetPrimaryRenderContextPointer()->GetRecordingFrameNumber(); - if( frame == m_lastBeginFrame ) - { - return; - } - m_lastBeginFrame = frame; - TRINITY_STATS_ZONE( __FUNCTION__ ); renderContext.AddGpuMarker( __FUNCTION__ ); @@ -1856,8 +1886,7 @@ void EveSpaceScene::RenderReflectionPass( Tr2GpuResourcePool& gpuResourcePool, T TRINITY_STATS_ZONE( __FUNCTION__ ); - // set the current reflection - GPU_REGION( renderContext, "Reflection" ); + GPU_REGION( renderContext, "Reflection" ) m_volumetricsRenderer->SetIsReflectionProbe( true ); ON_BLOCK_EXIT( [&] { @@ -2849,16 +2878,22 @@ bool EveSpaceScene::RenderMainPass( Tr2Renderer::SetProjectionTransform( m_jitteredProjection ); PopulateAndApplyPerFrameData( renderContext ); - if( view.temporal ) + std::pair froxelFogTextures; + + if( view.reason == TR2RENDERREASON_NORMAL ) { - auto [froxelFog, volumetricSlices] = RenderVolumetrics( depthMap, gpuResourcePool, renderContext ); - GlobalStore().RegisterVariable( "EveSceneFogVolumeMap", volumetricSlices ); - GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", froxelFog ); + froxelFogTextures = RenderVolumetrics( depthMap, gpuResourcePool, renderContext ); } - else + else if( view.reason == TR2RENDERREASON_REFLECTION ) + { + froxelFogTextures = RenderReflectionVolumetrics( depthMap, gpuResourcePool, renderContext ); + } + else { - RegisterReflectionFog( depthMap, gpuResourcePool, renderContext ); + froxelFogTextures = { Tr2VolumetricsRenderer::GetEmptyFogTexture( gpuResourcePool ), Tr2VolumetricsRenderer::GetEmptyVolumetricTexture( gpuResourcePool ) }; } + GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", froxelFogTextures.first ); + GlobalStore().RegisterVariable( "EveSceneFogVolumeMap", froxelFogTextures.second ); RenderTransparentBatches( m_primaryBatches, renderContext ); if( distortionMap.IsValid() ) @@ -2886,15 +2921,19 @@ bool EveSpaceScene::RenderMainPass( // Non-temporal views use the reflection fog, so they don't disturb the main view's fog // history. // -------------------------------------------------------------------------------------- -void EveSpaceScene::RegisterReflectionFog( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ) +std::pair EveSpaceScene::RenderReflectionVolumetrics( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ) { Tr2GpuResourcePool::Texture froxelFog; if( m_volumetricsRenderer ) { froxelFog = m_volumetricsRenderer->RenderFogIntoReflectionMap( renderContext, gpuResourcePool, depthMap.GetWidth(), depthMap.GetHeight(), m_sunData.DirWorld, m_currentSunColor, m_updateContext.GetOrigin(), Tr2Renderer::GetViewTransform(), Tr2Renderer::GetReversedDepthProjectionTransform() ); } - GlobalStore().RegisterVariable( "EveSceneFogVolumeMap", Tr2VolumetricsRenderer::GetEmptyFogTexture( gpuResourcePool ) ); - GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", froxelFog ); + else + { + froxelFog = Tr2VolumetricsRenderer::GetEmptyFogTexture( gpuResourcePool ); + } + + return { froxelFog, Tr2VolumetricsRenderer::GetEmptyVolumetricTexture( gpuResourcePool ) }; } void EveSpaceScene::RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ) @@ -3310,6 +3349,11 @@ void EveSpaceScene::PopulatePerFramePSData( PerFramePSData& data, Tr2ShadowMap* m_volumetricsRenderer->PopulatePerFrameData( data.FroxelFogData ); } +Tr2ReflectionProbe* EveSpaceScene::GetReflectionProbe() const +{ + return m_reflectionProbe; +} + bool EveSpaceScene::Initialize() { // the environment cubemap aka the nebula @@ -4405,12 +4449,18 @@ void RegisterWithVariableStore( const EveSpaceScene::ShadowResources& shadowReso GlobalStore().RegisterVariable( "ShadowMapAtlas", shadowResources.pointLightShadowDepth ); } -void EveSpaceScene::SetRenderingInfo( Matrix jitterMatrix, float mipLodBias, float upscaling ) +// -------------------------------------------------------------------------------------- +// Description: +// Set the view's camera matrices and rendering parameters, and apply the jittered +// projection to the renderer. Only depends on its arguments, so it is safe to call +// more than once for a view. +// -------------------------------------------------------------------------------------- +void EveSpaceScene::SetViewInfo( const Matrix& view, const Matrix& projection, const Matrix& jitterMatrix, float mipLodBias, float upscaling ) { - m_projection = Tr2Renderer::GetProjectionTransform(); + m_projection = projection; m_jitterMatrix = jitterMatrix; m_jitteredProjection = m_projection * m_jitterMatrix; - m_reprojectionMatrix = Inverse( m_projection ) * Inverse( Tr2Renderer::GetViewTransform() ) * m_viewLast * m_projectionLast; + m_reprojectionMatrix = Inverse( m_projection ) * Inverse( view ) * m_viewLast * m_projectionLast; m_upscalingAmount = upscaling; m_mipLodBias = mipLodBias; Tr2Renderer::SetProjectionTransform( m_jitteredProjection ); diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 61af84574..95878f0c5 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -124,30 +124,6 @@ struct ViewInfo static ViewInfo ReflectionFace(); }; -ViewInfo ViewInfo::MainView( bool distortion ) -{ - ViewInfo info; - info.distortion = distortion; - return info; -} - -ViewInfo ViewInfo::ViewInfo::ReflectionFace() -{ - ViewInfo view; - view.reason = TR2RENDERREASON_REFLECTION; - // Faces are rendered into a left handed cubemap, so the caller inverts the cull mode - // and the scene must not override it - view.cullMode = Tr2RenderContextEnum::CULLMODE_NONE; - view.distortion = false; - view.sceneObjects = false; - // faces must not disturb the main view's history - view.temporal = false; - view.reflection = true; - view.subsurfaceScattering = false; - view.gpuParticles = false; - view.lensflares = g_lensflaresInReflections && g_eveReflectionMode == EntityComponents::REFLECTION_SETTING_ULTRA; - return view; -} } @@ -210,7 +186,9 @@ BLUE_CLASS( EveSpaceScene ) : void GatherBatches( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); void PrepareRaytracedShadows( Tr2RenderContext & renderContext ); - void SetRenderingInfo( Matrix jitterMatrix, float mipLodBias, float upscaling ); + void SetViewInfo( const Matrix& view, const Matrix& projection, const Matrix& jitterMatrix, float mipLodBias, float upscaling ); + void UpdateView(); + Tr2ReflectionProbe* GetReflectionProbe() const; ////////////////////////////////////////////////////////////////////////////////////// // IInitialize @@ -442,8 +420,6 @@ BLUE_CLASS( EveSpaceScene ) : void ReregisterEntities(); void ClearComponentRegistry(); - std::pair RenderVolumetrics( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); - void EnableShadowsInReflections( bool enable ); bool IsShadowsInReflectionsEnabled() const; @@ -661,6 +637,7 @@ BLUE_CLASS( EveSpaceScene ) : virtual void ReleaseResources( TriStorage s ); virtual bool OnPrepareResources(); + void UpdateFrame( Be::Time realTime, Be::Time simTime ); void EnsureVisualizerEffect(); void ResolveLightingOverrides(); void ApplyViewLighting( const SceneRender::ViewInfo& view ); @@ -669,7 +646,9 @@ BLUE_CLASS( EveSpaceScene ) : void GatherSceneObjectRenderables( std::vector& allObjects, std::vector& renderables, Tr2RenderContext& renderContext ); void GatherReflectionRenderables( std::vector& renderables ); ShadowResources RenderReflectionShadows( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); - void RegisterReflectionFog( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); + + std::pair RenderVolumetrics( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); + std::pair RenderReflectionVolumetrics( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); void UpdateShLighting( const std::vector& objectsNotReceivingShadow ); @@ -765,8 +744,6 @@ BLUE_CLASS( EveSpaceScene ) : // Frame index when Update was last called uint64_t m_lastUpdateFrame = std::numeric_limits::max(); - // Frame index when BeginFrame last did its work - uint64_t m_lastBeginFrame = std::numeric_limits::max(); public: // raytracing diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 31ccd87eb..203b1f7b4 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -300,23 +300,34 @@ void EveSpaceSceneRenderDriver::PropagateSettings() { m_scene->m_volumetricsRenderer->SetQuality( m_settings.volumetricQuality ); } +} - if( m_settings.reflectionQuality != ReflectionQuality::Disabled && m_reflectionProbe == nullptr ) - { - m_reflectionProbe.CreateInstance(); - } - else if( m_settings.reflectionQuality == ReflectionQuality::Disabled && m_reflectionProbe ) - { - m_reflectionProbe = nullptr; - } +// -------------------------------------------------------------------------------------- +// Description: +// Set the view's camera to the renderer and its rendering parameters to the scene. +// -------------------------------------------------------------------------------------- +void EveSpaceSceneRenderDriver::ApplyView( const RenderInfo& renderInfo, Tr2RenderContext& renderContext ) const +{ + SetCameraToRenderer( renderInfo.view, renderInfo.projection, renderContext ); + // SetProjection adjusts the projection to the viewport, so read back the result + m_scene->SetViewInfo( renderInfo.view, Tr2Renderer::GetProjectionTransform(), renderInfo.jitterMatrix, renderInfo.mipLodBias, renderInfo.upscalingAmount ); + m_scene->UpdateView(); } -void EveSpaceSceneRenderDriver::PropagateRenderningInfo( Matrix jitterMatrix, float mipLodBias, float upscalingAmount ) +// -------------------------------------------------------------------------------------- +// Description: +// The scene's per-frame update, done once and from the main camera before any view is +// rendered, so the frame's object updates and LOD follow the main view. +// -------------------------------------------------------------------------------------- +void EveSpaceSceneRenderDriver::UpdateSceneFrame( const RenderInfo& mainView, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { - if( m_scene ) + // the projection depends on the viewport, so set up the main view's target first + ApplyView( mainView, renderContext ); { - m_scene->SetRenderingInfo( jitterMatrix, mipLodBias, upscalingAmount ); + TimeSection updateSection( m_timers.update, "Update", rootTimer, renderContext ); + m_scene->Update( realTime, simTime ); } + UpdateGpuParticleSystem( renderContext ); } bool EveSpaceSceneRenderDriver::Validate( const Span& destDimensions, const Span& outputs, Be::Time realTime, Be::Time simTime ) @@ -480,14 +491,6 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina PrepareLightManager(); - // The new route renders the reflection faces through RenderView. The old route lets - // the scene render them from within the main view (see RenderView). - if( m_newRoute ) - { - TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); - RenderReflectionFaces( realTime, simTime, rootTimer, renderContext ); - } - const RenderInfo mainView = { m_camera ? m_camera->GetViewMatrix()->GetTransform() : m_view->GetTransform(), m_camera ? m_camera->GetProjection() : m_projection, @@ -499,14 +502,38 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina SceneRender::ViewInfo::MainView( m_settings.enableDistortion ) }; auto colorTarget = m_gpuResourcePool.GetTempTexture( "customBackBuffer", renderSize, m_internalPixelFormat, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); - ViewTargets targets = RenderView( mainView, std::move( colorTarget ), outputs, realTime, simTime, rootTimer, renderContext ); + + ViewTargets targets; + { + auto prevVisualizeMethod = m_scene->m_visualizeMethod; + m_scene->m_visualizeMethod = m_settings.visualizeMethod; + ON_BLOCK_EXIT( [&] { m_scene->m_visualizeMethod = prevVisualizeMethod; } ); + BeginRenderPass( renderContext, { colorTarget }, {} ); + + UpdateSceneFrame( mainView, realTime, simTime, rootTimer, renderContext ); + { + TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); + + m_scene->BeginFrame( renderContext ); + } + + // The new route renders the reflection faces through RenderView. The old route lets + // the scene render them from within the main view (see RenderView). + if( m_newRoute ) + { + TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); + RenderReflectionFaces( realTime, simTime, rootTimer, renderContext ); + } + + targets = RenderView( mainView, colorTarget, outputs, realTime, simTime, rootTimer, renderContext ); + } renderContext.m_esm.SetRenderTarget( 0, *destinations.data ); renderContext.m_esm.SetDepthStencilBuffer( {} ); { TimeSection postProcessSection( m_timers.postProcess, "PostProcess", rootTimer, renderContext ); - m_postProcess->Execute( *destinations.data, std::move( targets.color ), targets.depth, std::move( targets.velocity ), std::move( targets.opaqueColor ), m_scene, m_upscalingContext, m_gpuResourcePool, renderContext ); + m_postProcess->Execute( *destinations.data, std::move( colorTarget ), targets.depth, std::move( targets.velocity ), std::move( targets.opaqueColor ), m_scene, m_upscalingContext, m_gpuResourcePool, renderContext ); } SetCameraToRenderer( renderContext ); { @@ -543,10 +570,10 @@ Matrix EveSpaceSceneRenderDriver::GetJitterMatrix( uint32_t renderWidth, uint32_ m_upscalingContext->GetJitter( jitterX, jitterY ); jitterMatrix = TranslationMatrix( Vector3( jitterX, jitterY, 0 ) ); } - else if( m_postProcess && m_scene && m_scene->GetPostProcess() && m_scene->GetPostProcess()->GetTaaIfAvailable() != nullptr ) + else if( m_scene && m_scene->GetPostProcess() && m_scene->GetPostProcess()->GetTaaIfAvailable() != nullptr ) { auto frameIndex = renderContext.GetPrimaryRenderContext().GetRenderedFrameNumber(); - jitterMatrix = m_postProcess->GetJitter( frameIndex, renderWidth, renderHeight ); + jitterMatrix = Tr2PostProcessRenderer::GetJitter( frameIndex, renderWidth, renderHeight ); } else { @@ -558,19 +585,19 @@ Matrix EveSpaceSceneRenderDriver::GetJitterMatrix( uint32_t renderWidth, uint32_ float EveSpaceSceneRenderDriver::GetMipLodBias() const { - float mipLodBias = 0.0f; - USE_MAIN_THREAD_RENDER_CONTEXT(); - - if( m_scene ) + // 0 if no upscaling is enabled + float mipLodBias = m_upscalingInfo.mipLevelBias; + + // The post process bias belongs to its TAA. A temporal upscaler replaces the TAA and + // its bias already accounts for it. + if( !m_upscalingInfo.temporal && m_scene ) { - if( auto postprocess = m_scene->GetPostProcess() ) + auto postprocess = m_scene->GetPostProcess(); + if( postprocess && postprocess->GetTaaIfAvailable() != nullptr ) { mipLodBias += postprocess->GetMipLodBias(); } } - - // 0 if no upscaling is enabled - mipLodBias += m_upscalingInfo.mipLevelBias; return mipLodBias; } @@ -580,16 +607,16 @@ float EveSpaceSceneRenderDriver::GetMipLodBias() const // colorTarget. Returns the targets the view rendered, for post processing or for // submitting to the reflection probe. // -------------------------------------------------------------------------------------- -EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, Tr2GpuResourcePool::Texture colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { const FeatureSet& featureSet = renderInfo.featureSet; const SceneRender::ViewInfo& sceneView = renderInfo.sceneView; const TextureSize2D& renderSize = renderInfo.renderSize; ViewTargets targets; - targets.color = std::move( colorTarget ); - const Tr2TextureAL& backBuffer = targets.color; - + const Tr2TextureAL& backBuffer = colorTarget; + ApplyView( renderInfo, renderContext ); + const bool renderBackgroundNode = m_background && featureSet.background; if( renderBackgroundNode ) { @@ -605,26 +632,9 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co BeginRenderPass( renderContext, { backBuffer }, depthBuffer ); renderContext.Clear( renderBackgroundNode ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); - - SetCameraToRenderer( renderInfo.view, renderInfo.projection, renderContext ); - PropagateRenderningInfo( renderInfo.jitterMatrix, renderInfo.mipLodBias, renderInfo.upscalingAmount ); - - { - TimeSection updateSection( m_timers.update, "Update", rootTimer, renderContext ); - m_scene->Update( realTime, simTime ); - } - if( sceneView.gpuParticles ) - { - UpdateGpuParticleSystem( renderContext ); - } - - auto prevVisualizeMethod = m_scene->m_visualizeMethod; - m_scene->m_visualizeMethod = m_settings.visualizeMethod; - ON_BLOCK_EXIT( [&] { m_scene->m_visualizeMethod = prevVisualizeMethod; } ); - + { TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); - m_scene->BeginFrame( renderContext ); m_scene->BeginView( sceneView, renderContext ); } // Old route: the scene renders the reflection faces itself, from within the main view @@ -667,6 +677,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co EveSpaceScene::ShadowResources shadowResources; if( m_scene->m_display && m_mainPassRenderingEnabled ) { + if( featureSet.shadows ) { TimeSection shadowsSection( m_timers.shadows, "Shadows", rootTimer, renderContext ); shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, sceneView, renderContext ); @@ -726,8 +737,6 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co normalMap = {}; BeginRenderPass( renderContext, { backBuffer }, depthBuffer ); - // occlusion query results are read by the next frame - if( sceneView.temporal ) { renderContext.SetReadOnlyDepth( true ); m_scene->RunLensflareOcclusionQueries( depthBuffer, renderContext ); @@ -749,7 +758,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co void EveSpaceSceneRenderDriver::RenderReflectionFaces( Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { - Tr2ReflectionProbe* probe = m_reflectionProbe; + Tr2ReflectionProbe* probe = m_scene->GetReflectionProbe(); if( !probe || !probe->IsValid() || !m_scene->m_display ) { return; @@ -777,7 +786,6 @@ void EveSpaceSceneRenderDriver::RenderReflectionFaces( Be::Time realTime, Be::Ti projection.CreateInstance(); projection->CustomProjection( faceProjection ); - const unsigned faceSize = probe->GetFaceSize(); const TextureSize2D faceRenderSize = probe->GetFaceTarget( 0 ).GetDesc(); const bool prevCullInversion = renderContext.m_esm.IsCullModeInverted(); @@ -793,10 +801,11 @@ void EveSpaceSceneRenderDriver::RenderReflectionFaces( Be::Time realTime, Be::Ti RenderInfo renderInfo = { view, projection, IdentityMatrix(), 0.0f, 1.0f, faceRenderSize, reflectionFeatures, faceView }; - auto faceTarget = m_gpuResourcePool.GetTempTexture( "reflectionFace", faceSize, faceSize, probe->GetFaceTarget( face ).GetFormat(), Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); - ViewTargets targets = RenderView( renderInfo, std::move( faceTarget ), {}, realTime, simTime, rootTimer, renderContext ); + // render straight into the probe's face target, then copy it into the cubemap + const Tr2TextureAL& faceTarget = probe->GetFaceTarget( face ); + RenderView( renderInfo, faceTarget, {}, realTime, simTime, rootTimer, renderContext ); - probe->SubmitFace( face, targets.color, renderContext ); + probe->SubmitFace( face, faceTarget, renderContext ); } probe->FinishFaces( viewDirection, renderContext ); @@ -840,13 +849,13 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetDistortionMapIfNeeded( Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) { - if( !featureSet.velocityMap || ( !featureSet.antiAliasing && !featureSet.upscaling && !m_upscalingInfo.temporal ) ) + if( !featureSet.velocityMap ) { return {}; } - USE_MAIN_THREAD_RENDER_CONTEXT(); - bool needsVelocityMap = m_settings.antiAliasingQuality != AntiAliasingQuality::Disabled || m_upscalingInfo.temporal; + // TAA and temporal upscaling reproject with the velocity map + const bool needsVelocityMap = ( featureSet.antiAliasing && m_settings.antiAliasingQuality != AntiAliasingQuality::Disabled ) || ( featureSet.upscaling && m_upscalingInfo.temporal ); auto namedOutput = FindNamedOutput( outputs, "VelocityMap" ); Tr2GpuResourcePool::Texture velocityMap; if( needsVelocityMap || m_settings.forceVelocityMap || namedOutput ) @@ -863,12 +872,10 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( c Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) { - if( !featureSet.ssao && !featureSet.shadows ) - { - return {}; - } + // SSAO and raytraced shadows read the normal map + const bool needsNormalMap = ( featureSet.ssao && m_settings.aoQuality != AmbientOcclusionQuality::Disabled ) || ( featureSet.shadows && m_settings.shadowQuality == ShadowQuality::SHADOW_RAYTRACED ); auto namedOutput = FindNamedOutput( outputs, "NormalMap" ); - if( ( ( m_settings.aoQuality != AmbientOcclusionQuality::Disabled || m_settings.shadowQuality == ShadowQuality::SHADOW_RAYTRACED ) && Tr2Renderer::GetShaderModel() != TR2SM_3_0_LO ) || m_settings.forceNormalMap || namedOutput ) + if( ( needsNormalMap && Tr2Renderer::GetShaderModel() != TR2SM_3_0_LO ) || m_settings.forceNormalMap || namedOutput ) { auto texture = m_gpuResourcePool.GetTempTexture( "normalMap", size, ImageIO::PIXEL_FORMAT_R10G10B10A2_UNORM, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); if( namedOutput ) @@ -897,6 +904,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetCustomStencilMapIfNeed Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) { + // the opaque copy is only used by views that can be upscaled or anti-aliased if( !featureSet.upscaling && !featureSet.antiAliasing ) { return {}; diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 2f1bce875..576f56f38 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -20,7 +20,6 @@ BLUE_DECLARE( TriStepRenderFps ); BLUE_DECLARE_IVECTOR( ITr2Scene ); BLUE_DECLARE( EveCamera ); BLUE_DECLARE( Tr2Sprite2dScene ); -BLUE_DECLARE( Tr2ReflectionProbe ); @@ -44,14 +43,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : Medium, High, }; - enum class ReflectionQuality - { - Disabled, - Low, - Medium, - High, - Ultra, - }; struct Settings { ShadowQuality shadowQuality = ShadowQuality::SHADOW_HIGH; @@ -60,7 +51,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : PostProcess::Quality postProcessingQuality = PostProcess::Quality::HIGH; Tr2VolumerticQuality volumetricQuality = Tr2VolumerticQuality::High; LightingQuality lightingQuality = LightingQuality::HIGH; - ReflectionQuality reflectionQuality = ReflectionQuality::High; Color clearColor = Color( 0.0f, 0.0f, 0.0f, 1.0f ); @@ -80,7 +70,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : bool upscaling = true; bool antiAliasing = true; bool reflections = true; - bool volumetrics = true; bool ssao = true; bool shadows = true; bool velocityMap = true; @@ -130,17 +119,17 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : // Targets rendered by RenderView struct ViewTargets { - Tr2GpuResourcePool::Texture color; Tr2GpuResourcePool::Texture depth; Tr2GpuResourcePool::Texture velocity; Tr2GpuResourcePool::Texture opaqueColor; }; - ViewTargets RenderView( const RenderInfo& renderInfo, Tr2GpuResourcePool::Texture colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + ViewTargets RenderView( const RenderInfo& renderInfo, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void RenderReflectionFaces( Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetupUpscaling( const TextureSize2D& displaySize ); void PropagateSettings(); - void PropagateRenderningInfo( Matrix jitterMatrix, float mipLodBias, float upscaling ); + void ApplyView( const RenderInfo& renderInfo, Tr2RenderContext& renderContext ) const; + void UpdateSceneFrame( const RenderInfo& mainView, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetCameraToRenderer( Tr2RenderContext & renderContext ) const; void SetCameraToRenderer( const Matrix& view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const; TextureSize2D GetRenderSize( const TextureSize2D& displaySize ) const; @@ -180,7 +169,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : Settings m_settings; - Tr2ReflectionProbePtr m_reflectionProbe; Tr2EffectPtr m_distortionEffect; PITr2SceneVector m_toolsScenes; diff --git a/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp b/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp index b3d7c1fbf..10974c1d9 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp @@ -50,15 +50,6 @@ const Be::VarChooser TriRMChooser[] = { { 0 } }; -Be::VarChooser ReflectionQualityChooser[] = { - { "Disabled", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::Disabled ), "" }, - { "Low", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::Low ), "" }, - { "Medium", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::Medium ), "" }, - { "High", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::High ), "" }, - { "Ultra", BeCast( EveSpaceSceneRenderDriver::ReflectionQuality::Ultra ), "" }, - { 0 } -}; - } extern Be::VarChooser EveVisualizerChooser[]; extern const Be::VarChooser Tr2VolumetricQuality_Chooser[]; @@ -68,7 +59,6 @@ BLUE_REGISTER_ENUM_EX( "EveSpaceSceneRenderDriverAntiAliasingQuality", EveSpaceS BLUE_REGISTER_ENUM_EX( "EveSpaceSceneRenderDriverAmbientOcclusionQuality", EveSpaceSceneRenderDriver::AmbientOcclusionQuality, AmbientOcclusionQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); BLUE_REGISTER_ENUM_EX( "ShadowQuality", ShadowQuality, ShadowQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); BLUE_REGISTER_ENUM_EX( "LightingQuality", LightingQuality, LightingQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); -BLUE_REGISTER_ENUM_EX( "ReflectionQuality", EveSpaceSceneRenderDriver::ReflectionQuality, ReflectionQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); @@ -92,12 +82,6 @@ const Be::ClassInfo* EveSpaceSceneRenderDriver::ExposeToBlue() "", Be::READWRITE ) - MAP_ATTRIBUTE( - "reflectionProbe", - m_reflectionProbe, - "", - Be::READWRITE ) - MAP_ATTRIBUTE( "enableRendering", m_enableRendering, @@ -181,13 +165,6 @@ const Be::ClassInfo* EveSpaceSceneRenderDriver::ExposeToBlue() Be::READWRITE | Be::ENUM, ShadowQualityChooser ) - MAP_ATTRIBUTE_WITH_CHOOSER( - "reflectionQuality", - m_settings.reflectionQuality, - "Reflection quality setting. One of trinity.EveSpaceSceneRenderDriverReflectionQuality enum", - Be::READWRITE | Be::ENUM, - ReflectionQualityChooser ) - MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_settings.lightingQuality, diff --git a/trinity/PostProcess/Tr2PostProcessRenderer.cpp b/trinity/PostProcess/Tr2PostProcessRenderer.cpp index 6bac9526d..408c134c4 100644 --- a/trinity/PostProcess/Tr2PostProcessRenderer.cpp +++ b/trinity/PostProcess/Tr2PostProcessRenderer.cpp @@ -644,7 +644,7 @@ void Tr2PostProcessRenderer::SetPostProcessingQuality( PostProcess::Quality qual m_quality = quality; } -Matrix Tr2PostProcessRenderer::GetJitter(uint64_t frameIndex, uint32_t renderWidth, uint32_t renderHeight) const +Matrix Tr2PostProcessRenderer::GetJitter( uint64_t frameIndex, uint32_t renderWidth, uint32_t renderHeight ) { const Vector2 samplingPatterns[] = { Vector2( .125f, -.375f ), Vector2( -.125f, .375f ), diff --git a/trinity/PostProcess/Tr2PostProcessRenderer.h b/trinity/PostProcess/Tr2PostProcessRenderer.h index 2cdefcc9d..9d4c3c9c1 100644 --- a/trinity/PostProcess/Tr2PostProcessRenderer.h +++ b/trinity/PostProcess/Tr2PostProcessRenderer.h @@ -134,7 +134,7 @@ BLUE_CLASS( Tr2PostProcessRenderer ) : PostProcess::Quality GetPostProcessingQuality() const; void SetPostProcessingQuality( PostProcess::Quality quality ); - Matrix GetJitter( uint64_t frameIndex, uint32_t renderWidth, uint32_t renderHeight ) const; + static Matrix GetJitter( uint64_t frameIndex, uint32_t renderWidth, uint32_t renderHeight ); private: struct DownsampleData From 8ba6e6f73e03d80ef6b41656fd8ac7f4b489ef3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Tue, 20 Oct 2026 14:16:19 +0000 Subject: [PATCH 04/20] Removing an unused parameter from GetFrustum in Tr2ReflectionProbe --- trinity/Eve/EveSpaceScene.cpp | 2 +- trinity/Tr2ReflectionProbe.cpp | 2 +- trinity/Tr2ReflectionProbe.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index 07c7c986f..d8bb1a84f 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1968,7 +1968,7 @@ void EveSpaceScene::RenderReflectionPass( Tr2GpuResourcePool& gpuResourcePool, T Tr2Renderer::SetViewTransform( orgViewMatrix ); } - TriFrustum currentFrustum = m_reflectionProbe->GetFrustum( i, renderContext ); + TriFrustum currentFrustum = m_reflectionProbe->GetFrustum( renderContext ); m_updateContext.SetFrustum( currentFrustum ); // get the background reflection renderables from the component registry RenderBackgroundPassObjects( {}, {}, renderContext, BackgroundRenderingReason::BACKGROUND_RENDER_REFLECTION ); diff --git a/trinity/Tr2ReflectionProbe.cpp b/trinity/Tr2ReflectionProbe.cpp index 3388db3a3..8efd9119e 100644 --- a/trinity/Tr2ReflectionProbe.cpp +++ b/trinity/Tr2ReflectionProbe.cpp @@ -70,7 +70,7 @@ bool Tr2ReflectionProbe::HasData() const return m_hasData && m_postFilterTarget->IsValid(); } -TriFrustum Tr2ReflectionProbe::GetFrustum( unsigned face, Tr2RenderContext& renderContext ) +TriFrustum Tr2ReflectionProbe::GetFrustum( Tr2RenderContext& renderContext ) { TriFrustum frustum = TriFrustum(); frustum.DeriveFrustum( &Tr2Renderer::GetViewTransform(), &m_position, &Tr2Renderer::GetProjectionTransform(), renderContext.m_esm.GetViewport() ); diff --git a/trinity/Tr2ReflectionProbe.h b/trinity/Tr2ReflectionProbe.h index aa44c42e0..de55433d2 100644 --- a/trinity/Tr2ReflectionProbe.h +++ b/trinity/Tr2ReflectionProbe.h @@ -38,7 +38,7 @@ BLUE_CLASS( Tr2ReflectionProbe ) : void EndRenderPass( Tr2RenderContext & renderContext ); Tr2TextureAL GetDepthBuffer( unsigned face ); - TriFrustum GetFrustum( unsigned face, Tr2RenderContext& renderContext ); + TriFrustum GetFrustum( Tr2RenderContext& renderContext ); Tr2RenderTargetPtr GetReflection(); void SetPosition( Vector3 position ); From 4be45f544c9adfbbc6ab84a93280c6a71c1574d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Tue, 20 Oct 2026 14:17:34 +0000 Subject: [PATCH 05/20] Adding reflection settings to lights and the ability to show light shadows in the reflection probe --- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 3 +++ trinity/Eve/EveSpaceSceneRenderDriver.h | 3 +++ trinity/Lights/Tr2FactionLight_Blue.cpp | 2 ++ trinity/Lights/Tr2Light.h | 5 ++++- trinity/Lights/Tr2Light_Blue.cpp | 8 ++++++++ trinity/Lights/Tr2PointLight_Blue.cpp | 3 +++ trinity/Lights/Tr2SpotLight_Blue.cpp | 2 ++ trinity/Lights/Tr2TexturedPointLight_Blue.cpp | 2 ++ trinity/Tr2LightManager.h | 7 +++++++ 9 files changed, 34 insertions(+), 1 deletion(-) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 203b1f7b4..65900eb8d 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -775,6 +775,9 @@ void EveSpaceSceneRenderDriver::RenderReflectionFaces( Be::Time realTime, Be::Ti reflectionFeatures.velocityMap = false; reflectionFeatures.background = false; reflectionFeatures.overlay = false; + reflectionFeatures.dynamicLights = false; + reflectionFeatures.dynamicLightShadows = false; + const SceneRender::ViewInfo faceView = SceneRender::ViewInfo::ReflectionFace(); SetCameraToRenderer( renderContext ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 576f56f38..d4ee93ed7 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -77,6 +77,9 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : bool background = true; // special overlay rendering (f.ex texture overlay) bool overlay = true; + + bool dynamicLights = true; + bool dynamicLightShadows = true; }; struct RenderInfo diff --git a/trinity/Lights/Tr2FactionLight_Blue.cpp b/trinity/Lights/Tr2FactionLight_Blue.cpp index c666e10de..d52f4a5d4 100644 --- a/trinity/Lights/Tr2FactionLight_Blue.cpp +++ b/trinity/Lights/Tr2FactionLight_Blue.cpp @@ -39,6 +39,8 @@ const Be::ClassInfo* Tr2FactionLight::ExposeToBlue() MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE( "reflectionShadowCaster", m_lightData.reflectionShadowCaster, "Casts shadows in the reflection, only if it casts shadows regularly.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) diff --git a/trinity/Lights/Tr2Light.h b/trinity/Lights/Tr2Light.h index 1ed08c067..7cec56e34 100644 --- a/trinity/Lights/Tr2Light.h +++ b/trinity/Lights/Tr2Light.h @@ -49,6 +49,8 @@ struct LightData EnumFilter castsShadows; bool isVolumetric; + EnumFilter reflectionMode; + bool reflectionShadowCaster; Be::Time startTime; }; @@ -113,4 +115,5 @@ TYPEDEF_BLUECLASS( Tr2Light ); extern const Be::VarChooser PerLightShadowSettingChooser[]; extern const Be::VarChooser Tr2LightFlagChooser[]; -extern const Be::VarChooser LightingQualityFilterChooser[]; \ No newline at end of file +extern const Be::VarChooser LightingQualityFilterChooser[]; +extern const Be::VarChooser ReflectionQualityFilterChooser[]; \ No newline at end of file diff --git a/trinity/Lights/Tr2Light_Blue.cpp b/trinity/Lights/Tr2Light_Blue.cpp index 0806a5571..cc043b476 100644 --- a/trinity/Lights/Tr2Light_Blue.cpp +++ b/trinity/Lights/Tr2Light_Blue.cpp @@ -21,6 +21,14 @@ const Be::VarChooser LightingQualityFilterChooser[] = { }; BLUE_REGISTER_ENUM_EX( "LightingQualityFilter", uint8_t, LightingQualityFilterChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); +const Be::VarChooser ReflectionQualityFilterChooser[] = { + { "Low", BeCast( 1u << static_cast( ReflectionQuality::LOW ) ), "Active when reflection quality is Low." }, + { "Medium", BeCast( 1u << static_cast( ReflectionQuality::MEDIUM ) ), "Active when reflection quality is Medium." }, + { "High", BeCast( 1u << static_cast( ReflectionQuality::HIGH ) ), "Active when reflection quality is High." }, + { 0 } +}; +BLUE_REGISTER_ENUM_EX( "ReflectionQualityFilter", uint8_t, ReflectionQualityFilterChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); + const Be::VarChooser Tr2LightFlagChooser[] = { { "AFFECTS_SURFACES", BeCast( Tr2LightManager::FLAG_AFFECTS_SURFACES ), "Affects surfaces" }, diff --git a/trinity/Lights/Tr2PointLight_Blue.cpp b/trinity/Lights/Tr2PointLight_Blue.cpp index 238316983..22aba24de 100644 --- a/trinity/Lights/Tr2PointLight_Blue.cpp +++ b/trinity/Lights/Tr2PointLight_Blue.cpp @@ -30,12 +30,15 @@ const Be::ClassInfo* Tr2PointLight::ExposeToBlue() MAP_ATTRIBUTE( "noiseFrequency", m_lightData.noiseFrequency, "Brightness noise frequency\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseOctaves", m_lightData.noiseOctaves, "Brightness turbulence octaves\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) + MAP_ATTRIBUTE_WITH_CHOOSER( "reflectionMode", m_lightData.reflectionMode.m_filter, "Reflection quality settings. Controls whether light is active with a certain reflection quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, ReflectionQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE( "reflectionShadowCaster", m_lightData.reflectionShadowCaster, "Casts shadows in the reflection, only if it casts shadows regularly.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) + MAP_ATTRIBUTE( "lightProfilePath", m_lightProfilePath, diff --git a/trinity/Lights/Tr2SpotLight_Blue.cpp b/trinity/Lights/Tr2SpotLight_Blue.cpp index 816d61331..908531939 100644 --- a/trinity/Lights/Tr2SpotLight_Blue.cpp +++ b/trinity/Lights/Tr2SpotLight_Blue.cpp @@ -35,6 +35,8 @@ const Be::ClassInfo* Tr2SpotLight::ExposeToBlue() MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE( "reflectionShadowCaster", m_lightData.reflectionShadowCaster, "Casts shadows in the reflection, only if it casts shadows regularly.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) diff --git a/trinity/Lights/Tr2TexturedPointLight_Blue.cpp b/trinity/Lights/Tr2TexturedPointLight_Blue.cpp index 7eaed997b..c5d1e112f 100644 --- a/trinity/Lights/Tr2TexturedPointLight_Blue.cpp +++ b/trinity/Lights/Tr2TexturedPointLight_Blue.cpp @@ -35,6 +35,8 @@ const Be::ClassInfo* Tr2TexturedPointLight::ExposeToBlue() MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE( "reflectionShadowCaster", m_lightData.reflectionShadowCaster, "Casts shadows in the reflection, only if it casts shadows regularly.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) diff --git a/trinity/Tr2LightManager.h b/trinity/Tr2LightManager.h index 260664815..2c101b808 100644 --- a/trinity/Tr2LightManager.h +++ b/trinity/Tr2LightManager.h @@ -38,6 +38,13 @@ enum class LightingQuality : int32_t HIGH, }; +enum class ReflectionQuality : int32_t +{ + LOW, + MEDIUM, + HIGH, +}; + // -------------------------------------------------------------------------------------- // Description: // Manages light sources for tiled/clustered lighting. An object of this class does not From fe775b8d339888160124808bc813960b56b83d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Mon, 26 Oct 2026 13:52:13 +0000 Subject: [PATCH 06/20] Adding dynamic lights to the reflection probe Reflection probe faces get their own dynamic lights. Each face is lit by the lights tagged for the current reflection quality, and face fog uses the face's own volumetric lights. Light manager: - One light store per view (Tr2LightManager::View): the main view plus one per face rendered in a frame (1 or 6, by the probe's render frequency). Face views are only recreated when that count changes. - AddLight tests each light against every view and keeps a separate copy per view (fade, flags). Face views take only lights tagged for the current reflection quality and never cast shadows. - Resolve, list building and the getters work on the active view. SetActiveView binds that view's buffers; SetViewFrustum sets a frustum without activating the view. - Face views: 16 volumetric lights, no shadow casters, 1M-entry index buffer. Lights: - Tr2LightTags on LightData. The reflectionMode attribute is exposed on point, spot, textured point and faction lights. ReflectionQuality gains OFF and ULTRA. Removed the unused reflectionShadowCaster attribute and the duplicate castsShadows registrations. Scene and driver: - SceneRender::View and Features replace the driver's RenderInfo/FeatureSet and the scene's ViewInfo parameters. The view's frustum is computed when the view is built, not taken from the bound viewport. - Lights are gathered once per frame, by the driver, for the main view and the faces, instead of once per view in BeginView. - The driver sets the reflection quality on the light manager, prepares the reflection faces before the gather and activates each view's lights in RenderView. - The probe's first pass only draws the background into the faces. Tr2ReflectionProbe gets GetRenderFrequency() and IsFirstPass(). --- trinity/Eve/EveSpaceScene.cpp | 201 ++++++---- trinity/Eve/EveSpaceScene.h | 75 +++- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 233 ++++++++---- trinity/Eve/EveSpaceSceneRenderDriver.h | 59 ++- trinity/Lights/Tr2FactionLight_Blue.cpp | 3 +- trinity/Lights/Tr2Light.cpp | 4 +- trinity/Lights/Tr2Light.h | 4 +- trinity/Lights/Tr2Light_Blue.cpp | 1 + trinity/Lights/Tr2PointLight_Blue.cpp | 3 +- trinity/Lights/Tr2SpotLight_Blue.cpp | 3 +- trinity/Lights/Tr2TexturedPointLight_Blue.cpp | 3 +- trinity/Tr2LightManager.cpp | 344 ++++++++++++------ trinity/Tr2LightManager.h | 64 +++- trinity/Tr2ReflectionProbe.cpp | 10 + trinity/Tr2ReflectionProbe.h | 3 + 15 files changed, 691 insertions(+), 319 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index d8bb1a84f..f546339ab 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -171,6 +171,91 @@ ViewInfo ViewInfo::ReflectionFace() view.lensflares = g_lensflaresInReflections && g_eveReflectionMode == EntityComponents::REFLECTION_SETTING_ULTRA; return view; } + +Features Features::ReflectionFace() +{ + // faces are rendered without TAA/upscaling, SSAO, velocity or the render nodes + Features features; + features.upscaling = false; + features.antiAliasing = false; + features.reflections = false; // no reflections rendered in the reflection faces, this can be removed when m_newRoute is removed + features.ssao = false; + features.velocityMap = false; + features.background = false; + features.overlay = false; + features.dynamicLights = false; + features.dynamicLightShadows = false; + return features; +} + +Features Features::ReflectionBackground() +{ + // the probe's first pass: nothing but the background + Features features = ReflectionFace(); + features.shadows = false; + features.background = true; + return features; +} + +namespace +{ +TriFrustum DeriveViewFrustum( const Matrix& view, const Matrix& projection, const Matrix& jitterMatrix, const TextureSize2D& renderSize ) +{ + const Matrix jitteredProjection = projection * jitterMatrix; + const Matrix inverseView = Inverse( view ); + const Vector3 position( inverseView._41, inverseView._42, inverseView._43 ); + + CTriViewport viewport; + viewport.width = renderSize.width; + viewport.height = renderSize.height; + + TriFrustum frustum; + frustum.DeriveFrustum( &view, &position, &jitteredProjection, viewport ); + return frustum; +} +} + +View View::Main( const Matrix& view, const TriProjectionPtr& projection, const Matrix& jitterMatrix, float mipLodBias, float upscalingAmount, const TextureSize2D& renderSize, bool distortion ) +{ + View result; + result.view = view; + result.projectionSource = projection; + result.projection = projection->GetTransform(); + result.jitterMatrix = jitterMatrix; + result.renderSize = renderSize; + result.mipLodBias = mipLodBias; + result.upscalingAmount = upscalingAmount; + result.frustum = DeriveViewFrustum( result.view, result.projection, result.jitterMatrix, result.renderSize ); + result.lightView = 0; + result.scene = ViewInfo::MainView( distortion ); + return result; +} + +View View::ReflectionFace( unsigned face, const Tr2ReflectionProbe& probe, const TriProjectionPtr& faceProjection, const TextureSize2D& faceRenderSize, bool backgroundOnly ) +{ + View result; + result.view = probe.GetFaceViewTransform( face, probe.GetPosition() ); + result.projectionSource = faceProjection; + result.projection = faceProjection->GetTransform(); + result.jitterMatrix = IdentityMatrix(); + result.renderSize = faceRenderSize; + result.mipLodBias = 0.0f; + result.upscalingAmount = 1.0f; + result.frustum = DeriveViewFrustum( result.view, result.projection, result.jitterMatrix, result.renderSize ); + result.scene = ViewInfo::ReflectionFace(); + if( backgroundOnly ) + { + result.lightView = 0; + result.scene.lensflares = false; + result.features = Features::ReflectionBackground(); + } + else + { + result.lightView = 1 + ( face - probe.GetStartFace() ); + result.features = Features::ReflectionFace(); + } + return result; +} } struct ShadowReceiver @@ -478,7 +563,6 @@ void EveSpaceScene::Update( Be::Time realTime, Be::Time simTime ) if( frame == m_lastUpdateFrame ) { // already updated this frame - UpdateView(); return; } m_lastUpdateFrame = frame; @@ -490,27 +574,25 @@ void EveSpaceScene::Update( Be::Time realTime, Be::Time simTime ) // -------------------------------------------------------------------------------------- // Description: -// Per-view part of the update: called for every view after the frame's Update, so -// culling and post process attributes follow the view's camera. +// Per-view part of the update: called for every view before the frame's Update and +// before it is rendered, so culling and post process attributes follow the view's +// camera. The frustum comes from the view, not from the bound viewport. // -------------------------------------------------------------------------------------- -void EveSpaceScene::UpdateView() +void EveSpaceScene::UpdateView( const SceneRender::View& view ) { - TriFrustum frustum; - frustum.DeriveFrustum( &Tr2Renderer::GetViewTransform(), &Tr2Renderer::GetViewPosition(), &Tr2Renderer::GetProjectionTransform(), Tr2Renderer::GetViewport() ); - - m_updateContext.SetFrustum( frustum ); - m_updateContext.SetHighDetailThreshold( g_eveSpaceSceneHighDetailThreshold / m_upscalingAmount ); - m_updateContext.SetMediumDetailThreshold( g_eveSpaceSceneMediumDetailThreshold / m_upscalingAmount ); - m_updateContext.SetLowDetailThreshold( g_eveSpaceSceneLowDetailThreshold / m_upscalingAmount ); - m_updateContext.SetVisibilityThreshold( g_eveSpaceSceneVisibilityThreshold / m_upscalingAmount ); - m_updateContext.SetLodFactor( g_eveSpaceSceneLODFactor / m_upscalingAmount ); + m_updateContext.SetFrustum( view.frustum ); + m_updateContext.SetHighDetailThreshold( g_eveSpaceSceneHighDetailThreshold / view.upscalingAmount ); + m_updateContext.SetMediumDetailThreshold( g_eveSpaceSceneMediumDetailThreshold / view.upscalingAmount ); + m_updateContext.SetLowDetailThreshold( g_eveSpaceSceneLowDetailThreshold / view.upscalingAmount ); + m_updateContext.SetVisibilityThreshold( g_eveSpaceSceneVisibilityThreshold / view.upscalingAmount ); + m_updateContext.SetLodFactor( g_eveSpaceSceneLODFactor / view.upscalingAmount ); m_updateContext.m_raytracingEnabled = m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows; } // -------------------------------------------------------------------------------------- // Description: -// Per-frame part of the update: animates and updates all objects, using the current -// camera for LOD. +// Per-frame part of the update: animates and updates all objects, using the camera of +// the view applied with UpdateView before it for LOD. // -------------------------------------------------------------------------------------- void EveSpaceScene::UpdateFrame( Be::Time realTime, Be::Time simTime ) { @@ -523,9 +605,6 @@ void EveSpaceScene::UpdateFrame( Be::Time realTime, Be::Time simTime ) m_updateContext.UpdateOrigin( m_ballpark ); m_updateContext.SetDataTextureManager( m_dataTextureMgr ); - // objects use the frustum and thresholds for LOD while they update - UpdateView(); - // update the combined postprocess attributes UpdatePostProcessAttributes(); @@ -1334,10 +1413,10 @@ void EveSpaceScene::BeginFrame( Tr2RenderContext& renderContext ) // -------------------------------------------------------------------------------------- // Description: -// Per-view setup: lighting values, states, and frustum dependent gathering of batches -// and lights. Called for the main view and for each reflection face, after BeginFrame. +// Per-view setup: lighting values, states, and frustum dependent gathering of batches. +// Called for the main view and for each reflection face, after BeginFrame. // -------------------------------------------------------------------------------------- -void EveSpaceScene::BeginView( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) +void EveSpaceScene::BeginView( const SceneRender::View& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -1356,23 +1435,22 @@ void EveSpaceScene::BeginView( const SceneRender::ViewInfo& view, Tr2RenderConte // the previous view's value. It is read whenever the per-frame data is populated. if( m_volumetricsRenderer ) { - m_volumetricsRenderer->SetIsReflectionProbe( view.reflection ); + m_volumetricsRenderer->SetIsReflectionProbe( view.scene.reflection ); } - renderContext.m_esm.BeginManagedRendering( view.cullMode ); + renderContext.m_esm.BeginManagedRendering( view.scene.cullMode ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_OPAQUE ); GatherBatches( view, renderContext ); // reflection views use the reflection shadow map, which is not raytraced - if( !view.reflection && m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows ) + if( !view.scene.reflection && m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows ) { PrepareRaytracedShadows( renderContext ); } UpdateVariableStore(); - GatherDynamicLights( renderContext ); PrepareLensflares(); PopulatePerFramePSData( m_perFramePS, renderContext ); @@ -1447,21 +1525,23 @@ static float GetReflectionIntensityInReflections( float reflectionIntensity ) // Description: // Derive the lighting values for the current view from the per-frame base values. // -------------------------------------------------------------------------------------- -void EveSpaceScene::ApplyViewLighting( const SceneRender::ViewInfo& view ) +void EveSpaceScene::ApplyViewLighting( const SceneRender::View& view ) { m_currentSunColor = m_baseSunColor; m_currentNebulaIntensity = m_baseNebulaIntensity; - m_currentReflectionIntensity = view.reflection ? GetReflectionIntensityInReflections( m_baseReflectionIntensity ) : m_baseReflectionIntensity; + m_currentReflectionIntensity = view.scene.reflection ? GetReflectionIntensityInReflections( m_baseReflectionIntensity ) : m_baseReflectionIntensity; } // -------------------------------------------------------------------------------------- // Description: -// Gather the dynamic lights visible in the current frustum. +// Gather the dynamic lights visible in the frustum of each of the light manager's views. +// The caller sets the frustums (SetViewFrustum) and calls this once per frame, after the +// main view's update and BeginFrame and before the first view is rendered. // -------------------------------------------------------------------------------------- void EveSpaceScene::GatherDynamicLights( Tr2RenderContext& renderContext ) { auto lightManager = Tr2LightManager::GetInstance(); - if( !lightManager ) + if( !m_display || !lightManager ) { return; } @@ -1470,7 +1550,6 @@ void EveSpaceScene::GatherDynamicLights( Tr2RenderContext& renderContext ) lightManager->SetShadowQuality( m_shadowQuality, renderContext.GetPrimaryRenderContextPointer()->GetRecordingFrameNumber() ); lightManager->Clear( renderContext ); - lightManager->SetFrustum( m_updateContext.GetFrustum() ); lightManager->AdjustLightCutoff( m_updateContext.GetLodFactor() ); auto lightOwners = m_componentRegistry->GetComponents(); @@ -1499,7 +1578,7 @@ void EveSpaceScene::PrepareLensflares() // Description: // Gather all batches into the m_primaryBatches BatchMap. // -------------------------------------------------------------------------------------- -void EveSpaceScene::GatherBatches( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) +void EveSpaceScene::GatherBatches( const SceneRender::View& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -1510,7 +1589,7 @@ void EveSpaceScene::GatherBatches( const SceneRender::ViewInfo& view, Tr2RenderC const Matrix& identity = IdentityMatrix(); // Reflection views only get the dynamic objects once the probe is ready for them - const bool gatherObjects = view.sceneObjects || ( m_dynamicObjectReflectionEnabled && m_reflectionProbe && m_reflectionProbe->ReadyForDynamicObjectReflections() ); + const bool gatherObjects = view.scene.sceneObjects || ( m_dynamicObjectReflectionEnabled && m_reflectionProbe && m_reflectionProbe->ReadyForDynamicObjectReflections() ); { TRINITY_STATS_ZONE( "UpdateVisibility" ); @@ -1526,7 +1605,7 @@ void EveSpaceScene::GatherBatches( const SceneRender::ViewInfo& view, Tr2RenderC } ); } - if( view.sceneObjects ) + if( view.scene.sceneObjects ) { GatherSceneObjectRenderables( allObjects, renderables, renderContext ); } @@ -1539,15 +1618,15 @@ void EveSpaceScene::GatherBatches( const SceneRender::ViewInfo& view, Tr2RenderC Tr2QuadRenderer::Instance()->GetBatches( TRIBATCHTYPE_OPAQUE, m_primaryBatches[TRIBATCHTYPE_OPAQUE] ); Tr2QuadRenderer::Instance()->GetBatches( TRIBATCHTYPE_ADDITIVE, m_primaryBatches[TRIBATCHTYPE_ADDITIVE] ); - GetAllBatchesFromRenderables( renderables, transparentObjects, view.distortion, m_primaryBatches, view.reason ); - PrepareTransparentBatch( transparentObjects, m_primaryBatches, view.reason ); + GetAllBatchesFromRenderables( renderables, transparentObjects, view.scene.distortion, m_primaryBatches, view.scene.reason ); + PrepareTransparentBatch( transparentObjects, m_primaryBatches, view.scene.reason ); if( gatherObjects ) { m_instancedMeshManager->CollectMeshes( *m_componentRegistry ); // passing the reason filters out instanced meshes that should not be reflected - m_instancedMeshManager->GetBatches( m_updateContext.GetFrustum(), m_updateContext.GetInvLodFactor(), { { TRIBATCHTYPE_OPAQUE, *m_primaryBatches[TRIBATCHTYPE_OPAQUE] }, { TRIBATCHTYPE_DECAL, *m_primaryBatches[TRIBATCHTYPE_DECAL] }, { TRIBATCHTYPE_ADDITIVE, *m_primaryBatches[TRIBATCHTYPE_ADDITIVE] }, { TRIBATCHTYPE_DEPTH, *m_primaryBatches[TRIBATCHTYPE_DEPTH] }, { TRIBATCHTYPE_DISTORTION, *m_primaryBatches[TRIBATCHTYPE_DISTORTION] } }, view.reason ); + m_instancedMeshManager->GetBatches( m_updateContext.GetFrustum(), m_updateContext.GetInvLodFactor(), { { TRIBATCHTYPE_OPAQUE, *m_primaryBatches[TRIBATCHTYPE_OPAQUE] }, { TRIBATCHTYPE_DECAL, *m_primaryBatches[TRIBATCHTYPE_DECAL] }, { TRIBATCHTYPE_ADDITIVE, *m_primaryBatches[TRIBATCHTYPE_ADDITIVE] }, { TRIBATCHTYPE_DEPTH, *m_primaryBatches[TRIBATCHTYPE_DEPTH] }, { TRIBATCHTYPE_DISTORTION, *m_primaryBatches[TRIBATCHTYPE_DISTORTION] } }, view.scene.reason ); m_instancedMeshManager->ReportUsedScreenSizes(); } @@ -2095,7 +2174,7 @@ void EveSpaceScene::RenderReflectionPass( Tr2GpuResourcePool& gpuResourcePool, T // Returns: // boolean indicating whether background distortion is required. // -------------------------------------------------------------------------------------- -bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) +bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const SceneRender::View& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -2154,7 +2233,7 @@ bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr else { renderContext.RenderPassHint( { Tr2LoadAction::LOAD, Tr2StoreAction::STORE }, { Tr2LoadAction::LOAD, Tr2StoreAction::STORE } ); - hasBackgroundDistortionBatches = RenderBackgroundPassObjects( depthMap, distortionMap, renderContext, view.reflection ? BACKGROUND_RENDER_REFLECTION : BACKGROUND_RENDER_COLOR ); + hasBackgroundDistortionBatches = RenderBackgroundPassObjects( depthMap, distortionMap, renderContext, view.scene.reflection ? BACKGROUND_RENDER_REFLECTION : BACKGROUND_RENDER_COLOR ); } if( !m_planets.empty() ) { @@ -2291,7 +2370,7 @@ bool EveSpaceScene::RenderBackgroundPassObjects( const Tr2TextureAL& depthMap, c // Description: // Render opaque objects to populate a readable depth stencil. // -------------------------------------------------------------------------------------- -void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName ) +void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const SceneRender::View& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -2300,7 +2379,7 @@ void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2Text return; } - renderContext.m_esm.BeginManagedRendering( view.cullMode ); + renderContext.m_esm.BeginManagedRendering( view.scene.cullMode ); { // Update mesh morphs auto& meshMorphs = m_componentRegistry->GetComponents(); @@ -2730,14 +2809,14 @@ EveSpaceScene::ShadowResources EveSpaceScene::RenderReflectionShadows( const Tr2 return result; } -EveSpaceScene::ShadowResources EveSpaceScene::RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) +EveSpaceScene::ShadowResources EveSpaceScene::RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const SceneRender::View& view, Tr2RenderContext& renderContext ) { if( !m_display || !m_enableShadows ) { return {}; } - if( view.reflection ) + if( view.scene.reflection ) { return RenderReflectionShadows( depthMap, gpuResourcePool, renderContext ); } @@ -2813,14 +2892,14 @@ bool EveSpaceScene::RenderMainPass( const Tr2TextureAL& velocityMap, const Tr2TextureAL& opaqueColorMap, Tr2GpuResourcePool& gpuResourcePool, - const SceneRender::ViewInfo& view, + const SceneRender::View& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); bool hasForegroundDistortionBatches = false; - renderContext.m_esm.BeginManagedRendering( view.cullMode ); + renderContext.m_esm.BeginManagedRendering( view.scene.cullMode ); if( !m_display ) { @@ -2870,7 +2949,7 @@ bool EveSpaceScene::RenderMainPass( GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", opaqueColorMap ); } - if( view.subsurfaceScattering ) + if( view.scene.subsurfaceScattering ) { m_sssss->SetupScreenSpaceSubSurfaceScattering( renderContext, m_primaryBatches[TRIBATCHTYPE_OPAQUE], colorMap, opaqueColorMap, depthMap, gpuResourcePool ); } @@ -2880,11 +2959,11 @@ bool EveSpaceScene::RenderMainPass( PopulateAndApplyPerFrameData( renderContext ); std::pair froxelFogTextures; - if( view.reason == TR2RENDERREASON_NORMAL ) + if( view.scene.reason == TR2RENDERREASON_NORMAL ) { froxelFogTextures = RenderVolumetrics( depthMap, gpuResourcePool, renderContext ); } - else if( view.reason == TR2RENDERREASON_REFLECTION ) + else if( view.scene.reason == TR2RENDERREASON_REFLECTION ) { froxelFogTextures = RenderReflectionVolumetrics( depthMap, gpuResourcePool, renderContext ); } @@ -2902,7 +2981,7 @@ bool EveSpaceScene::RenderMainPass( } //GPU particles - if( view.gpuParticles && GetGpuParticleSystem() ) + if( view.scene.gpuParticles && GetGpuParticleSystem() ) { GetGpuParticleSystem()->Render( renderContext ); } @@ -2949,7 +3028,7 @@ void EveSpaceScene::RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, // Description: // Render lensflares. Clear primary batches and frame dependant object lists. // -------------------------------------------------------------------------------------- -void EveSpaceScene::EndRender( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ) +void EveSpaceScene::EndRender( const SceneRender::View& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -2962,7 +3041,7 @@ void EveSpaceScene::EndRender( const SceneRender::ViewInfo& view, Tr2RenderConte Tr2QuadRenderer::Instance()->DoneRendering( renderContext ); - renderContext.m_esm.BeginManagedRendering( view.cullMode ); + renderContext.m_esm.BeginManagedRendering( view.scene.cullMode ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_OPAQUE ); PopulatePerFramePSData( m_perFramePS, renderContext ); @@ -2994,7 +3073,7 @@ void EveSpaceScene::EndRender( const SceneRender::ViewInfo& view, Tr2RenderConte } // lensflares - if( !m_lensflares.empty() && view.lensflares ) + if( !m_lensflares.empty() && view.scene.lensflares ) { GPU_REGION( renderContext, "Lens Flares" ); // lensflares @@ -3011,7 +3090,7 @@ void EveSpaceScene::EndRender( const SceneRender::ViewInfo& view, Tr2RenderConte TRIBATCHTYPE_ADDITIVE, Tr2EffectStateManager::RM_ALPHA_ADDITIVE, renderContext, - view.reason ); + view.scene.reason ); renderContext.SetReadOnlyDepth( false ); } } @@ -3022,7 +3101,7 @@ void EveSpaceScene::EndRender( const SceneRender::ViewInfo& view, Tr2RenderConte ClearBatches( m_primaryBatches ); // Store the view transform from this frame - if( view.temporal ) + if( view.scene.temporal ) { m_viewLast = Tr2Renderer::GetViewTransform(); m_projectionLast = m_projection; @@ -4452,16 +4531,16 @@ void RegisterWithVariableStore( const EveSpaceScene::ShadowResources& shadowReso // -------------------------------------------------------------------------------------- // Description: // Set the view's camera matrices and rendering parameters, and apply the jittered -// projection to the renderer. Only depends on its arguments, so it is safe to call -// more than once for a view. +// projection to the renderer. Only depends on the view, so it is safe to call more than +// once for a view. // -------------------------------------------------------------------------------------- -void EveSpaceScene::SetViewInfo( const Matrix& view, const Matrix& projection, const Matrix& jitterMatrix, float mipLodBias, float upscaling ) +void EveSpaceScene::SetViewInfo( const SceneRender::View& view ) { - m_projection = projection; - m_jitterMatrix = jitterMatrix; + m_projection = view.projection; + m_jitterMatrix = view.jitterMatrix; m_jitteredProjection = m_projection * m_jitterMatrix; - m_reprojectionMatrix = Inverse( m_projection ) * Inverse( view ) * m_viewLast * m_projectionLast; - m_upscalingAmount = upscaling; - m_mipLodBias = mipLodBias; + m_reprojectionMatrix = Inverse( m_projection ) * Inverse( view.view ) * m_viewLast * m_projectionLast; + m_upscalingAmount = view.upscalingAmount; + m_mipLodBias = view.mipLodBias; Tr2Renderer::SetProjectionTransform( m_jitteredProjection ); } diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 95878f0c5..40fc3628b 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -83,6 +83,7 @@ BLUE_DECLARE( Tr2ImpostorManager ); BLUE_DECLARE( Tr2DebugRenderer ); BLUE_DECLARE( EveEffectRoot2 ); BLUE_DECLARE( Tr2ReflectionProbe ); +BLUE_DECLARE( TriProjection ); BLUE_DECLARE( EvePlanet ); BLUE_DECLARE_VECTOR( EvePlanet ); BLUE_DECLARE( EveVirtualCameraSystem ); @@ -98,9 +99,9 @@ class EveInstancedMeshManager; namespace SceneRender { -// Describes the view being rendered and what it renders. Built by the render driver with -// MainView or ReflectionFace and passed to every per-view call; the scene reads these -// flags rather than deciding per call site what a kind of view gets.S +// Describes the view being rendered and what it renders. Built with MainView or +// ReflectionFace as part of a View, which is passed to every per-view call; the scene reads +// these flags rather than deciding per call site what a kind of view gets.S struct ViewInfo { // Passed on to GetBatches, so renderables can filter themselves (e.g. ShouldReflect) @@ -124,6 +125,54 @@ struct ViewInfo static ViewInfo ReflectionFace(); }; +// Driver side features of a view. What the scene renders is described by the ViewInfo in +// View. +struct Features +{ + bool upscaling = true; + bool antiAliasing = true; + bool reflections = true; + bool ssao = true; + bool shadows = true; + bool velocityMap = true; + // special background rendering (f.ex texture background) + bool background = true; + // special overlay rendering (f.ex texture overlay) + bool overlay = true; + + bool dynamicLights = true; + bool dynamicLightShadows = true; + + static Features ReflectionFace(); + static Features ReflectionBackground(); +}; + +// A view to render: its camera, what the scene renders from it (scene) and which driver +// features it uses (features). Built by the render driver with Main or ReflectionFace. +struct View +{ + Matrix view; + // sets the renderer's projection + TriProjectionPtr projectionSource; + Matrix projection; + Matrix jitterMatrix; + TextureSize2D renderSize; + float mipLodBias = 0.0f; + float upscalingAmount = 1.0f; + // derived from the view, the jittered projection and renderSize + TriFrustum frustum; + + // The Tr2LightManager view of this view: 0 is the main view, 1.. are reflection probe faces + uint32_t lightView = 0; + + ViewInfo scene; + Features features; + + static View Main( const Matrix& view, const TriProjectionPtr& projection, const Matrix& jitterMatrix, float mipLodBias, float upscalingAmount, const TextureSize2D& renderSize, bool distortion ); + // backgroundOnly: the probe's first pass, which draws only the background into the faces + static View ReflectionFace( unsigned face, const Tr2ReflectionProbe& probe, const TriProjectionPtr& faceProjection, const TextureSize2D& faceRenderSize, bool backgroundOnly = false ); +}; + } @@ -161,7 +210,7 @@ BLUE_CLASS( EveSpaceScene ) : Tr2GpuResourcePool::Texture pointLightShadowDepth; }; - ShadowResources RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); + ShadowResources RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const SceneRender::View& view, Tr2RenderContext& renderContext ); bool RenderMainPass( const Tr2TextureAL& colorMap, const Tr2TextureAL& depthMap, @@ -169,25 +218,25 @@ BLUE_CLASS( EveSpaceScene ) : const Tr2TextureAL& velocityMap, const Tr2TextureAL& opaqueColorMap, Tr2GpuResourcePool& gpuResourcePool, - const SceneRender::ViewInfo& view, + const SceneRender::View& view, Tr2RenderContext& renderContext ); void RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ); - void RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName = BlueSharedString( "Depth" ) ); + void RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const SceneRender::View& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName = BlueSharedString( "Depth" ) ); - bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); + bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const SceneRender::View& view, Tr2RenderContext& renderContext ); void RenderReflectionPass( Tr2GpuResourcePool & gpuResourcePool, Tr2RenderContext & renderContext ); void BeginFrame( Tr2RenderContext& renderContext ); - void BeginView( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); - void EndRender( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); + void BeginView( const SceneRender::View& view, Tr2RenderContext& renderContext ); + void EndRender( const SceneRender::View& view, Tr2RenderContext& renderContext ); void Render3DUI( Tr2RenderContext & renderContext ); void PopulateAndApplyPerFrameData( Tr2RenderContext& renderContext ); void ApplyUpscalingToPerFrameData( uint32_t width, uint32_t height, Tr2RenderContext& renderContext ); - void GatherBatches( const SceneRender::ViewInfo& view, Tr2RenderContext& renderContext ); + void GatherBatches( const SceneRender::View& view, Tr2RenderContext& renderContext ); void PrepareRaytracedShadows( Tr2RenderContext & renderContext ); - void SetViewInfo( const Matrix& view, const Matrix& projection, const Matrix& jitterMatrix, float mipLodBias, float upscaling ); - void UpdateView(); + void SetViewInfo( const SceneRender::View& view ); + void UpdateView( const SceneRender::View& view ); Tr2ReflectionProbe* GetReflectionProbe() const; ////////////////////////////////////////////////////////////////////////////////////// @@ -640,7 +689,7 @@ BLUE_CLASS( EveSpaceScene ) : void UpdateFrame( Be::Time realTime, Be::Time simTime ); void EnsureVisualizerEffect(); void ResolveLightingOverrides(); - void ApplyViewLighting( const SceneRender::ViewInfo& view ); + void ApplyViewLighting( const SceneRender::View& view ); void GatherDynamicLights( Tr2RenderContext& renderContext ); void PrepareLensflares(); void GatherSceneObjectRenderables( std::vector& allObjects, std::vector& renderables, Tr2RenderContext& renderContext ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 65900eb8d..3ad19be6e 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -300,18 +300,40 @@ void EveSpaceSceneRenderDriver::PropagateSettings() { m_scene->m_volumetricsRenderer->SetQuality( m_settings.volumetricQuality ); } + + // The reflection quality is set from the global variable g_eveReflectionMode, which is set from the graphics settings. + // Leaving the global in for now, until a broader refactor is done. + switch( g_eveReflectionMode ) + { + case EntityComponents::ReflectionSetting::REFLECTION_SETTING_OFF: + m_settings.reflectionQuality = ReflectionQuality::OFF; + break; + case EntityComponents::ReflectionSetting::REFLECTION_SETTING_LOW: + m_settings.reflectionQuality = ReflectionQuality::LOW; + break; + case EntityComponents::ReflectionSetting::REFLECTION_SETTING_MEDIUM: + m_settings.reflectionQuality = ReflectionQuality::MEDIUM; + break; + case EntityComponents::ReflectionSetting::REFLECTION_SETTING_HIGH: + m_settings.reflectionQuality = ReflectionQuality::HIGH; + break; + case EntityComponents::ReflectionSetting::REFLECTION_SETTING_ULTRA: + m_settings.reflectionQuality = ReflectionQuality::ULTRA; + break; + default: + break; + } } // -------------------------------------------------------------------------------------- // Description: // Set the view's camera to the renderer and its rendering parameters to the scene. // -------------------------------------------------------------------------------------- -void EveSpaceSceneRenderDriver::ApplyView( const RenderInfo& renderInfo, Tr2RenderContext& renderContext ) const +void EveSpaceSceneRenderDriver::ApplyView( const SceneRender::View& view, Tr2RenderContext& renderContext ) const { - SetCameraToRenderer( renderInfo.view, renderInfo.projection, renderContext ); - // SetProjection adjusts the projection to the viewport, so read back the result - m_scene->SetViewInfo( renderInfo.view, Tr2Renderer::GetProjectionTransform(), renderInfo.jitterMatrix, renderInfo.mipLodBias, renderInfo.upscalingAmount ); - m_scene->UpdateView(); + SetCameraToRenderer( view.view, view.projectionSource, renderContext ); + m_scene->SetViewInfo( view ); + m_scene->UpdateView( view ); } // -------------------------------------------------------------------------------------- @@ -319,7 +341,7 @@ void EveSpaceSceneRenderDriver::ApplyView( const RenderInfo& renderInfo, Tr2Rend // The scene's per-frame update, done once and from the main camera before any view is // rendered, so the frame's object updates and LOD follow the main view. // -------------------------------------------------------------------------------------- -void EveSpaceSceneRenderDriver::UpdateSceneFrame( const RenderInfo& mainView, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +void EveSpaceSceneRenderDriver::UpdateSceneFrame( const SceneRender::View& mainView, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { // the projection depends on the viewport, so set up the main view's target first ApplyView( mainView, renderContext ); @@ -448,7 +470,16 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina return; } - SetCameraToRenderer( renderContext ); + const TextureSize2D displaySize = destinations.size > 0 ? TextureSize2D( destinations.data->GetDesc() ) : TextureSize2D(); + const SceneRender::View mainView = SceneRender::View::Main( + m_camera ? m_camera->GetViewMatrix()->GetTransform() : m_view->GetTransform(), + m_camera ? m_camera->GetProjection() : m_projection, + IdentityMatrix(), + 0.0f, + m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f, + GetRenderSize( displaySize ), + m_settings.enableDistortion ); + ApplyView( mainView, renderContext ); m_scene->Update( realTime, simTime ); UpdateGpuParticleSystem( renderContext ); return; @@ -491,16 +522,14 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina PrepareLightManager(); - const RenderInfo mainView = { + const SceneRender::View mainView = SceneRender::View::Main( m_camera ? m_camera->GetViewMatrix()->GetTransform() : m_view->GetTransform(), m_camera ? m_camera->GetProjection() : m_projection, GetJitterMatrix( renderSize.width, renderSize.height, renderContext ), GetMipLodBias(), m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f, renderSize, - FeatureSet{}, - SceneRender::ViewInfo::MainView( m_settings.enableDistortion ) - }; + m_settings.enableDistortion ); auto colorTarget = m_gpuResourcePool.GetTempTexture( "customBackBuffer", renderSize, m_internalPixelFormat, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); ViewTargets targets; @@ -511,10 +540,28 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina BeginRenderPass( renderContext, { colorTarget }, {} ); UpdateSceneFrame( mainView, realTime, simTime, rootTimer, renderContext ); + ReflectionFaces reflectionFaces; { TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); m_scene->BeginFrame( renderContext ); + + reflectionFaces = PrepareReflectionFaces( renderContext ); + UpdateLightManagerFaceViews( reflectionFaces ); + + // the lights are gathered once, for the main view and the faces that have lights + if( auto lightManager = Tr2LightManager::GetInstance() ) + { + lightManager->SetViewFrustum( static_cast( mainView.lightView ), mainView.frustum ); + for( const SceneRender::View& faceView : reflectionFaces.views ) + { + if( faceView.features.dynamicLights ) + { + lightManager->SetViewFrustum( static_cast( faceView.lightView ), faceView.frustum ); + } + } + } + m_scene->GatherDynamicLights( renderContext ); } // The new route renders the reflection faces through RenderView. The old route lets @@ -522,7 +569,7 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina if( m_newRoute ) { TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); - RenderReflectionFaces( realTime, simTime, rootTimer, renderContext ); + RenderReflectionFaces( reflectionFaces, realTime, simTime, rootTimer, renderContext ); } targets = RenderView( mainView, colorTarget, outputs, realTime, simTime, rootTimer, renderContext ); @@ -607,17 +654,24 @@ float EveSpaceSceneRenderDriver::GetMipLodBias() const // colorTarget. Returns the targets the view rendered, for post processing or for // submitting to the reflection probe. // -------------------------------------------------------------------------------------- -EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& renderInfo, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const SceneRender::View& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { - const FeatureSet& featureSet = renderInfo.featureSet; - const SceneRender::ViewInfo& sceneView = renderInfo.sceneView; - const TextureSize2D& renderSize = renderInfo.renderSize; + const TextureSize2D& renderSize = view.renderSize; ViewTargets targets; const Tr2TextureAL& backBuffer = colorTarget; - ApplyView( renderInfo, renderContext ); - - const bool renderBackgroundNode = m_background && featureSet.background; + ApplyView( view, renderContext ); + + // every view with lights makes its own view active, so it can't read another view's lights + if( view.features.dynamicLights ) + { + if( auto lightManager = Tr2LightManager::GetInstance() ) + { + lightManager->SetActiveView( static_cast( view.lightView ), view.frustum ); + } + } + + const bool renderBackgroundNode = m_background && view.features.background; if( renderBackgroundNode ) { // render background effect to back buffer before rendering the scene @@ -635,10 +689,10 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co { TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); - m_scene->BeginView( sceneView, renderContext ); + m_scene->BeginView( view, renderContext ); } // Old route: the scene renders the reflection faces itself, from within the main view - if( featureSet.reflections && !m_newRoute ) + if( view.features.reflections && !m_newRoute ) { TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); m_scene->RenderReflectionPass( m_gpuResourcePool, renderContext ); @@ -647,16 +701,16 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co SetNamedOutput( outputs, "DepthMap", targets.depth ); - Tr2GpuResourcePool::Texture distortionMap = GetDistortionMapIfNeeded( renderSize, sceneView.distortion ); - targets.velocity = GetVelocityMapIfNeeded( renderSize, outputs, featureSet ); + Tr2GpuResourcePool::Texture distortionMap = GetDistortionMapIfNeeded( renderSize, view.scene.distortion ); + targets.velocity = GetVelocityMapIfNeeded( renderSize, outputs, view.features ); const Tr2TextureAL& velocityMap = targets.velocity; bool hasBackgroundDistortionBatches = false; { TimeSection backgroundSection( m_timers.background, "BackgroundPass", rootTimer, renderContext ); - hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, sceneView, renderContext ); + hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, view, renderContext ); } - if( sceneView.distortion && hasBackgroundDistortionBatches ) + if( view.scene.distortion && hasBackgroundDistortionBatches ) { ApplyDistortion( backBuffer, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); @@ -664,11 +718,11 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co } distortionMap = {}; - Tr2GpuResourcePool::Texture normalMap = GetNormalMapIfNeeded( renderSize, outputs, featureSet ); + Tr2GpuResourcePool::Texture normalMap = GetNormalMapIfNeeded( renderSize, outputs, view.features ); Tr2GpuResourcePool::Texture customStencil = GetCustomStencilMapIfNeeded( renderSize, outputs ); { TimeSection depthPassSection( m_timers.depthPass, "DepthPass", rootTimer, renderContext ); - m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, sceneView, renderContext, m_depthPassTechnique ); + m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, view, renderContext, m_depthPassTechnique ); } GlobalStore().RegisterVariable( "SpaceSceneNormalMap", normalMap ); GlobalStore().RegisterVariable( "SpaceSceneCustomStencil", customStencil ); @@ -677,18 +731,19 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co EveSpaceScene::ShadowResources shadowResources; if( m_scene->m_display && m_mainPassRenderingEnabled ) { - if( featureSet.shadows ) + if( view.features.shadows ) { TimeSection shadowsSection( m_timers.shadows, "Shadows", rootTimer, renderContext ); - shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, sceneView, renderContext ); + shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, view, renderContext ); } TimeSection ssaoSection( m_timers.ssao, "SSAO", rootTimer, renderContext ); - auto ssao = featureSet.ssao ? RenderSSAO( depthBuffer, normalMap, renderContext ) : GetEmptySSAO( m_gpuResourcePool ); + auto ssao = view.features.ssao ? RenderSSAO( depthBuffer, normalMap, renderContext ) : GetEmptySSAO( m_gpuResourcePool ); ssaoSection.Stop(); GlobalStore().RegisterVariable( "SSAOMap", ssao ); - if( auto lightManager = Tr2LightManager::GetInstance() ) + auto lightManager = Tr2LightManager::GetInstance(); + if( lightManager && view.features.dynamicLights ) { GPU_REGION( renderContext, "Lighting" ); TRINITY_STATS_SCOPED_TIME( updateDynamicLightLists ); @@ -697,18 +752,18 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co renderContext.SetReadOnlyDepth( false ); } - targets.opaqueColor = GetOpaqueColorMapIfNeeded( renderSize, outputs, featureSet ); + targets.opaqueColor = GetOpaqueColorMapIfNeeded( renderSize, outputs, view.features ); RegisterWithVariableStore( shadowResources, m_gpuResourcePool ); bool hasDistortionBatches = false; - distortionMap = GetDistortionMapIfNeeded( renderSize, sceneView.distortion ); + distortionMap = GetDistortionMapIfNeeded( renderSize, view.scene.distortion ); { TimeSection mainPassSection( m_timers.mainPass, "MainPass", rootTimer, renderContext ); - hasDistortionBatches = m_scene->RenderMainPass( backBuffer, depthBuffer, distortionMap, velocityMap, targets.opaqueColor, m_gpuResourcePool, sceneView, renderContext ); + hasDistortionBatches = m_scene->RenderMainPass( backBuffer, depthBuffer, distortionMap, velocityMap, targets.opaqueColor, m_gpuResourcePool, view, renderContext ); } RegisterWithVariableStore( {}, m_gpuResourcePool ); - if( sceneView.distortion && hasDistortionBatches ) + if( view.scene.distortion && hasDistortionBatches ) { ApplyDistortion( backBuffer, distortionMap, m_gpuResourcePool, m_distortionEffect, renderContext ); renderContext.m_esm.SetDepthStencilBuffer( depthBuffer ); @@ -719,7 +774,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co else { // Even if we skip main pass rendering, we might need opaqueBackBuffer for upscaling or TAA - targets.opaqueColor = GetOpaqueColorMapIfNeeded( renderSize, outputs, featureSet ); + targets.opaqueColor = GetOpaqueColorMapIfNeeded( renderSize, outputs, view.features ); if( targets.opaqueColor.IsValid() ) { renderContext.RenderPassHint( { Tr2LoadAction::DONT_CARE, Tr2StoreAction::STORE }, {} ); @@ -728,7 +783,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co Tr2Renderer::DrawTexture( renderContext, backBuffer ); } } - if( m_sceneOverlay && featureSet.overlay ) + if( m_sceneOverlay && view.features.overlay ) { Tr2TextureAL overlayTargets[] = { backBuffer, velocityMap }; m_sceneOverlay->Execute( { overlayTargets, 2 }, {}, realTime, simTime, rootTimer, renderContext ); @@ -744,7 +799,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co } { TimeSection endRenderSection( m_timers.endRender, "EndRender", rootTimer, renderContext ); - m_scene->EndRender( sceneView, renderContext ); + m_scene->EndRender( view, renderContext ); } // non-temporal views leave the scene's history untouched, so this is a no-op for them m_viewLast = m_scene->m_viewLast; @@ -756,33 +811,18 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co return targets; } -void EveSpaceSceneRenderDriver::RenderReflectionFaces( Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +EveSpaceSceneRenderDriver::ReflectionFaces EveSpaceSceneRenderDriver::PrepareReflectionFaces( Tr2RenderContext& renderContext ) { - Tr2ReflectionProbe* probe = m_scene->GetReflectionProbe(); + ReflectionFaces faces; + + Tr2ReflectionProbe* probe = m_newRoute ? m_scene->GetReflectionProbe() : nullptr; if( !probe || !probe->IsValid() || !m_scene->m_display ) { - return; + return faces; } - GPU_REGION( renderContext, "Reflection" ); - - // faces are rendered without TAA/upscaling, SSAO, velocity or the render nodes - FeatureSet reflectionFeatures; - reflectionFeatures.upscaling = false; - reflectionFeatures.antiAliasing = false; - reflectionFeatures.reflections = false; // no reflections rendered in the reflection faces, this can be removed when m_newRoute is removed - reflectionFeatures.ssao = false; - reflectionFeatures.velocityMap = false; - reflectionFeatures.background = false; - reflectionFeatures.overlay = false; - reflectionFeatures.dynamicLights = false; - reflectionFeatures.dynamicLightShadows = false; - - const SceneRender::ViewInfo faceView = SceneRender::ViewInfo::ReflectionFace(); - SetCameraToRenderer( renderContext ); probe->UpdatePosition( Tr2Renderer::GetViewPosition() ); - const Vector3 viewDirection = Tr2Renderer::GetInverseViewTransform().GetZ(); const Matrix faceProjection = probe->GetFaceProjection( Tr2Renderer::GetProjectionTransform() ); TriProjectionPtr projection; @@ -791,27 +831,82 @@ void EveSpaceSceneRenderDriver::RenderReflectionFaces( Be::Time realTime, Be::Ti const TextureSize2D faceRenderSize = probe->GetFaceTarget( 0 ).GetDesc(); + faces.probe = probe; + faces.firstPass = probe->IsFirstPass(); + faces.startFace = probe->GetStartFace(); + faces.viewDirection = Tr2Renderer::GetInverseViewTransform().GetZ(); + + const bool dynamicLights = !faces.firstPass && Tr2LightManager::GetInstance() != nullptr; + for( unsigned face = probe->GetStartFace(); face < probe->GetEndFace(); ++face ) + { + SceneRender::View faceView = SceneRender::View::ReflectionFace( face, *probe, projection, faceRenderSize, faces.firstPass ); + faceView.features.dynamicLights = dynamicLights; + faces.views.push_back( faceView ); + } + + return faces; +} + +// -------------------------------------------------------------------------------------- +// Description: +// The light manager has one view per face rendered in a frame: one with one side per +// frame and six with all sides per frame. The views +// are only recreated when that number changes, which is when the probe's render frequency +// changes. +// -------------------------------------------------------------------------------------- +void EveSpaceSceneRenderDriver::UpdateLightManagerFaceViews( const ReflectionFaces& faces ) +{ + auto lightManager = Tr2LightManager::GetInstance(); + if( !lightManager ) + { + return; + } + + uint32_t faceViewCount = 0; + if( faces.probe && !faces.firstPass ) + { + faceViewCount = faces.probe->GetRenderFrequency() == Tr2ReflectionProbe::ALL_SIDES_PER_FRAME ? 6 : 1; + } + + if( lightManager->GetReflectionViewCount() != faceViewCount ) + { + lightManager->ClearReflectionViews(); + for( uint32_t i = 0; i < faceViewCount; ++i ) + { + lightManager->AddReflectionView(); + } + } +} + +void EveSpaceSceneRenderDriver::RenderReflectionFaces( const ReflectionFaces& faces, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +{ + Tr2ReflectionProbe* probe = faces.probe; + if( !probe ) + { + return; + } + + GPU_REGION( renderContext, "Reflection" ); + const bool prevCullInversion = renderContext.m_esm.IsCullModeInverted(); renderContext.m_esm.SetInvertedCullMode( true ); ON_BLOCK_EXIT( [&] { renderContext.m_esm.SetInvertedCullMode( prevCullInversion ); } ); - for( unsigned face = probe->GetStartFace(); face < probe->GetEndFace(); ++face ) + unsigned face = faces.startFace; + for( const SceneRender::View& faceView : faces.views ) { std::string regionName = "Reflection Face " + std::to_string( face ); GPU_REGION( renderContext, regionName.c_str() ); - Matrix view = probe->GetFaceViewTransform( face, probe->GetPosition() ); - - RenderInfo renderInfo = { view, projection, IdentityMatrix(), 0.0f, 1.0f, faceRenderSize, reflectionFeatures, faceView }; - // render straight into the probe's face target, then copy it into the cubemap const Tr2TextureAL& faceTarget = probe->GetFaceTarget( face ); - RenderView( renderInfo, faceTarget, {}, realTime, simTime, rootTimer, renderContext ); + RenderView( faceView, faceTarget, {}, realTime, simTime, rootTimer, renderContext ); probe->SubmitFace( face, faceTarget, renderContext ); + ++face; } - probe->FinishFaces( viewDirection, renderContext ); + probe->FinishFaces( faces.viewDirection, renderContext ); SetCameraToRenderer( renderContext ); } @@ -821,8 +916,8 @@ void EveSpaceSceneRenderDriver::PrepareLightManager() { if( auto lightManager = Tr2LightManager::GetOrCreateInstance( "res:/graphics/effect/managed/space/system/computelightlists.fx" ) ) { - lightManager->SetVariableStore(); lightManager->SetLightingQuality( m_settings.lightingQuality ); + lightManager->SetReflectionQuality( m_settings.reflectionQuality ); } } else @@ -850,7 +945,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetDistortionMapIfNeeded( return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ) { if( !featureSet.velocityMap ) { @@ -873,7 +968,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( c return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ) { // SSAO and raytraced shadows read the normal map const bool needsNormalMap = ( featureSet.ssao && m_settings.aoQuality != AmbientOcclusionQuality::Disabled ) || ( featureSet.shadows && m_settings.shadowQuality == ShadowQuality::SHADOW_RAYTRACED ); @@ -905,7 +1000,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetCustomStencilMapIfNeed return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ) { // the opaque copy is only used by views that can be upscaled or anti-aliased if( !featureSet.upscaling && !featureSet.antiAliasing ) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index d4ee93ed7..84c21536e 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -51,6 +51,7 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : PostProcess::Quality postProcessingQuality = PostProcess::Quality::HIGH; Tr2VolumerticQuality volumetricQuality = Tr2VolumerticQuality::High; LightingQuality lightingQuality = LightingQuality::HIGH; + ReflectionQuality reflectionQuality = ReflectionQuality::OFF; Color clearColor = Color( 0.0f, 0.0f, 0.0f, 1.0f ); @@ -63,37 +64,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : EveSpaceScene::EveVisualizeMethod visualizeMethod = EveSpaceScene::VM_NONE; }; - // Driver side features of a view. What the scene renders is described by the - // EveSpaceScene::ViewInfo in RenderInfo. - struct FeatureSet - { - bool upscaling = true; - bool antiAliasing = true; - bool reflections = true; - bool ssao = true; - bool shadows = true; - bool velocityMap = true; - // special background rendering (f.ex texture background) - bool background = true; - // special overlay rendering (f.ex texture overlay) - bool overlay = true; - - bool dynamicLights = true; - bool dynamicLightShadows = true; - }; - - struct RenderInfo - { - const Matrix& view; - TriProjectionPtr projection; - Matrix jitterMatrix; - float mipLodBias; - float upscalingAmount; - TextureSize2D renderSize; - FeatureSet featureSet; - SceneRender::ViewInfo sceneView; - }; - EveSpaceSceneRenderDriver( IRoot* lockobj = nullptr ); ~EveSpaceSceneRenderDriver(); @@ -127,12 +97,25 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : Tr2GpuResourcePool::Texture opaqueColor; }; - ViewTargets RenderView( const RenderInfo& renderInfo, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); - void RenderReflectionFaces( Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + // The faces the reflection probe renders in a frame, built before the lights are gathered + struct ReflectionFaces + { + Tr2ReflectionProbe* probe = nullptr; + // the probe's first pass only draws the background into the faces + bool firstPass = false; + unsigned startFace = 0; + Vector3 viewDirection = Vector3( 0.0f, 0.0f, 0.0f ); + std::vector views; + }; + + ViewTargets RenderView( const SceneRender::View& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + ReflectionFaces PrepareReflectionFaces( Tr2RenderContext& renderContext ); + void UpdateLightManagerFaceViews( const ReflectionFaces& faces ); + void RenderReflectionFaces( const ReflectionFaces& faces, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetupUpscaling( const TextureSize2D& displaySize ); void PropagateSettings(); - void ApplyView( const RenderInfo& renderInfo, Tr2RenderContext& renderContext ) const; - void UpdateSceneFrame( const RenderInfo& mainView, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + void ApplyView( const SceneRender::View& view, Tr2RenderContext& renderContext ) const; + void UpdateSceneFrame( const SceneRender::View& mainView, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetCameraToRenderer( Tr2RenderContext & renderContext ) const; void SetCameraToRenderer( const Matrix& view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const; TextureSize2D GetRenderSize( const TextureSize2D& displaySize ) const; @@ -141,10 +124,10 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : float GetMipLodBias( ) const; Tr2GpuResourcePool::Texture GetDistortionMapIfNeeded( const TextureSize2D& size, bool distortion ); - Tr2GpuResourcePool::Texture GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); - Tr2GpuResourcePool::Texture GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); + Tr2GpuResourcePool::Texture GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ); + Tr2GpuResourcePool::Texture GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ); Tr2GpuResourcePool::Texture GetCustomStencilMapIfNeeded( const TextureSize2D& size, const Span& outputs ); - Tr2GpuResourcePool::Texture GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const FeatureSet& featureSet ); + Tr2GpuResourcePool::Texture GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ); void UpdateGpuParticleSystem( Tr2RenderContext & renderContext ); void PrepareLightManager(); diff --git a/trinity/Lights/Tr2FactionLight_Blue.cpp b/trinity/Lights/Tr2FactionLight_Blue.cpp index d52f4a5d4..7de06d48d 100644 --- a/trinity/Lights/Tr2FactionLight_Blue.cpp +++ b/trinity/Lights/Tr2FactionLight_Blue.cpp @@ -39,8 +39,7 @@ const Be::ClassInfo* Tr2FactionLight::ExposeToBlue() MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); - MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); - MAP_ATTRIBUTE( "reflectionShadowCaster", m_lightData.reflectionShadowCaster, "Casts shadows in the reflection, only if it casts shadows regularly.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) + MAP_ATTRIBUTE_WITH_CHOOSER( "reflectionMode", m_lightData.tags.reflects.m_filter, "Reflection quality settings. Controls whether light is active with a certain reflection quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, ReflectionQualityFilterChooser ); MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) diff --git a/trinity/Lights/Tr2Light.cpp b/trinity/Lights/Tr2Light.cpp index c7183b7e6..ecb4d0585 100644 --- a/trinity/Lights/Tr2Light.cpp +++ b/trinity/Lights/Tr2Light.cpp @@ -147,12 +147,12 @@ void Tr2Light::AddLight( Tr2LightManager& lightManager, CXMMATRIX transform, flo if( m_type == Tr2Light::POINT_LIGHT ) { auto data = m_lightData.AsPerPointLightData( lightTransform, features, lightManager.GetCurrentSpaceSceneShadowQuality() ); - lightManager.AddLight( data, m_scaleBrightness ); + lightManager.AddLight( data, m_scaleBrightness, m_lightData.tags ); } else if( m_type == Tr2Light::SPOT_LIGHT ) { auto data = m_lightData.AsPerSpotLightData( lightTransform, features, lightManager.GetCurrentSpaceSceneShadowQuality() ); - lightManager.AddLight( data, m_scaleBrightness ); + lightManager.AddLight( data, m_scaleBrightness, m_lightData.tags ); } } diff --git a/trinity/Lights/Tr2Light.h b/trinity/Lights/Tr2Light.h index 7cec56e34..2f00493b0 100644 --- a/trinity/Lights/Tr2Light.h +++ b/trinity/Lights/Tr2Light.h @@ -49,8 +49,8 @@ struct LightData EnumFilter castsShadows; bool isVolumetric; - EnumFilter reflectionMode; - bool reflectionShadowCaster; + + Tr2LightTags tags; Be::Time startTime; }; diff --git a/trinity/Lights/Tr2Light_Blue.cpp b/trinity/Lights/Tr2Light_Blue.cpp index cc043b476..614bab86c 100644 --- a/trinity/Lights/Tr2Light_Blue.cpp +++ b/trinity/Lights/Tr2Light_Blue.cpp @@ -25,6 +25,7 @@ const Be::VarChooser ReflectionQualityFilterChooser[] = { { "Low", BeCast( 1u << static_cast( ReflectionQuality::LOW ) ), "Active when reflection quality is Low." }, { "Medium", BeCast( 1u << static_cast( ReflectionQuality::MEDIUM ) ), "Active when reflection quality is Medium." }, { "High", BeCast( 1u << static_cast( ReflectionQuality::HIGH ) ), "Active when reflection quality is High." }, + { "Ultra", BeCast( 1u << static_cast( ReflectionQuality::ULTRA ) ), "Active when reflection quality is Ultra." }, { 0 } }; BLUE_REGISTER_ENUM_EX( "ReflectionQualityFilter", uint8_t, ReflectionQualityFilterChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); diff --git a/trinity/Lights/Tr2PointLight_Blue.cpp b/trinity/Lights/Tr2PointLight_Blue.cpp index 22aba24de..ebbb3335f 100644 --- a/trinity/Lights/Tr2PointLight_Blue.cpp +++ b/trinity/Lights/Tr2PointLight_Blue.cpp @@ -30,10 +30,9 @@ const Be::ClassInfo* Tr2PointLight::ExposeToBlue() MAP_ATTRIBUTE( "noiseFrequency", m_lightData.noiseFrequency, "Brightness noise frequency\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseOctaves", m_lightData.noiseOctaves, "Brightness turbulence octaves\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) - MAP_ATTRIBUTE_WITH_CHOOSER( "reflectionMode", m_lightData.reflectionMode.m_filter, "Reflection quality settings. Controls whether light is active with a certain reflection quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, ReflectionQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); - MAP_ATTRIBUTE( "reflectionShadowCaster", m_lightData.reflectionShadowCaster, "Casts shadows in the reflection, only if it casts shadows regularly.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) + MAP_ATTRIBUTE_WITH_CHOOSER( "reflectionMode", m_lightData.tags.reflects.m_filter, "Reflection quality settings. Controls whether light is active with a certain reflection quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, ReflectionQualityFilterChooser ); MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) diff --git a/trinity/Lights/Tr2SpotLight_Blue.cpp b/trinity/Lights/Tr2SpotLight_Blue.cpp index 908531939..087bae398 100644 --- a/trinity/Lights/Tr2SpotLight_Blue.cpp +++ b/trinity/Lights/Tr2SpotLight_Blue.cpp @@ -35,8 +35,7 @@ const Be::ClassInfo* Tr2SpotLight::ExposeToBlue() MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); - MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); - MAP_ATTRIBUTE( "reflectionShadowCaster", m_lightData.reflectionShadowCaster, "Casts shadows in the reflection, only if it casts shadows regularly.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) + MAP_ATTRIBUTE_WITH_CHOOSER( "reflectionMode", m_lightData.tags.reflects.m_filter, "Reflection quality settings. Controls whether light is active with a certain reflection quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, ReflectionQualityFilterChooser ); MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) diff --git a/trinity/Lights/Tr2TexturedPointLight_Blue.cpp b/trinity/Lights/Tr2TexturedPointLight_Blue.cpp index c5d1e112f..a4351d8cd 100644 --- a/trinity/Lights/Tr2TexturedPointLight_Blue.cpp +++ b/trinity/Lights/Tr2TexturedPointLight_Blue.cpp @@ -35,8 +35,7 @@ const Be::ClassInfo* Tr2TexturedPointLight::ExposeToBlue() MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); - MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); - MAP_ATTRIBUTE( "reflectionShadowCaster", m_lightData.reflectionShadowCaster, "Casts shadows in the reflection, only if it casts shadows regularly.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) + MAP_ATTRIBUTE_WITH_CHOOSER( "reflectionMode", m_lightData.tags.reflects.m_filter, "Reflection quality settings. Controls whether light is active with a certain reflection quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, ReflectionQualityFilterChooser ); MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) diff --git a/trinity/Tr2LightManager.cpp b/trinity/Tr2LightManager.cpp index 0c031e657..d0425e096 100644 --- a/trinity/Tr2LightManager.cpp +++ b/trinity/Tr2LightManager.cpp @@ -30,6 +30,9 @@ namespace const uint32_t LIGHT_BUFFER_SIZE = 1024; // Size of the light index buffer (number of indices) const uint32_t INDEX_BUFFER_SIZE = 8 * 1024 * 1024; +// Size of the light index buffer of a reflection probe face (number of indices) +const uint32_t INDEX_BUFFER_SIZE_IN_REFLECTIONS = 1024 * 1024; + // Minimal size of a point light sphere in pixels before it is culled out const float CUTOFF_PIXEL_SIZE = 7.f; // Size (in pixels) for the light to stat dimming out before disappeating @@ -43,7 +46,9 @@ const uint32_t TILE_HEIGHT = 16; const uint32_t HIGH_QUALITY_ATLAS_SIZE_LOG2 = 14; const uint32_t HIGH_QUALITY_ATLAS_ENTRY_MAX_SIZE = 1 << 13; const uint32_t MAX_NUM_SHADOWCASTING_LIGHTS = 16; +const uint32_t MAX_NUM_SHADOWCASTING_LIGHTS_IN_REFLECTIONS = 4; const uint32_t MAX_NUM_VOLUMETRIC_LIGHTS = 16; +const uint32_t MAX_NUM_VOLUMETRIC_LIGHTS_IN_REFLECTIONS = 16; const uint32_t RAYTRACING_DUMMY_RESOLUTION = 4; @@ -143,18 +148,27 @@ const BlueSharedString RtSceneTechniqueName = BlueSharedString( "RtShadowScene" } +Tr2LightManager::View::View( uint32_t maxVolumetricLights, uint32_t maxShadowCastingLights, uint32_t indexBufferSize, bool reflection ) : + reflection( reflection ), + maxVolumetricLights( maxVolumetricLights ), + maxShadowCastingLights( maxShadowCastingLights ), + indexBufferSize( indexBufferSize ) +{ + lightBuffer.CreateInstance(); + indexBuffer.CreateInstance(); + indexBufferCounter.CreateInstance(); +} + Tr2LightManager::Tr2LightManager( const char* effectPath ) { m_effect.CreateInstance(); m_effect->SetEffectPathName( effectPath ); - m_lightBuffer.CreateInstance(); - m_indexBuffer.CreateInstance(); - - m_indexBufferCounter.CreateInstance(); + m_views.emplace_back( MAX_NUM_VOLUMETRIC_LIGHTS, MAX_NUM_SHADOWCASTING_LIGHTS, INDEX_BUFFER_SIZE, false ); + View& mainView = m_views[MAIN_VIEW]; - m_lightBufferVariable.Register( "LightBuffer", m_lightBuffer ); - m_indexBufferVariable.Register( "LightIndexBuffer", m_indexBuffer ); + m_lightBufferVariable.Register( "LightBuffer", mainView.lightBuffer ); + m_indexBufferVariable.Register( "LightIndexBuffer", mainView.indexBuffer ); GlobalStore().RegisterVariable( "LightProfileArray", &GetLightProfileArray() ); m_currentSpaceSceneShadowQuality = ShadowQuality::SHADOW_DISABLED; @@ -211,31 +225,105 @@ void Tr2LightManager::DeleteInstance() singleton = nullptr; } -void Tr2LightManager::SetVariableStore() +uint16_t Tr2LightManager::PackFlags( uint16_t flags, uint16_t profileIndex ) { - m_lightBufferVariable = m_lightBuffer; - m_indexBufferVariable = m_indexBuffer; + return ( flags & ( ( 1 << FLAG_BITS ) - 1 ) ) | ( profileIndex << FLAG_BITS ); } -uint16_t Tr2LightManager::PackFlags( uint16_t flags, uint16_t profileIndex ) +Tr2LightManager::View& Tr2LightManager::GetActiveView() { - return ( flags & ( ( 1 << FLAG_BITS ) - 1 ) ) | ( profileIndex << FLAG_BITS ); + return m_views[m_activeView]; } -void Tr2LightManager::Clear( Tr2RenderContext& renderContext ) +const Tr2LightManager::View& Tr2LightManager::GetActiveView() const { - m_lightBufferVariable = m_lightBuffer; - m_indexBufferVariable = m_indexBuffer; - ClearLightIndices( renderContext ); + return m_views[m_activeView]; +} + +void Tr2LightManager::CreateViewBuffers( View& view, Tr2RenderContext& renderContext ) +{ + view.lightBuffer->Create( LIGHT_BUFFER_SIZE, sizeof( PerLightData ), Tr2GpuBuffer::CPU_WRITABLE ); + view.lightBuffer->SetName( "Light buffer" ); + view.indexBuffer->Create( view.indexBufferSize, sizeof( uint32_t ), Tr2GpuStructuredBuffer::GPU_WRITABLE ); + view.indexBuffer->SetName( "Light indices" ); + view.indexBufferCounter->Create( 1, Tr2RenderContextEnum::PIXEL_FORMAT_R32_SINT, Tr2GpuBuffer::GPU_WRITABLE ); + ClearLightIndices( view, renderContext ); +} + +void Tr2LightManager::ClearReflectionViews() +{ + while( m_views.size() > MAIN_VIEW + 1 ) + { + m_views.pop_back(); + } - for( auto& data : m_tlsLightData ) + if( m_activeView != MAIN_VIEW ) { - data.clear(); + m_activeView = MAIN_VIEW; + m_lightBufferVariable = m_views[MAIN_VIEW].lightBuffer; + m_indexBufferVariable = m_views[MAIN_VIEW].indexBuffer; + } +} + +uint8_t Tr2LightManager::AddReflectionView() +{ + m_views.emplace_back( MAX_NUM_VOLUMETRIC_LIGHTS_IN_REFLECTIONS, 0u, INDEX_BUFFER_SIZE_IN_REFLECTIONS, true ); + + if( Tr2Renderer::IsResourceCreationAllowed() ) + { + USE_MAIN_THREAD_RENDER_CONTEXT(); + CreateViewBuffers( m_views.back(), renderContext ); + } + + return static_cast( m_views.size() - 1 ); +} + +uint32_t Tr2LightManager::GetReflectionViewCount() const +{ + return static_cast( m_views.size() - ( MAIN_VIEW + 1 ) ); +} + +void Tr2LightManager::SetViewFrustum( uint8_t viewIndex, const TriFrustum& frustum ) +{ + if( viewIndex >= m_views.size() ) + { + CCP_LOGERR( "Tr2LightManager: invalid view index %u", viewIndex ); + return; + } + + m_views[viewIndex].frustum = frustum; +} + +void Tr2LightManager::SetActiveView( uint8_t viewIndex, const TriFrustum& frustum ) +{ + if( viewIndex >= m_views.size() ) + { + CCP_LOGERR( "Tr2LightManager: invalid view index %u", viewIndex ); + return; } - m_lightData.clear(); - m_shadowCastingLights.clear(); - m_volumetricLights.clear(); + m_activeView = viewIndex; + View& view = m_views[viewIndex]; + view.frustum = frustum; + m_lightBufferVariable = view.lightBuffer; + m_indexBufferVariable = view.indexBuffer; +} + +void Tr2LightManager::Clear( Tr2RenderContext& renderContext ) +{ + for( auto& view: m_views ) + { + ClearLightIndices( view, renderContext ); + for( auto& data : view.tlsLightData ) + { + data.clear(); + } + view.lightData.clear(); + view.shadowCastingLights.clear(); + view.volumetricLights.clear(); + } + + // shadow map is only on the main view currently m_ShadowMap.m_atlasNodes.resize( 1 ); m_ShadowMap.m_atlasNodes[0].x = 0; m_ShadowMap.m_atlasNodes[0].y = 0; @@ -246,14 +334,14 @@ void Tr2LightManager::Clear( Tr2RenderContext& renderContext ) m_ShadowMap.m_atlasNodes[0].lightIndex = -1; } -void Tr2LightManager::SetFrustum( const TriFrustum& frustum ) +void Tr2LightManager::AdjustLightCutoff( float lodFactor ) { - m_frustum = frustum; + m_adjustedCutoff = CUTOFF_PIXEL_SIZE * lodFactor; } -void Tr2LightManager::AdjustLightCutoff( float lodFactor ) +void Tr2LightManager::SetReflectionQuality( ReflectionQuality reflectionQuality ) { - m_adjustedCutoff = CUTOFF_PIXEL_SIZE * lodFactor; + m_reflectionQuality = reflectionQuality; } // This entire thing is annoyingly complex for what should be a simple thing... @@ -323,12 +411,15 @@ void Tr2LightManager::AddPointLight( const Vector3& position, float radius, cons { return; } - if( !m_frustum.IsSphereVisible( reinterpret_cast( &data.position ) ) ) + + View& mainView = m_views[MAIN_VIEW]; + + if( !mainView.frustum.IsSphereVisible( reinterpret_cast( &data.position ) ) ) { return; } - float size = m_frustum.GetPixelSizeAccross( reinterpret_cast( &data.position ) ); + float size = mainView.frustum.GetPixelSizeAccross( reinterpret_cast( &data.position ) ); if( size > m_adjustedCutoff ) { @@ -346,11 +437,11 @@ void Tr2LightManager::AddPointLight( const Vector3& position, float radius, cons data.direction = Vector3_16( Vector3( 1.f, 0.f, 0.f ) ); data.outerAngle = Float_16( 0.f ); data.innerAngle = Float_16( 0.f ); - m_tlsLightData.local().push_back( data ); + mainView.tlsLightData.local().push_back( data ); } } -void Tr2LightManager::AddLight( PerLightData& data, bool scaleBrightness ) +void Tr2LightManager::AddLight( PerLightData& data, bool scaleBrightness, const Tr2LightTags& tags ) { if( !AreLightFlagsValid( data.flags ) ) { @@ -362,73 +453,93 @@ void Tr2LightManager::AddLight( PerLightData& data, bool scaleBrightness ) { return; } - if( !m_frustum.IsSphereVisible( reinterpret_cast( &data.position ) ) ) - { - return; - } - float size = m_frustum.GetPixelSizeAccross( reinterpret_cast( &data.position ) ); - if( size > m_adjustedCutoff ) + for( auto& view : m_views ) { - float dimming = std::min( ( size - m_adjustedCutoff ) / FADE_SIZE, 1.f ); - if( scaleBrightness ) + // a face view only takes the lights that are tagged for the current reflection quality + if( view.reflection && !tags.reflects.HasBit( m_reflectionQuality ) ) { - dimming *= data.radius; + continue; } - data.color.x *= dimming; - data.color.y *= dimming; - data.color.z *= dimming; - bool usingShadowMap = m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_LOW || m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_HIGH; - if( m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_DISABLED || - ( usingShadowMap && m_ShadowMap.m_qualityUsedByAtlas == ShadowQuality::SHADOW_DISABLED ) || - !g_useDynamicLightsShadows ) + + if( !view.frustum.IsSphereVisible( reinterpret_cast( &data.position ) ) ) + { + continue; + } + float size = view.frustum.GetPixelSizeAccross( reinterpret_cast( &data.position ) ); + if( size > m_adjustedCutoff ) { - data.flags &= ~Tr2LightManager::FLAG_CASTS_SHADOWS; + // every view works on its own copy, as the fade and the flags differ per view + PerLightData viewData = data; + float dimming = std::min( ( size - m_adjustedCutoff ) / FADE_SIZE, 1.f ); + if( scaleBrightness ) + { + dimming *= viewData.radius; + } + viewData.color.x *= dimming; + viewData.color.y *= dimming; + viewData.color.z *= dimming; + + bool usingShadowMap = m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_LOW || m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_HIGH; + if( view.reflection || + m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_DISABLED || + ( usingShadowMap && m_ShadowMap.m_qualityUsedByAtlas == ShadowQuality::SHADOW_DISABLED ) || + !g_useDynamicLightsShadows ) + { + viewData.flags &= ~Tr2LightManager::FLAG_CASTS_SHADOWS; + } + view.tlsLightData.local().push_back( viewData ); } - m_tlsLightData.local().push_back( data ); } } -ALResult Tr2LightManager::ClearLightIndices( Tr2RenderContext& renderContext ) +ALResult Tr2LightManager::ClearLightIndices( Tr2LightManager::View& view, Tr2RenderContext& renderContext ) { - if( !m_indexBuffer || !m_indexBuffer->GetGpuBuffer( 0 ) ) + if( !view.indexBuffer || !view.indexBuffer->GetGpuBuffer( 0 ) ) { return S_OK; } const uint32_t clear[] = { 0, 0, 0, 0 }; - return renderContext.ClearUav( *m_indexBuffer->GetGpuBuffer( 0 ), clear ); + return renderContext.ClearUav( *view.indexBuffer->GetGpuBuffer( 0 ), clear ); } ALResult Tr2LightManager::UpdateLightBuffer( Tr2RenderContext& renderContext ) { - if( m_lightBuffer->GetGpuBuffer( 0 )->GetDesc().count < uint32_t( m_lightData.size() ) ) + View& view = GetActiveView(); + if( view.lightBuffer->GetGpuBuffer( 0 )->GetDesc().count < uint32_t( view.lightData.size() ) ) { - CR_RETURN_HR( m_lightBuffer->Create( - std::max( m_lightBuffer->GetGpuBuffer( 0 )->GetDesc().count + 1024, uint32_t( m_lightData.size() ) ), + CR_RETURN_HR( view.lightBuffer->Create( + std::max( view.lightBuffer->GetGpuBuffer( 0 )->GetDesc().count + 1024, uint32_t( view.lightData.size() ) ), sizeof( PerLightData ), Tr2GpuBuffer::CPU_WRITABLE ) ); - m_lightBuffer->SetName( "Light buffer" ); + view.lightBuffer->SetName( "Light buffer" ); } - auto lightCount = std::min( m_lightBuffer->GetGpuBuffer( 0 )->GetDesc().count, uint32_t( m_lightData.size() ) ); + auto lightCount = std::min( view.lightBuffer->GetGpuBuffer( 0 )->GetDesc().count, uint32_t( view.lightData.size() ) ); Vector4* data; - CR_RETURN_HR( m_lightBuffer->GetGpuBuffer( 0 )->MapForWriting( data, renderContext ) ); - memcpy( data, m_lightData.data(), lightCount * sizeof( PerLightData ) ); - CCP_STATS_ADD( lightsGathered, m_lightData.size() ); - m_lightBuffer->GetGpuBuffer( 0 )->UnmapForWriting( renderContext ); + CR_RETURN_HR( view.lightBuffer->GetGpuBuffer( 0 )->MapForWriting( data, renderContext ) ); + memcpy( data, view.lightData.data(), lightCount * sizeof( PerLightData ) ); + CCP_STATS_ADD( lightsGathered, view.lightData.size() ); + view.lightBuffer->GetGpuBuffer( 0 )->UnmapForWriting( renderContext ); return S_OK; } ALResult Tr2LightManager::DoUpdateLists( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ) { - if( !m_lightBuffer->IsValid() || !m_indexBuffer->IsValid() || !m_indexBufferCounter->IsValid() ) + View& view = GetActiveView(); + if( !view.lightBuffer->IsValid() || !view.indexBuffer->IsValid() || !view.indexBufferCounter->IsValid() ) { return E_INVALIDCALL; } CR_RETURN_HR( UpdateLightBuffer( renderContext ) ); + // the effect is shared by all the views, so it has to be pointed at the buffers of the active one + m_effect->SetParameter( BlueSharedString( "LightBuffer" ), view.lightBuffer ); + m_effect->SetParameter( BlueSharedString( "LightIndices" ), view.indexBuffer ); + m_effect->SetParameter( BlueSharedString( "LightIndexCount" ), view.indexBufferCounter ); + PerFrameData perFrameData; perFrameData.projInverse = Transpose( Tr2Renderer::GetInverseProjectionTransform() ); perFrameData.viewInverse = Transpose( Tr2Renderer::GetInverseViewTransform() ); @@ -437,8 +548,8 @@ ALResult Tr2LightManager::DoUpdateLists( const Tr2TextureAL& depthMap, Tr2Render perFrameData.height = uint32_t( renderContext.m_esm.GetRenderTargetHeight() ); perFrameData.tilesX = ( perFrameData.width + ( TILE_WIDTH - 1 ) ) / TILE_WIDTH; perFrameData.tilesY = ( perFrameData.height + ( TILE_HEIGHT - 1 ) ) / TILE_HEIGHT; - perFrameData.lightCount = std::min( m_lightBuffer->GetGpuBuffer( 0 )->GetDesc().count, uint32_t( m_lightData.size() ) ); - perFrameData.indexBufferSize = INDEX_BUFFER_SIZE; + perFrameData.lightCount = std::min( view.lightBuffer->GetGpuBuffer( 0 )->GetDesc().count, uint32_t( view.lightData.size() ) ); + perFrameData.indexBufferSize = view.indexBufferSize; m_effect->SetParameter( BlueSharedString( "DepthMap" ), depthMap ); @@ -461,7 +572,7 @@ ALResult Tr2LightManager::DoUpdateLists( const Tr2TextureAL& depthMap, Tr2Render return S_OK; } -void Tr2LightManager::CreateShadowMapAtlas( uint32_t numShadowCastingLights, const std::vector& lightTuples ) +void Tr2LightManager::CreateShadowMapAtlas( std::vector& lightDataList, uint32_t numShadowCastingLights, const std::vector& lightTuples ) { bool everythingFit = false; // 5 iterations are more than enough. With the current values we should actually only need at most 4 iterations. @@ -485,7 +596,7 @@ void Tr2LightManager::CreateShadowMapAtlas( uint32_t numShadowCastingLights, con for( uint32_t i = 0; i < numShadowCastingLights; i++ ) { uint32_t lightIndex = lightTuples[i].lightIndex; - Tr2LightManager::PerLightData& lightData = m_lightData[lightIndex]; + Tr2LightManager::PerLightData& lightData = lightDataList[lightIndex]; uint32_t size = ( (uint32_t)lightTuples[i].sizeAcross ) >> entryInverseScaleFactorLog2; size = ClampUInt( size, m_ShadowMap.m_atlasSettings.entryMinSize, entryMaxSize ); @@ -541,27 +652,36 @@ Tr2RtShaderTableDescriptionAL* Tr2LightManager::GetRaytracingShaderTableDesc() void Tr2LightManager::ResolveLightData() { - m_lightData.clear(); - for( auto& data : m_tlsLightData ) + for( auto& view : m_views ) + { + ResolveView( view ); + } +} + +void Tr2LightManager::ResolveView( View& view ) +{ + std::vector& lightData = view.lightData; + lightData.clear(); + for( auto& data : view.tlsLightData ) { if( !data.empty() ) { - m_lightData.insert( end( m_lightData ), begin( data ), end( data ) ); + lightData.insert( end( lightData ), begin( data ), end( data ) ); } data.clear(); } - m_shadowCastingLights.clear(); - m_volumetricLights.clear(); + view.shadowCastingLights.clear(); + view.volumetricLights.clear(); // filter volumetric lights std::vector lightTuples; { - for( uint32_t i = 0; i < m_lightData.size(); i++ ) + for( uint32_t i = 0; i < lightData.size(); i++ ) { - if( ( m_lightData[i].flags & FLAG_IS_VOLUMETRIC ) != 0 ) + if( ( lightData[i].flags & FLAG_IS_VOLUMETRIC ) != 0 ) { - float sizeAcross = m_frustum.GetPixelSizeAccrossEst( reinterpret_cast( &m_lightData[i].position ) ); + float sizeAcross = view.frustum.GetPixelSizeAccrossEst( reinterpret_cast( &lightData[i].position ) ); sizeAcross = sizeAcross == std::numeric_limits::max() ? ( 1 << HIGH_QUALITY_ATLAS_SIZE_LOG2 ) : sizeAcross; lightTuples.push_back( LightScreenSizeTuple{ i, sizeAcross } ); } @@ -573,21 +693,22 @@ void Tr2LightManager::ResolveLightData() } ); // keep only the largest lights - uint32_t numVolumetricLights = min( MAX_NUM_VOLUMETRIC_LIGHTS, (uint32_t)lightTuples.size() ); - m_volumetricLights.resize( numVolumetricLights ); + uint32_t numVolumetricLights = min( view.maxVolumetricLights, (uint32_t)lightTuples.size() ); + view.volumetricLights.resize( numVolumetricLights ); uint32_t i = 0; for( ; i < numVolumetricLights; i++ ) { - m_volumetricLights[i] = lightTuples[i].lightIndex; + view.volumetricLights[i] = lightTuples[i].lightIndex; } for( ; i < lightTuples.size(); i++ ) { - Tr2LightManager::PerLightData& lightData = m_lightData[lightTuples[i].lightIndex]; - lightData.flags &= ~Tr2LightManager::FLAG_IS_VOLUMETRIC; + Tr2LightManager::PerLightData& light = lightData[lightTuples[i].lightIndex]; + light.flags &= ~Tr2LightManager::FLAG_IS_VOLUMETRIC; } } - if( !g_useDynamicLightsShadows || m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_DISABLED ) + // the shadow map atlas is only on the main view currently + if( view.reflection || !g_useDynamicLightsShadows || m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_DISABLED ) { return; } @@ -596,11 +717,11 @@ void Tr2LightManager::ResolveLightData() uint32_t numShadowCastingLights = 0; lightTuples.resize( 0 ); { - for( uint32_t i = 0; i < m_lightData.size(); i++ ) + for( uint32_t i = 0; i < lightData.size(); i++ ) { - if( ( m_lightData[i].flags & FLAG_CASTS_SHADOWS ) != 0 ) + if( ( lightData[i].flags & FLAG_CASTS_SHADOWS ) != 0 ) { - float sizeAcross = m_frustum.GetPixelSizeAccrossEst( reinterpret_cast( &m_lightData[i].position ) ); + float sizeAcross = view.frustum.GetPixelSizeAccrossEst( reinterpret_cast( &lightData[i].position ) ); sizeAcross = sizeAcross == std::numeric_limits::max() ? ( 1 << HIGH_QUALITY_ATLAS_SIZE_LOG2 ) : sizeAcross; lightTuples.push_back( LightScreenSizeTuple{ i, sizeAcross } ); } @@ -612,49 +733,50 @@ void Tr2LightManager::ResolveLightData() } ); // keep only the largest lights - numShadowCastingLights = min( MAX_NUM_SHADOWCASTING_LIGHTS, (uint32_t)lightTuples.size() ); - m_shadowCastingLights.resize( numShadowCastingLights ); + numShadowCastingLights = min( view.maxShadowCastingLights, (uint32_t)lightTuples.size() ); + view.shadowCastingLights.resize( numShadowCastingLights ); uint32_t i = 0; for( ; i < numShadowCastingLights; i++ ) { - m_shadowCastingLights[i] = lightTuples[i].lightIndex; + view.shadowCastingLights[i] = lightTuples[i].lightIndex; if( m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_RAYTRACED ) { - Tr2LightManager::PerLightData& lightData = m_lightData[lightTuples[i].lightIndex]; - lightData.Raytracing.shadowMask = 1 << i; + Tr2LightManager::PerLightData& light = lightData[lightTuples[i].lightIndex]; + light.Raytracing.shadowMask = 1 << i; } } for( ; i < lightTuples.size(); i++ ) { - Tr2LightManager::PerLightData& lightData = m_lightData[lightTuples[i].lightIndex]; - lightData.flags &= ~Tr2LightManager::FLAG_CASTS_SHADOWS; + Tr2LightManager::PerLightData& light = lightData[lightTuples[i].lightIndex]; + light.flags &= ~Tr2LightManager::FLAG_CASTS_SHADOWS; if( m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_RAYTRACED ) { - lightData.Raytracing.shadowMask = 0; + light.Raytracing.shadowMask = 0; } } } if( m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_LOW || m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_HIGH ) { - CreateShadowMapAtlas( numShadowCastingLights, lightTuples ); + CreateShadowMapAtlas( lightData, numShadowCastingLights, lightTuples ); } } ALResult Tr2LightManager::UpdateLists( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ) { - m_lightBufferVariable = m_lightBuffer; - m_indexBufferVariable = m_indexBuffer; + View& view = GetActiveView(); + m_lightBufferVariable = view.lightBuffer; + m_indexBufferVariable = view.indexBuffer; - if( m_lightData.empty() ) + if( view.lightData.empty() ) { - return ClearLightIndices( renderContext ); + return ClearLightIndices( view, renderContext ); } auto hr = DoUpdateLists( depthMap, renderContext ); if( FAILED( hr ) ) { - ClearLightIndices( renderContext ); + ClearLightIndices( view, renderContext ); } return hr; @@ -680,17 +802,16 @@ bool Tr2LightManager::OnPrepareResources() { USE_MAIN_THREAD_RENDER_CONTEXT(); - m_lightBuffer->Create( LIGHT_BUFFER_SIZE, sizeof( PerLightData ), Tr2GpuBuffer::CPU_WRITABLE ); - m_lightBuffer->SetName( "Light buffer" ); - m_indexBuffer->Create( INDEX_BUFFER_SIZE, sizeof( uint32_t ), Tr2GpuStructuredBuffer::GPU_WRITABLE ); - m_indexBuffer->SetName( "Light indices" ); - m_indexBufferCounter->Create( 1, Tr2RenderContextEnum::PIXEL_FORMAT_R32_SINT, Tr2GpuBuffer::GPU_WRITABLE ); - ClearLightIndices( renderContext ); + for( auto& view : m_views ) + { + CreateViewBuffers( view, renderContext ); + } m_perFrameData.Create( sizeof( PerFrameData ), Tr2ConstantUsageAL::ONE_SHOT, nullptr, renderContext ); - m_effect->SetParameter( BlueSharedString( "LightBuffer" ), m_lightBuffer ); - m_effect->SetParameter( BlueSharedString( "LightIndices" ), m_indexBuffer ); - m_effect->SetParameter( BlueSharedString( "LightIndexCount" ), m_indexBufferCounter ); + // DoUpdateLists points the effect at the buffers of the view it works on + m_effect->SetParameter( BlueSharedString( "LightBuffer" ), m_views[MAIN_VIEW].lightBuffer ); + m_effect->SetParameter( BlueSharedString( "LightIndices" ), m_views[MAIN_VIEW].indexBuffer ); + m_effect->SetParameter( BlueSharedString( "LightIndexCount" ), m_views[MAIN_VIEW].indexBufferCounter ); m_effect->SetParameter( BlueSharedString( "DepthMap" ), Tr2TextureAL() ); return true; @@ -709,17 +830,17 @@ Tr2TextureArray& Tr2LightManager::GetLightProfileArray() const std::vector& Tr2LightManager::GetShadowCastingLights() const { - return m_shadowCastingLights; + return GetActiveView().shadowCastingLights; } const std::vector& Tr2LightManager::GetVolumetricLights() const { - return m_volumetricLights; + return GetActiveView().volumetricLights; } const Tr2LightManager::PerLightData& Tr2LightManager::GetLightData( uint32_t index ) const { - return m_lightData[index]; + return GetActiveView().lightData[index]; } Tr2GpuResourcePool::Texture Tr2LightManager::GetShadowMapAtlas( Tr2GpuResourcePool& gpuResourcePool ) @@ -849,7 +970,8 @@ Tr2GpuResourcePool::Texture Tr2LightManager::RenderRaytracedShadows( GPU_REGION( renderContext, "Raytraced dynamic shadows" ); TRINITY_STATS_ZONE( __FUNCTION__ ); - if( m_shadowCastingLights.size() == 0 || !g_useDynamicLightsShadows ) + const View& view = GetActiveView(); + if( view.shadowCastingLights.size() == 0 || !g_useDynamicLightsShadows ) { return {}; } @@ -910,13 +1032,13 @@ Tr2GpuResourcePool::Texture Tr2LightManager::RenderRaytracedShadows( data->resolution = Vector2( float( destTex->GetWidth() ), float( destTex->GetHeight() ) ); - for( uint32_t j = 0; j < m_shadowCastingLights.size(); j++ ) + for( uint32_t j = 0; j < view.shadowCastingLights.size(); j++ ) { - uint32_t lightIndex = m_shadowCastingLights[j]; - PerLightData& lightData = m_lightData[lightIndex]; + uint32_t lightIndex = view.shadowCastingLights[j]; + const PerLightData& lightData = view.lightData[lightIndex]; data->dynamicLights[j] = lightData; } - data->numDynamicLights = (uint32_t)m_shadowCastingLights.size(); + data->numDynamicLights = (uint32_t)view.shadowCastingLights.size(); m_Raytracing.m_perFrameData.Unlock( renderContext ); } diff --git a/trinity/Tr2LightManager.h b/trinity/Tr2LightManager.h index 2c101b808..beb31069f 100644 --- a/trinity/Tr2LightManager.h +++ b/trinity/Tr2LightManager.h @@ -12,6 +12,7 @@ #include "Raytracing/Tr2RaytracingGeometry.h" #include "Tr2Denoiser.h" +#include "EnumFilter.h" BLUE_DECLARE( Tr2Effect ); BLUE_DECLARE( Tr2GpuBuffer ); @@ -40,9 +41,16 @@ enum class LightingQuality : int32_t enum class ReflectionQuality : int32_t { + OFF, LOW, MEDIUM, HIGH, + ULTRA, +}; + +struct Tr2LightTags +{ + EnumFilter reflects; }; // -------------------------------------------------------------------------------------- @@ -98,6 +106,7 @@ class Tr2LightManager : public Tr2DeviceResource } Raytracing; }; }; + typedef Tr2EnumerableThreadSpecific> ThreadLocalLightData; struct ShadowMapAtlasSettings { @@ -124,15 +133,21 @@ class Tr2LightManager : public Tr2DeviceResource static uint16_t PackFlags( uint16_t flags, uint16_t profileIndex ); void Clear( Tr2RenderContext& renderContext ); - void SetFrustum( const TriFrustum& frustum ); + + void ClearReflectionViews(); + uint8_t AddReflectionView(); + uint32_t GetReflectionViewCount() const; + void SetViewFrustum( uint8_t viewIndex, const TriFrustum& frustum ); + void SetActiveView( uint8_t viewIndex, const TriFrustum& frustum ); + void AddPointLight( const Vector3& position, float radius, const Color& color, Float_16 innerRadius = Float_16( 0.f ), uint16_t flags = FLAG_DEFAULT, bool scaleBrightness = true ); - void AddLight( PerLightData& data, bool scaleBrightness = true ); - void ResolveLightData(); + void AddLight( PerLightData& data, bool scaleBrightness = true, const Tr2LightTags& tags = Tr2LightTags() ); + void ResolveLightData(); ALResult UpdateLists( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ); - void SetVariableStore(); void AdjustLightCutoff( float lodFactor ); void SetShadowQuality( ShadowQuality shadowQuality, uint64_t frameCounter ); + void SetReflectionQuality( ReflectionQuality reflectionQuality ); void SetLightingQuality( LightingQuality lightingQuality ); LightingQuality GetLightingQuality() const; @@ -189,36 +204,55 @@ class Tr2LightManager : public Tr2DeviceResource float sizeAcross; }; + struct View + { + bool reflection = false; // face view: needs the reflection tag + TriFrustum frustum; + uint32_t maxVolumetricLights; + uint32_t maxShadowCastingLights; + uint32_t indexBufferSize; + + ThreadLocalLightData tlsLightData; + std::vector lightData; + std::vector shadowCastingLights; + std::vector volumetricLights; + Tr2GpuStructuredBufferPtr lightBuffer; + Tr2GpuStructuredBufferPtr indexBuffer; + Tr2GpuBufferPtr indexBufferCounter; + + View( uint32_t maxVolumetricLights, uint32_t maxShadowCastingLights, uint32_t indexBufferSize, bool reflection ); + }; + virtual bool OnPrepareResources(); + View& GetActiveView(); + const View& GetActiveView() const; + void CreateViewBuffers( View& view, Tr2RenderContext& renderContext ); + void ResolveView( View& view ); + ALResult DoUpdateLists( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ); - ALResult ClearLightIndices( Tr2RenderContext& renderContext ); + ALResult ClearLightIndices( Tr2LightManager::View& view, Tr2RenderContext& renderContext ); ALResult UpdateLightBuffer( Tr2RenderContext& renderContext ); uint32_t InsertAtlasNode( std::vector& atlasNodes, uint32_t nodeId, uint32_t lightIndex, int32_t width, int32_t height ); bool GetShadowMapAtlasEntry( uint32_t lightIndex, uint32_t width, uint32_t height, uint32_t& out_posX, uint32_t& out_posY ); - void CreateShadowMapAtlas( uint32_t numShadowCastingLights, const std::vector& lightTuples ); + void CreateShadowMapAtlas( std::vector& lightData, uint32_t numShadowCastingLights, const std::vector& lightTuples ); - Tr2EnumerableThreadSpecific> m_tlsLightData; - std::vector m_lightData; - std::vector m_shadowCastingLights; - std::vector m_volumetricLights; - Tr2GpuStructuredBufferPtr m_lightBuffer; - Tr2GpuStructuredBufferPtr m_indexBuffer; + static const uint32_t MAIN_VIEW = 0; // faces are 1..6 + std::vector m_views; + uint32_t m_activeView = MAIN_VIEW; - Tr2GpuBufferPtr m_indexBufferCounter; Tr2ConstantBufferAL m_perFrameData; Tr2EffectPtr m_effect; Tr2Variable m_lightBufferVariable; Tr2Variable m_indexBufferVariable; - TriFrustum m_frustum; - float m_adjustedCutoff; uint32_t nextFrameShadowQuality; // bitmask, collecting ShadowQualities during the current frame ShadowQuality m_currentSpaceSceneShadowQuality; LightingQuality m_lightingQuality = LightingQuality::HIGH; + ReflectionQuality m_reflectionQuality = ReflectionQuality::OFF; uint64_t m_currentFrameCounter; struct diff --git a/trinity/Tr2ReflectionProbe.cpp b/trinity/Tr2ReflectionProbe.cpp index 8efd9119e..55ef785e9 100644 --- a/trinity/Tr2ReflectionProbe.cpp +++ b/trinity/Tr2ReflectionProbe.cpp @@ -95,6 +95,16 @@ uint8_t Tr2ReflectionProbe::GetEndFace() const return m_currentFrame + 1; } +Tr2ReflectionProbe::ReflectionProbeRenderFrequency Tr2ReflectionProbe::GetRenderFrequency() const +{ + return m_renderFrequency; +} + +bool Tr2ReflectionProbe::IsFirstPass() const +{ + return !m_onePassDone; +} + void Tr2ReflectionProbe::InitRenderPass( Tr2RenderContext& renderContext ) { diff --git a/trinity/Tr2ReflectionProbe.h b/trinity/Tr2ReflectionProbe.h index de55433d2..d7c522e17 100644 --- a/trinity/Tr2ReflectionProbe.h +++ b/trinity/Tr2ReflectionProbe.h @@ -54,6 +54,9 @@ BLUE_CLASS( Tr2ReflectionProbe ) : uint8_t GetStartFace() const; uint8_t GetEndFace() const; + ReflectionProbeRenderFrequency GetRenderFrequency() const; + // True until the first FinishFaces: until then every face is rendered, whatever the render frequency. + bool IsFirstPass() const; const Tr2TextureAL& GetDepthBuffer( unsigned face ) const { From 7c2bcf7ab48c16d598a1a07e96c9fe21c01f5598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Tue, 27 Oct 2026 11:51:41 +0000 Subject: [PATCH 07/20] Code move Moving all the eve space view specific things into their own file --- trinity/CMakeLists.txt | 2 + trinity/Eve/EveSceneView.cpp | 58 ++++++ trinity/Eve/EveSceneView.h | 56 ++++++ trinity/Eve/EveSpaceScene.cpp | 221 +++++----------------- trinity/Eve/EveSpaceScene.h | 99 +--------- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 159 ++++++++++------ trinity/Eve/EveSpaceSceneRenderDriver.h | 51 ++++- trinity/Tr2LightManager.cpp | 14 +- trinity/Tr2LightManager.h | 2 +- 9 files changed, 324 insertions(+), 338 deletions(-) create mode 100644 trinity/Eve/EveSceneView.cpp create mode 100644 trinity/Eve/EveSceneView.h diff --git a/trinity/CMakeLists.txt b/trinity/CMakeLists.txt index 9387e2ffb..6b07cd764 100644 --- a/trinity/CMakeLists.txt +++ b/trinity/CMakeLists.txt @@ -829,6 +829,8 @@ set(_SOURCES Eve/EveRootTransform.cpp Eve/EveRootTransform.h Eve/EveRootTransform_Blue.cpp + Eve/EveSceneView.cpp + Eve/EveSceneView.h Eve/EveSpaceScene.cpp Eve/EveSpaceScene.h Eve/EveSpaceScene_Blue.cpp diff --git a/trinity/Eve/EveSceneView.cpp b/trinity/Eve/EveSceneView.cpp new file mode 100644 index 000000000..4750c58a8 --- /dev/null +++ b/trinity/Eve/EveSceneView.cpp @@ -0,0 +1,58 @@ +// Copyright © 2026 CCP ehf. + +#include "StdAfx.h" +#include "EveSceneView.h" + +namespace EveSceneView +{ +Camera::Camera( const Matrix& view, const Matrix& projection, const TextureSize2D& renderSize, const Matrix& jitterMatrix, float mipLodBias, float upscalingAmount ) +{ + this->view = view; + this->projection = projection; + this->jitterMatrix = jitterMatrix; + this->renderSize = renderSize; + this->mipLodBias = mipLodBias; + this->upscalingAmount = upscalingAmount; + + const Matrix jitteredProjection = projection * jitterMatrix; + const Matrix inverseView = Inverse( view ); + const Vector3 position( inverseView._41, inverseView._42, inverseView._43 ); + + CTriViewport viewport; + viewport.width = renderSize.width; + viewport.height = renderSize.height; + + this->frustum.DeriveFrustum( &this->view, &position, &jitteredProjection, viewport ); +} + +ViewInfo ViewInfo::Main( bool distortion ) +{ + ViewInfo view; + view.reason = TR2RENDERREASON_NORMAL; + view.cullMode = Tr2RenderContextEnum::CULLMODE_CW; + view.distortion = distortion; + view.sceneObjects = true; + view.temporal = true; + view.subsurfaceScattering = true; + view.gpuParticles = true; + view.lensflares = true; + return view; +} + +ViewInfo ViewInfo::ReflectionFace( bool lensflares ) +{ + ViewInfo view; + view.reason = TR2RENDERREASON_REFLECTION; + // Faces are rendered into a left handed cubemap, so the caller inverts the cull mode + // and the scene must not override it + view.cullMode = Tr2RenderContextEnum::CULLMODE_NONE; + view.distortion = false; + view.sceneObjects = false; + // faces must not disturb the main view's history + view.temporal = false; + view.subsurfaceScattering = false; + view.gpuParticles = false; + view.lensflares = lensflares; + return view; +} +} diff --git a/trinity/Eve/EveSceneView.h b/trinity/Eve/EveSceneView.h new file mode 100644 index 000000000..57220acf4 --- /dev/null +++ b/trinity/Eve/EveSceneView.h @@ -0,0 +1,56 @@ +// Copyright © 2026 CCP ehf. + +#pragma once + +#include "TriFrustum.h" +#include "ITr2Renderable.h" +#include "Tr2GpuResourcePool.h" + +namespace EveSceneView +{ +// Where a view looks from. Create derives the frustum from the other fields, so build a +// new Camera rather than changing one. +struct Camera +{ + Matrix view; + Matrix projection; + Matrix jitterMatrix; + TextureSize2D renderSize; + float mipLodBias = 0.0f; + float upscalingAmount = 1.0f; + // derived from the view, the jittered projection and renderSize, never from the bound viewport + TriFrustum frustum; + + Camera( const Matrix& view, const Matrix& projection, const TextureSize2D& renderSize, const Matrix& jitterMatrix = IdentityMatrix(), float mipLodBias = 0.0f, float upscalingAmount = 1.0f ); +}; + +// What the scene renders for a view. The scene reads these flags rather than deciding per +// call site what a kind of view gets. The flags are off by default and every factory sets +// every field, so a new field has to be decided for each kind of view. +struct ViewInfo +{ + // Passed on to GetBatches, so renderables can filter themselves (e.g. ShouldReflect) + Tr2RenderReason reason = TR2RENDERREASON_NORMAL; + Tr2RenderContextEnum::CullMode cullMode = Tr2RenderContextEnum::CULLMODE_CW; + bool distortion = false; + // Gather the scene's objects, camera attachments, static particles and impostors. + // Otherwise only the reflectable renderables in the component registry are gathered. + bool sceneObjects = false; + // The view feeds the next frame: view history for reprojection, temporal volumetrics + // and lens flare occlusion queries + bool temporal = false; + bool subsurfaceScattering = false; + bool gpuParticles = false; + bool lensflares = false; + + // Render the scene as a reflection probe sees it: damped reflection intensity, + // reflection background and the reflection shadow map + bool IsReflection() const + { + return reason == TR2RENDERREASON_REFLECTION; + } + + static ViewInfo Main( bool distortion ); + static ViewInfo ReflectionFace( bool lensflares ); +}; +} diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index f546339ab..dadd21320 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -145,118 +145,6 @@ TriFrustum CreatePickingFrustum() return pickFrustum; } } -namespace SceneRender -{ -ViewInfo ViewInfo::MainView( bool distortion ) -{ - ViewInfo info; - info.distortion = distortion; - return info; -} - -ViewInfo ViewInfo::ReflectionFace() -{ - ViewInfo view; - view.reason = TR2RENDERREASON_REFLECTION; - // Faces are rendered into a left handed cubemap, so the caller inverts the cull mode - // and the scene must not override it - view.cullMode = Tr2RenderContextEnum::CULLMODE_NONE; - view.distortion = false; - view.sceneObjects = false; - // faces must not disturb the main view's history - view.temporal = false; - view.reflection = true; - view.subsurfaceScattering = false; - view.gpuParticles = false; - view.lensflares = g_lensflaresInReflections && g_eveReflectionMode == EntityComponents::REFLECTION_SETTING_ULTRA; - return view; -} - -Features Features::ReflectionFace() -{ - // faces are rendered without TAA/upscaling, SSAO, velocity or the render nodes - Features features; - features.upscaling = false; - features.antiAliasing = false; - features.reflections = false; // no reflections rendered in the reflection faces, this can be removed when m_newRoute is removed - features.ssao = false; - features.velocityMap = false; - features.background = false; - features.overlay = false; - features.dynamicLights = false; - features.dynamicLightShadows = false; - return features; -} - -Features Features::ReflectionBackground() -{ - // the probe's first pass: nothing but the background - Features features = ReflectionFace(); - features.shadows = false; - features.background = true; - return features; -} - -namespace -{ -TriFrustum DeriveViewFrustum( const Matrix& view, const Matrix& projection, const Matrix& jitterMatrix, const TextureSize2D& renderSize ) -{ - const Matrix jitteredProjection = projection * jitterMatrix; - const Matrix inverseView = Inverse( view ); - const Vector3 position( inverseView._41, inverseView._42, inverseView._43 ); - - CTriViewport viewport; - viewport.width = renderSize.width; - viewport.height = renderSize.height; - - TriFrustum frustum; - frustum.DeriveFrustum( &view, &position, &jitteredProjection, viewport ); - return frustum; -} -} - -View View::Main( const Matrix& view, const TriProjectionPtr& projection, const Matrix& jitterMatrix, float mipLodBias, float upscalingAmount, const TextureSize2D& renderSize, bool distortion ) -{ - View result; - result.view = view; - result.projectionSource = projection; - result.projection = projection->GetTransform(); - result.jitterMatrix = jitterMatrix; - result.renderSize = renderSize; - result.mipLodBias = mipLodBias; - result.upscalingAmount = upscalingAmount; - result.frustum = DeriveViewFrustum( result.view, result.projection, result.jitterMatrix, result.renderSize ); - result.lightView = 0; - result.scene = ViewInfo::MainView( distortion ); - return result; -} - -View View::ReflectionFace( unsigned face, const Tr2ReflectionProbe& probe, const TriProjectionPtr& faceProjection, const TextureSize2D& faceRenderSize, bool backgroundOnly ) -{ - View result; - result.view = probe.GetFaceViewTransform( face, probe.GetPosition() ); - result.projectionSource = faceProjection; - result.projection = faceProjection->GetTransform(); - result.jitterMatrix = IdentityMatrix(); - result.renderSize = faceRenderSize; - result.mipLodBias = 0.0f; - result.upscalingAmount = 1.0f; - result.frustum = DeriveViewFrustum( result.view, result.projection, result.jitterMatrix, result.renderSize ); - result.scene = ViewInfo::ReflectionFace(); - if( backgroundOnly ) - { - result.lightView = 0; - result.scene.lensflares = false; - result.features = Features::ReflectionBackground(); - } - else - { - result.lightView = 1 + ( face - probe.GetStartFace() ); - result.features = Features::ReflectionFace(); - } - return result; -} -} struct ShadowReceiver { @@ -574,25 +462,35 @@ void EveSpaceScene::Update( Be::Time realTime, Be::Time simTime ) // -------------------------------------------------------------------------------------- // Description: -// Per-view part of the update: called for every view before the frame's Update and -// before it is rendered, so culling and post process attributes follow the view's -// camera. The frustum comes from the view, not from the bound viewport. +// Per-view part of the update: set the view's camera matrices and rendering parameters, +// apply the jittered projection to the renderer, and set the frustum and the detail +// thresholds that culling and LOD use. Called for every view before the frame's Update +// and before it is rendered. Only depends on the camera, so it is safe to call more than +// once for a view. // -------------------------------------------------------------------------------------- -void EveSpaceScene::UpdateView( const SceneRender::View& view ) +void EveSpaceScene::SetCamera( const EveSceneView::Camera& camera ) { - m_updateContext.SetFrustum( view.frustum ); - m_updateContext.SetHighDetailThreshold( g_eveSpaceSceneHighDetailThreshold / view.upscalingAmount ); - m_updateContext.SetMediumDetailThreshold( g_eveSpaceSceneMediumDetailThreshold / view.upscalingAmount ); - m_updateContext.SetLowDetailThreshold( g_eveSpaceSceneLowDetailThreshold / view.upscalingAmount ); - m_updateContext.SetVisibilityThreshold( g_eveSpaceSceneVisibilityThreshold / view.upscalingAmount ); - m_updateContext.SetLodFactor( g_eveSpaceSceneLODFactor / view.upscalingAmount ); + m_projection = camera.projection; + m_jitterMatrix = camera.jitterMatrix; + m_jitteredProjection = m_projection * m_jitterMatrix; + m_reprojectionMatrix = Inverse( m_projection ) * Inverse( camera.view ) * m_viewLast * m_projectionLast; + m_upscalingAmount = camera.upscalingAmount; + m_mipLodBias = camera.mipLodBias; + Tr2Renderer::SetProjectionTransform( m_jitteredProjection ); + + m_updateContext.SetFrustum( camera.frustum ); + m_updateContext.SetHighDetailThreshold( g_eveSpaceSceneHighDetailThreshold / camera.upscalingAmount ); + m_updateContext.SetMediumDetailThreshold( g_eveSpaceSceneMediumDetailThreshold / camera.upscalingAmount ); + m_updateContext.SetLowDetailThreshold( g_eveSpaceSceneLowDetailThreshold / camera.upscalingAmount ); + m_updateContext.SetVisibilityThreshold( g_eveSpaceSceneVisibilityThreshold / camera.upscalingAmount ); + m_updateContext.SetLodFactor( g_eveSpaceSceneLODFactor / camera.upscalingAmount ); m_updateContext.m_raytracingEnabled = m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows; } // -------------------------------------------------------------------------------------- // Description: -// Per-frame part of the update: animates and updates all objects, using the camera of -// the view applied with UpdateView before it for LOD. +// Per-frame part of the update: animates and updates all objects, using the camera set +// with SetCamera before it for LOD. // -------------------------------------------------------------------------------------- void EveSpaceScene::UpdateFrame( Be::Time realTime, Be::Time simTime ) { @@ -1416,7 +1314,7 @@ void EveSpaceScene::BeginFrame( Tr2RenderContext& renderContext ) // Per-view setup: lighting values, states, and frustum dependent gathering of batches. // Called for the main view and for each reflection face, after BeginFrame. // -------------------------------------------------------------------------------------- -void EveSpaceScene::BeginView( const SceneRender::View& view, Tr2RenderContext& renderContext ) +void EveSpaceScene::BeginView( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -1435,16 +1333,16 @@ void EveSpaceScene::BeginView( const SceneRender::View& view, Tr2RenderContext& // the previous view's value. It is read whenever the per-frame data is populated. if( m_volumetricsRenderer ) { - m_volumetricsRenderer->SetIsReflectionProbe( view.scene.reflection ); + m_volumetricsRenderer->SetIsReflectionProbe( view.IsReflection() ); } - renderContext.m_esm.BeginManagedRendering( view.scene.cullMode ); + renderContext.m_esm.BeginManagedRendering( view.cullMode ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_OPAQUE ); GatherBatches( view, renderContext ); // reflection views use the reflection shadow map, which is not raytraced - if( !view.scene.reflection && m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows ) + if( !view.IsReflection() && m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows ) { PrepareRaytracedShadows( renderContext ); } @@ -1525,11 +1423,11 @@ static float GetReflectionIntensityInReflections( float reflectionIntensity ) // Description: // Derive the lighting values for the current view from the per-frame base values. // -------------------------------------------------------------------------------------- -void EveSpaceScene::ApplyViewLighting( const SceneRender::View& view ) +void EveSpaceScene::ApplyViewLighting( const EveSceneView::ViewInfo& view ) { m_currentSunColor = m_baseSunColor; m_currentNebulaIntensity = m_baseNebulaIntensity; - m_currentReflectionIntensity = view.scene.reflection ? GetReflectionIntensityInReflections( m_baseReflectionIntensity ) : m_baseReflectionIntensity; + m_currentReflectionIntensity = view.IsReflection() ? GetReflectionIntensityInReflections( m_baseReflectionIntensity ) : m_baseReflectionIntensity; } // -------------------------------------------------------------------------------------- @@ -1578,7 +1476,7 @@ void EveSpaceScene::PrepareLensflares() // Description: // Gather all batches into the m_primaryBatches BatchMap. // -------------------------------------------------------------------------------------- -void EveSpaceScene::GatherBatches( const SceneRender::View& view, Tr2RenderContext& renderContext ) +void EveSpaceScene::GatherBatches( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -1589,7 +1487,7 @@ void EveSpaceScene::GatherBatches( const SceneRender::View& view, Tr2RenderConte const Matrix& identity = IdentityMatrix(); // Reflection views only get the dynamic objects once the probe is ready for them - const bool gatherObjects = view.scene.sceneObjects || ( m_dynamicObjectReflectionEnabled && m_reflectionProbe && m_reflectionProbe->ReadyForDynamicObjectReflections() ); + const bool gatherObjects = view.sceneObjects || ( m_dynamicObjectReflectionEnabled && m_reflectionProbe && m_reflectionProbe->ReadyForDynamicObjectReflections() ); { TRINITY_STATS_ZONE( "UpdateVisibility" ); @@ -1605,7 +1503,7 @@ void EveSpaceScene::GatherBatches( const SceneRender::View& view, Tr2RenderConte } ); } - if( view.scene.sceneObjects ) + if( view.sceneObjects ) { GatherSceneObjectRenderables( allObjects, renderables, renderContext ); } @@ -1618,15 +1516,15 @@ void EveSpaceScene::GatherBatches( const SceneRender::View& view, Tr2RenderConte Tr2QuadRenderer::Instance()->GetBatches( TRIBATCHTYPE_OPAQUE, m_primaryBatches[TRIBATCHTYPE_OPAQUE] ); Tr2QuadRenderer::Instance()->GetBatches( TRIBATCHTYPE_ADDITIVE, m_primaryBatches[TRIBATCHTYPE_ADDITIVE] ); - GetAllBatchesFromRenderables( renderables, transparentObjects, view.scene.distortion, m_primaryBatches, view.scene.reason ); - PrepareTransparentBatch( transparentObjects, m_primaryBatches, view.scene.reason ); + GetAllBatchesFromRenderables( renderables, transparentObjects, view.distortion, m_primaryBatches, view.reason ); + PrepareTransparentBatch( transparentObjects, m_primaryBatches, view.reason ); if( gatherObjects ) { m_instancedMeshManager->CollectMeshes( *m_componentRegistry ); // passing the reason filters out instanced meshes that should not be reflected - m_instancedMeshManager->GetBatches( m_updateContext.GetFrustum(), m_updateContext.GetInvLodFactor(), { { TRIBATCHTYPE_OPAQUE, *m_primaryBatches[TRIBATCHTYPE_OPAQUE] }, { TRIBATCHTYPE_DECAL, *m_primaryBatches[TRIBATCHTYPE_DECAL] }, { TRIBATCHTYPE_ADDITIVE, *m_primaryBatches[TRIBATCHTYPE_ADDITIVE] }, { TRIBATCHTYPE_DEPTH, *m_primaryBatches[TRIBATCHTYPE_DEPTH] }, { TRIBATCHTYPE_DISTORTION, *m_primaryBatches[TRIBATCHTYPE_DISTORTION] } }, view.scene.reason ); + m_instancedMeshManager->GetBatches( m_updateContext.GetFrustum(), m_updateContext.GetInvLodFactor(), { { TRIBATCHTYPE_OPAQUE, *m_primaryBatches[TRIBATCHTYPE_OPAQUE] }, { TRIBATCHTYPE_DECAL, *m_primaryBatches[TRIBATCHTYPE_DECAL] }, { TRIBATCHTYPE_ADDITIVE, *m_primaryBatches[TRIBATCHTYPE_ADDITIVE] }, { TRIBATCHTYPE_DEPTH, *m_primaryBatches[TRIBATCHTYPE_DEPTH] }, { TRIBATCHTYPE_DISTORTION, *m_primaryBatches[TRIBATCHTYPE_DISTORTION] } }, view.reason ); m_instancedMeshManager->ReportUsedScreenSizes(); } @@ -2174,7 +2072,7 @@ void EveSpaceScene::RenderReflectionPass( Tr2GpuResourcePool& gpuResourcePool, T // Returns: // boolean indicating whether background distortion is required. // -------------------------------------------------------------------------------------- -bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const SceneRender::View& view, Tr2RenderContext& renderContext ) +bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -2233,7 +2131,7 @@ bool EveSpaceScene::RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr else { renderContext.RenderPassHint( { Tr2LoadAction::LOAD, Tr2StoreAction::STORE }, { Tr2LoadAction::LOAD, Tr2StoreAction::STORE } ); - hasBackgroundDistortionBatches = RenderBackgroundPassObjects( depthMap, distortionMap, renderContext, view.scene.reflection ? BACKGROUND_RENDER_REFLECTION : BACKGROUND_RENDER_COLOR ); + hasBackgroundDistortionBatches = RenderBackgroundPassObjects( depthMap, distortionMap, renderContext, view.IsReflection() ? BACKGROUND_RENDER_REFLECTION : BACKGROUND_RENDER_COLOR ); } if( !m_planets.empty() ) { @@ -2370,7 +2268,7 @@ bool EveSpaceScene::RenderBackgroundPassObjects( const Tr2TextureAL& depthMap, c // Description: // Render opaque objects to populate a readable depth stencil. // -------------------------------------------------------------------------------------- -void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const SceneRender::View& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName ) +void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -2379,7 +2277,7 @@ void EveSpaceScene::RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2Text return; } - renderContext.m_esm.BeginManagedRendering( view.scene.cullMode ); + renderContext.m_esm.BeginManagedRendering( view.cullMode ); { // Update mesh morphs auto& meshMorphs = m_componentRegistry->GetComponents(); @@ -2809,14 +2707,14 @@ EveSpaceScene::ShadowResources EveSpaceScene::RenderReflectionShadows( const Tr2 return result; } -EveSpaceScene::ShadowResources EveSpaceScene::RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const SceneRender::View& view, Tr2RenderContext& renderContext ) +EveSpaceScene::ShadowResources EveSpaceScene::RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ) { if( !m_display || !m_enableShadows ) { return {}; } - if( view.scene.reflection ) + if( view.IsReflection() ) { return RenderReflectionShadows( depthMap, gpuResourcePool, renderContext ); } @@ -2892,14 +2790,14 @@ bool EveSpaceScene::RenderMainPass( const Tr2TextureAL& velocityMap, const Tr2TextureAL& opaqueColorMap, Tr2GpuResourcePool& gpuResourcePool, - const SceneRender::View& view, + const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); bool hasForegroundDistortionBatches = false; - renderContext.m_esm.BeginManagedRendering( view.scene.cullMode ); + renderContext.m_esm.BeginManagedRendering( view.cullMode ); if( !m_display ) { @@ -2949,7 +2847,7 @@ bool EveSpaceScene::RenderMainPass( GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", opaqueColorMap ); } - if( view.scene.subsurfaceScattering ) + if( view.subsurfaceScattering ) { m_sssss->SetupScreenSpaceSubSurfaceScattering( renderContext, m_primaryBatches[TRIBATCHTYPE_OPAQUE], colorMap, opaqueColorMap, depthMap, gpuResourcePool ); } @@ -2959,11 +2857,11 @@ bool EveSpaceScene::RenderMainPass( PopulateAndApplyPerFrameData( renderContext ); std::pair froxelFogTextures; - if( view.scene.reason == TR2RENDERREASON_NORMAL ) + if( view.reason == TR2RENDERREASON_NORMAL ) { froxelFogTextures = RenderVolumetrics( depthMap, gpuResourcePool, renderContext ); } - else if( view.scene.reason == TR2RENDERREASON_REFLECTION ) + else if( view.reason == TR2RENDERREASON_REFLECTION ) { froxelFogTextures = RenderReflectionVolumetrics( depthMap, gpuResourcePool, renderContext ); } @@ -2981,7 +2879,7 @@ bool EveSpaceScene::RenderMainPass( } //GPU particles - if( view.scene.gpuParticles && GetGpuParticleSystem() ) + if( view.gpuParticles && GetGpuParticleSystem() ) { GetGpuParticleSystem()->Render( renderContext ); } @@ -3028,7 +2926,7 @@ void EveSpaceScene::RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, // Description: // Render lensflares. Clear primary batches and frame dependant object lists. // -------------------------------------------------------------------------------------- -void EveSpaceScene::EndRender( const SceneRender::View& view, Tr2RenderContext& renderContext ) +void EveSpaceScene::EndRender( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -3041,7 +2939,7 @@ void EveSpaceScene::EndRender( const SceneRender::View& view, Tr2RenderContext& Tr2QuadRenderer::Instance()->DoneRendering( renderContext ); - renderContext.m_esm.BeginManagedRendering( view.scene.cullMode ); + renderContext.m_esm.BeginManagedRendering( view.cullMode ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_OPAQUE ); PopulatePerFramePSData( m_perFramePS, renderContext ); @@ -3073,7 +2971,7 @@ void EveSpaceScene::EndRender( const SceneRender::View& view, Tr2RenderContext& } // lensflares - if( !m_lensflares.empty() && view.scene.lensflares ) + if( !m_lensflares.empty() && view.lensflares ) { GPU_REGION( renderContext, "Lens Flares" ); // lensflares @@ -3090,7 +2988,7 @@ void EveSpaceScene::EndRender( const SceneRender::View& view, Tr2RenderContext& TRIBATCHTYPE_ADDITIVE, Tr2EffectStateManager::RM_ALPHA_ADDITIVE, renderContext, - view.scene.reason ); + view.reason ); renderContext.SetReadOnlyDepth( false ); } } @@ -3101,7 +2999,7 @@ void EveSpaceScene::EndRender( const SceneRender::View& view, Tr2RenderContext& ClearBatches( m_primaryBatches ); // Store the view transform from this frame - if( view.scene.temporal ) + if( view.temporal ) { m_viewLast = Tr2Renderer::GetViewTransform(); m_projectionLast = m_projection; @@ -4527,20 +4425,3 @@ void RegisterWithVariableStore( const EveSpaceScene::ShadowResources& shadowReso shadowResources.pointLightShadowMap.IsValid() ? shadowResources.pointLightShadowMap : gpuResourcePool.GetPersistentTexture( "EmptyShadowUint", 1, 1, ImageIO::PIXEL_FORMAT_R8_UINT, Tr2GpuUsage::SHADER_RESOURCE, &initData ) ); GlobalStore().RegisterVariable( "ShadowMapAtlas", shadowResources.pointLightShadowDepth ); } - -// -------------------------------------------------------------------------------------- -// Description: -// Set the view's camera matrices and rendering parameters, and apply the jittered -// projection to the renderer. Only depends on the view, so it is safe to call more than -// once for a view. -// -------------------------------------------------------------------------------------- -void EveSpaceScene::SetViewInfo( const SceneRender::View& view ) -{ - m_projection = view.projection; - m_jitterMatrix = view.jitterMatrix; - m_jitteredProjection = m_projection * m_jitterMatrix; - m_reprojectionMatrix = Inverse( m_projection ) * Inverse( view.view ) * m_viewLast * m_projectionLast; - m_upscalingAmount = view.upscalingAmount; - m_mipLodBias = view.mipLodBias; - Tr2Renderer::SetProjectionTransform( m_jitteredProjection ); -} diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 40fc3628b..70072b195 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -27,6 +27,7 @@ #include "../Tr2VolumetricsRenderer.h" #include "../Tr2RuntimeGpuBuffer.h" #include "EvePicking.h" +#include "EveSceneView.h" #include #include @@ -83,7 +84,6 @@ BLUE_DECLARE( Tr2ImpostorManager ); BLUE_DECLARE( Tr2DebugRenderer ); BLUE_DECLARE( EveEffectRoot2 ); BLUE_DECLARE( Tr2ReflectionProbe ); -BLUE_DECLARE( TriProjection ); BLUE_DECLARE( EvePlanet ); BLUE_DECLARE_VECTOR( EvePlanet ); BLUE_DECLARE( EveVirtualCameraSystem ); @@ -97,84 +97,6 @@ BLUE_DECLARE_VECTOR( Tr2PostProcessAttributes ); class EveInstancedMeshManager; -namespace SceneRender -{ -// Describes the view being rendered and what it renders. Built with MainView or -// ReflectionFace as part of a View, which is passed to every per-view call; the scene reads -// these flags rather than deciding per call site what a kind of view gets.S -struct ViewInfo -{ - // Passed on to GetBatches, so renderables can filter themselves (e.g. ShouldReflect) - Tr2RenderReason reason = TR2RENDERREASON_NORMAL; - Tr2RenderContextEnum::CullMode cullMode = Tr2RenderContextEnum::CULLMODE_CW; - bool distortion = true; - // Gather the scene's objects, camera attachments, static particles and impostors. - // Otherwise only the reflectable renderables in the component registry are gathered. - bool sceneObjects = true; - // The view feeds the next frame: view history for reprojection, temporal volumetrics - // and lens flare occlusion queries - bool temporal = true; - // Render the scene as a reflection probe sees it: damped reflection intensity, - // reflection background and the reflection shadow map - bool reflection = false; - bool subsurfaceScattering = true; - bool gpuParticles = true; - bool lensflares = true; - - static ViewInfo MainView( bool distortion ); - static ViewInfo ReflectionFace(); -}; - -// Driver side features of a view. What the scene renders is described by the ViewInfo in -// View. -struct Features -{ - bool upscaling = true; - bool antiAliasing = true; - bool reflections = true; - bool ssao = true; - bool shadows = true; - bool velocityMap = true; - // special background rendering (f.ex texture background) - bool background = true; - // special overlay rendering (f.ex texture overlay) - bool overlay = true; - - bool dynamicLights = true; - bool dynamicLightShadows = true; - - static Features ReflectionFace(); - static Features ReflectionBackground(); -}; - -// A view to render: its camera, what the scene renders from it (scene) and which driver -// features it uses (features). Built by the render driver with Main or ReflectionFace. -struct View -{ - Matrix view; - // sets the renderer's projection - TriProjectionPtr projectionSource; - Matrix projection; - Matrix jitterMatrix; - TextureSize2D renderSize; - float mipLodBias = 0.0f; - float upscalingAmount = 1.0f; - // derived from the view, the jittered projection and renderSize - TriFrustum frustum; - - // The Tr2LightManager view of this view: 0 is the main view, 1.. are reflection probe faces - uint32_t lightView = 0; - - ViewInfo scene; - Features features; - - static View Main( const Matrix& view, const TriProjectionPtr& projection, const Matrix& jitterMatrix, float mipLodBias, float upscalingAmount, const TextureSize2D& renderSize, bool distortion ); - // backgroundOnly: the probe's first pass, which draws only the background into the faces - static View ReflectionFace( unsigned face, const Tr2ReflectionProbe& probe, const TriProjectionPtr& faceProjection, const TextureSize2D& faceRenderSize, bool backgroundOnly = false ); -}; - -} - BLUE_CLASS( EveSpaceScene ) : public ITr2Scene, @@ -210,7 +132,7 @@ BLUE_CLASS( EveSpaceScene ) : Tr2GpuResourcePool::Texture pointLightShadowDepth; }; - ShadowResources RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const SceneRender::View& view, Tr2RenderContext& renderContext ); + ShadowResources RenderShadows( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, Tr2GpuResourcePool& gpuResourcePool, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); bool RenderMainPass( const Tr2TextureAL& colorMap, const Tr2TextureAL& depthMap, @@ -218,25 +140,24 @@ BLUE_CLASS( EveSpaceScene ) : const Tr2TextureAL& velocityMap, const Tr2TextureAL& opaqueColorMap, Tr2GpuResourcePool& gpuResourcePool, - const SceneRender::View& view, + const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ); - void RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const SceneRender::View& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName = BlueSharedString( "Depth" ) ); + void RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName = BlueSharedString( "Depth" ) ); - bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const SceneRender::View& view, Tr2RenderContext& renderContext ); + bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void RenderReflectionPass( Tr2GpuResourcePool & gpuResourcePool, Tr2RenderContext & renderContext ); void BeginFrame( Tr2RenderContext& renderContext ); - void BeginView( const SceneRender::View& view, Tr2RenderContext& renderContext ); - void EndRender( const SceneRender::View& view, Tr2RenderContext& renderContext ); + void BeginView( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); + void EndRender( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void Render3DUI( Tr2RenderContext & renderContext ); void PopulateAndApplyPerFrameData( Tr2RenderContext& renderContext ); void ApplyUpscalingToPerFrameData( uint32_t width, uint32_t height, Tr2RenderContext& renderContext ); - void GatherBatches( const SceneRender::View& view, Tr2RenderContext& renderContext ); + void GatherBatches( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void PrepareRaytracedShadows( Tr2RenderContext & renderContext ); - void SetViewInfo( const SceneRender::View& view ); - void UpdateView( const SceneRender::View& view ); + void SetCamera( const EveSceneView::Camera& camera ); Tr2ReflectionProbe* GetReflectionProbe() const; ////////////////////////////////////////////////////////////////////////////////////// @@ -689,7 +610,7 @@ BLUE_CLASS( EveSpaceScene ) : void UpdateFrame( Be::Time realTime, Be::Time simTime ); void EnsureVisualizerEffect(); void ResolveLightingOverrides(); - void ApplyViewLighting( const SceneRender::View& view ); + void ApplyViewLighting( const EveSceneView::ViewInfo& view ); void GatherDynamicLights( Tr2RenderContext& renderContext ); void PrepareLensflares(); void GatherSceneObjectRenderables( std::vector& allObjects, std::vector& renderables, Tr2RenderContext& renderContext ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 3ad19be6e..9710d507c 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -19,6 +19,7 @@ CCP_STATS_DECLARE( updateDynamicLightLists, "Trinity/EveSpaceScene/updateDynamicLights", true, CST_TIME, "Time took to gather dynamic lights for EveSpaceScene" ); extern bool g_eveSpaceSceneDynamicLighting; +extern bool g_lensflaresInReflections; namespace { @@ -325,15 +326,62 @@ void EveSpaceSceneRenderDriver::PropagateSettings() } } +EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::Main() +{ + Features features; + features.upscaling = true; + features.antiAliasing = true; + features.reflections = true; + features.ssao = true; + features.shadows = true; + features.velocityMap = true; + features.background = true; + features.overlay = true; + features.dynamicLights = true; + return features; +} + +EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::ReflectionFace() +{ + // faces are rendered without TAA/upscaling, SSAO, velocity or the render nodes + Features features; + features.upscaling = false; + features.antiAliasing = false; + features.reflections = false; // no reflections rendered in the reflection faces, this can be removed when m_newRoute is removed + features.ssao = false; + features.shadows = true; + features.velocityMap = false; + features.background = false; + features.overlay = false; + features.dynamicLights = true; + return features; +} + +EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::ReflectionBackground() +{ + // the probe's first pass: nothing but the background + Features features; + features.upscaling = false; + features.antiAliasing = false; + features.reflections = false; + features.ssao = false; + features.shadows = false; + features.velocityMap = false; + features.background = true; + features.overlay = false; + features.dynamicLights = false; + return features; +} + // -------------------------------------------------------------------------------------- // Description: -// Set the view's camera to the renderer and its rendering parameters to the scene. +// Set the camera's view transform to the renderer and the camera to the scene, which +// also applies its projection to the renderer. // -------------------------------------------------------------------------------------- -void EveSpaceSceneRenderDriver::ApplyView( const SceneRender::View& view, Tr2RenderContext& renderContext ) const +void EveSpaceSceneRenderDriver::ApplyCamera( const EveSceneView::Camera& camera ) const { - SetCameraToRenderer( view.view, view.projectionSource, renderContext ); - m_scene->SetViewInfo( view ); - m_scene->UpdateView( view ); + Tr2Renderer::SetViewTransform( camera.view ); + m_scene->SetCamera( camera ); } // -------------------------------------------------------------------------------------- @@ -341,10 +389,9 @@ void EveSpaceSceneRenderDriver::ApplyView( const SceneRender::View& view, Tr2Ren // The scene's per-frame update, done once and from the main camera before any view is // rendered, so the frame's object updates and LOD follow the main view. // -------------------------------------------------------------------------------------- -void EveSpaceSceneRenderDriver::UpdateSceneFrame( const SceneRender::View& mainView, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +void EveSpaceSceneRenderDriver::UpdateSceneFrame( const EveSceneView::Camera& mainCamera, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { - // the projection depends on the viewport, so set up the main view's target first - ApplyView( mainView, renderContext ); + ApplyCamera( mainCamera ); { TimeSection updateSection( m_timers.update, "Update", rootTimer, renderContext ); m_scene->Update( realTime, simTime ); @@ -442,12 +489,6 @@ void EveSpaceSceneRenderDriver::SetCameraToRenderer( Tr2RenderContext& renderCon Tr2Renderer::SetViewTransform( view->GetTransform() ); } -void EveSpaceSceneRenderDriver::SetCameraToRenderer( const Matrix& view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const -{ - projection->SetProjection( renderContext ); - Tr2Renderer::SetViewTransform( view ); -} - TextureSize2D EveSpaceSceneRenderDriver::GetRenderSize( const TextureSize2D& displaySize ) const { if( m_upscalingContext ) @@ -459,6 +500,15 @@ TextureSize2D EveSpaceSceneRenderDriver::GetRenderSize( const TextureSize2D& dis return displaySize; } +EveSceneView::Camera EveSpaceSceneRenderDriver::GetMainCamera( const TextureSize2D& renderSize, const Matrix& jitterMatrix, float mipLodBias ) const +{ + auto projection = m_camera ? m_camera->GetProjection() : m_projection; + auto view = m_camera ? m_camera->GetViewMatrix() : m_view; + const float upscalingAmount = m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f; + + return EveSceneView::Camera( view->GetTransform(), projection->GetTransform(), renderSize, jitterMatrix, mipLodBias, upscalingAmount ); +} + void EveSpaceSceneRenderDriver::Execute( const Span& destinations, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { const bool hasCamera = m_camera || ( m_view && m_projection ); @@ -471,15 +521,7 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina } const TextureSize2D displaySize = destinations.size > 0 ? TextureSize2D( destinations.data->GetDesc() ) : TextureSize2D(); - const SceneRender::View mainView = SceneRender::View::Main( - m_camera ? m_camera->GetViewMatrix()->GetTransform() : m_view->GetTransform(), - m_camera ? m_camera->GetProjection() : m_projection, - IdentityMatrix(), - 0.0f, - m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f, - GetRenderSize( displaySize ), - m_settings.enableDistortion ); - ApplyView( mainView, renderContext ); + ApplyCamera( GetMainCamera( GetRenderSize( displaySize ), IdentityMatrix(), 0.0f ) ); m_scene->Update( realTime, simTime ); UpdateGpuParticleSystem( renderContext ); return; @@ -522,14 +564,11 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina PrepareLightManager(); - const SceneRender::View mainView = SceneRender::View::Main( - m_camera ? m_camera->GetViewMatrix()->GetTransform() : m_view->GetTransform(), - m_camera ? m_camera->GetProjection() : m_projection, - GetJitterMatrix( renderSize.width, renderSize.height, renderContext ), - GetMipLodBias(), - m_upscalingContext ? m_upscalingContext->GetUpscalingAmount() : 1.0f, - renderSize, - m_settings.enableDistortion ); + const RenderInfo mainView = { + GetMainCamera( renderSize, GetJitterMatrix( renderSize.width, renderSize.height, renderContext ), GetMipLodBias() ), + EveSceneView::ViewInfo::Main( m_settings.enableDistortion ), + Features::Main() + }; auto colorTarget = m_gpuResourcePool.GetTempTexture( "customBackBuffer", renderSize, m_internalPixelFormat, Tr2GpuUsage::RENDER_TARGET | Tr2GpuUsage::SHADER_RESOURCE ); ViewTargets targets; @@ -539,7 +578,7 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina ON_BLOCK_EXIT( [&] { m_scene->m_visualizeMethod = prevVisualizeMethod; } ); BeginRenderPass( renderContext, { colorTarget }, {} ); - UpdateSceneFrame( mainView, realTime, simTime, rootTimer, renderContext ); + UpdateSceneFrame( mainView.camera, realTime, simTime, rootTimer, renderContext ); ReflectionFaces reflectionFaces; { TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); @@ -552,12 +591,13 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina // the lights are gathered once, for the main view and the faces that have lights if( auto lightManager = Tr2LightManager::GetInstance() ) { - lightManager->SetViewFrustum( static_cast( mainView.lightView ), mainView.frustum ); - for( const SceneRender::View& faceView : reflectionFaces.views ) + lightManager->SetViewFrustum( static_cast( mainView.lightView ), mainView.camera.frustum ); + + for( const RenderInfo& faceView : reflectionFaces.views ) { if( faceView.features.dynamicLights ) { - lightManager->SetViewFrustum( static_cast( faceView.lightView ), faceView.frustum ); + lightManager->SetViewFrustum( static_cast( faceView.lightView ), faceView.camera.frustum ); } } } @@ -654,20 +694,20 @@ float EveSpaceSceneRenderDriver::GetMipLodBias() const // colorTarget. Returns the targets the view rendered, for post processing or for // submitting to the reflection probe. // -------------------------------------------------------------------------------------- -EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const SceneRender::View& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) { - const TextureSize2D& renderSize = view.renderSize; + const TextureSize2D& renderSize = view.camera.renderSize; ViewTargets targets; const Tr2TextureAL& backBuffer = colorTarget; - ApplyView( view, renderContext ); + ApplyCamera( view.camera ); // every view with lights makes its own view active, so it can't read another view's lights if( view.features.dynamicLights ) { if( auto lightManager = Tr2LightManager::GetInstance() ) { - lightManager->SetActiveView( static_cast( view.lightView ), view.frustum ); + lightManager->SetActiveView( static_cast( view.lightView ) ); } } @@ -689,7 +729,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co { TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); - m_scene->BeginView( view, renderContext ); + m_scene->BeginView( view.scene, renderContext ); } // Old route: the scene renders the reflection faces itself, from within the main view if( view.features.reflections && !m_newRoute ) @@ -708,7 +748,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co bool hasBackgroundDistortionBatches = false; { TimeSection backgroundSection( m_timers.background, "BackgroundPass", rootTimer, renderContext ); - hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, view, renderContext ); + hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, view.scene, renderContext ); } if( view.scene.distortion && hasBackgroundDistortionBatches ) { @@ -722,7 +762,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co Tr2GpuResourcePool::Texture customStencil = GetCustomStencilMapIfNeeded( renderSize, outputs ); { TimeSection depthPassSection( m_timers.depthPass, "DepthPass", rootTimer, renderContext ); - m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, view, renderContext, m_depthPassTechnique ); + m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, view.scene, renderContext, m_depthPassTechnique ); } GlobalStore().RegisterVariable( "SpaceSceneNormalMap", normalMap ); GlobalStore().RegisterVariable( "SpaceSceneCustomStencil", customStencil ); @@ -734,7 +774,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co if( view.features.shadows ) { TimeSection shadowsSection( m_timers.shadows, "Shadows", rootTimer, renderContext ); - shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, view, renderContext ); + shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, view.scene, renderContext ); } TimeSection ssaoSection( m_timers.ssao, "SSAO", rootTimer, renderContext ); auto ssao = view.features.ssao ? RenderSSAO( depthBuffer, normalMap, renderContext ) : GetEmptySSAO( m_gpuResourcePool ); @@ -759,7 +799,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co distortionMap = GetDistortionMapIfNeeded( renderSize, view.scene.distortion ); { TimeSection mainPassSection( m_timers.mainPass, "MainPass", rootTimer, renderContext ); - hasDistortionBatches = m_scene->RenderMainPass( backBuffer, depthBuffer, distortionMap, velocityMap, targets.opaqueColor, m_gpuResourcePool, view, renderContext ); + hasDistortionBatches = m_scene->RenderMainPass( backBuffer, depthBuffer, distortionMap, velocityMap, targets.opaqueColor, m_gpuResourcePool, view.scene, renderContext ); } RegisterWithVariableStore( {}, m_gpuResourcePool ); @@ -799,7 +839,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co } { TimeSection endRenderSection( m_timers.endRender, "EndRender", rootTimer, renderContext ); - m_scene->EndRender( view, renderContext ); + m_scene->EndRender( view.scene, renderContext ); } // non-temporal views leave the scene's history untouched, so this is a no-op for them m_viewLast = m_scene->m_viewLast; @@ -824,11 +864,6 @@ EveSpaceSceneRenderDriver::ReflectionFaces EveSpaceSceneRenderDriver::PrepareRef SetCameraToRenderer( renderContext ); probe->UpdatePosition( Tr2Renderer::GetViewPosition() ); const Matrix faceProjection = probe->GetFaceProjection( Tr2Renderer::GetProjectionTransform() ); - - TriProjectionPtr projection; - projection.CreateInstance(); - projection->CustomProjection( faceProjection ); - const TextureSize2D faceRenderSize = probe->GetFaceTarget( 0 ).GetDesc(); faces.probe = probe; @@ -836,12 +871,20 @@ EveSpaceSceneRenderDriver::ReflectionFaces EveSpaceSceneRenderDriver::PrepareRef faces.startFace = probe->GetStartFace(); faces.viewDirection = Tr2Renderer::GetInverseViewTransform().GetZ(); - const bool dynamicLights = !faces.firstPass && Tr2LightManager::GetInstance() != nullptr; + const bool lensflares = g_lensflaresInReflections && m_settings.reflectionQuality == ReflectionQuality::ULTRA; for( unsigned face = probe->GetStartFace(); face < probe->GetEndFace(); ++face ) { - SceneRender::View faceView = SceneRender::View::ReflectionFace( face, *probe, projection, faceRenderSize, faces.firstPass ); - faceView.features.dynamicLights = dynamicLights; - faces.views.push_back( faceView ); + const EveSceneView::Camera camera = EveSceneView::Camera( probe->GetFaceViewTransform( face, probe->GetPosition() ), faceProjection, faceRenderSize ); + if( faces.firstPass ) + { + // the probe's first pass draws only the background: no lens flares and no light view + faces.views.push_back( { camera, EveSceneView::ViewInfo::ReflectionFace( false ), Features::ReflectionBackground() } ); + } + else + { + // with one side per frame, the rendered face always uses the first face light view + faces.views.push_back( { camera, EveSceneView::ViewInfo::ReflectionFace( lensflares ), Features::ReflectionFace(), 1 + ( face - faces.startFace ) } ); + } } return faces; @@ -893,7 +936,7 @@ void EveSpaceSceneRenderDriver::RenderReflectionFaces( const ReflectionFaces& fa ON_BLOCK_EXIT( [&] { renderContext.m_esm.SetInvertedCullMode( prevCullInversion ); } ); unsigned face = faces.startFace; - for( const SceneRender::View& faceView : faces.views ) + for( const RenderInfo& faceView : faces.views ) { std::string regionName = "Reflection Face " + std::to_string( face ); GPU_REGION( renderContext, regionName.c_str() ); @@ -945,7 +988,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetDistortionMapIfNeeded( return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const Features& featureSet ) { if( !featureSet.velocityMap ) { @@ -968,7 +1011,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( c return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const Features& featureSet ) { // SSAO and raytraced shadows read the normal map const bool needsNormalMap = ( featureSet.ssao && m_settings.aoQuality != AmbientOcclusionQuality::Disabled ) || ( featureSet.shadows && m_settings.shadowQuality == ShadowQuality::SHADOW_RAYTRACED ); @@ -1000,7 +1043,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetCustomStencilMapIfNeed return {}; } -Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ) +Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const Features& featureSet ) { // the opaque copy is only used by views that can be upscaled or anti-aliased if( !featureSet.upscaling && !featureSet.antiAliasing ) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 84c21536e..4bb90a96a 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -89,6 +89,41 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : private: + // The driver's features a view uses. What the scene renders is described by the + // SceneRender::ViewInfo in RenderInfo. The features are off by default and every factory + // sets every feature, so a new feature has to be decided for each kind of view. + struct Features + { + bool upscaling = false; + bool antiAliasing = false; + // the old route renders the reflection probe faces from within this view + bool reflections = false; + bool ssao = false; + bool shadows = false; + bool velocityMap = false; + // special background rendering (f.ex texture background) + bool background = false; + // special overlay rendering (f.ex texture overlay) + bool overlay = false; + bool dynamicLights = false; + + static Features Main(); + static Features ReflectionFace(); + // the probe's first pass, which only draws the background into the faces + static Features ReflectionBackground(); + }; + + // A view rendered by RenderView: its camera and what the scene renders from it, the + // driver's features, and its view in the Tr2LightManager + struct RenderInfo + { + EveSceneView::Camera camera; + EveSceneView::ViewInfo scene; + Features features; + // 0 is the main view, 1.. are the reflection probe faces rendered in the frame + uint32_t lightView = 0; + }; + // Targets rendered by RenderView struct ViewTargets { @@ -105,29 +140,29 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : bool firstPass = false; unsigned startFace = 0; Vector3 viewDirection = Vector3( 0.0f, 0.0f, 0.0f ); - std::vector views; + std::vector views; }; - ViewTargets RenderView( const SceneRender::View& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + ViewTargets RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); ReflectionFaces PrepareReflectionFaces( Tr2RenderContext& renderContext ); void UpdateLightManagerFaceViews( const ReflectionFaces& faces ); void RenderReflectionFaces( const ReflectionFaces& faces, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetupUpscaling( const TextureSize2D& displaySize ); void PropagateSettings(); - void ApplyView( const SceneRender::View& view, Tr2RenderContext& renderContext ) const; - void UpdateSceneFrame( const SceneRender::View& mainView, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + void ApplyCamera( const EveSceneView::Camera& camera ) const; + void UpdateSceneFrame( const EveSceneView::Camera& mainCamera, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetCameraToRenderer( Tr2RenderContext & renderContext ) const; - void SetCameraToRenderer( const Matrix& view, TriProjectionPtr projection, Tr2RenderContext& renderContext ) const; TextureSize2D GetRenderSize( const TextureSize2D& displaySize ) const; + EveSceneView::Camera GetMainCamera( const TextureSize2D& renderSize, const Matrix& jitterMatrix, float mipLodBias ) const; Matrix GetJitterMatrix( uint32_t renderWidth, uint32_t renderHeight, Tr2RenderContext& renderContext ) const; float GetMipLodBias( ) const; Tr2GpuResourcePool::Texture GetDistortionMapIfNeeded( const TextureSize2D& size, bool distortion ); - Tr2GpuResourcePool::Texture GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ); - Tr2GpuResourcePool::Texture GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ); + Tr2GpuResourcePool::Texture GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const Features& featureSet ); + Tr2GpuResourcePool::Texture GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const Features& featureSet ); Tr2GpuResourcePool::Texture GetCustomStencilMapIfNeeded( const TextureSize2D& size, const Span& outputs ); - Tr2GpuResourcePool::Texture GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const SceneRender::Features& featureSet ); + Tr2GpuResourcePool::Texture GetOpaqueColorMapIfNeeded( const TextureSize2D& size, const Span& outputs, const Features& featureSet ); void UpdateGpuParticleSystem( Tr2RenderContext & renderContext ); void PrepareLightManager(); diff --git a/trinity/Tr2LightManager.cpp b/trinity/Tr2LightManager.cpp index d0425e096..a427b0e93 100644 --- a/trinity/Tr2LightManager.cpp +++ b/trinity/Tr2LightManager.cpp @@ -259,9 +259,7 @@ void Tr2LightManager::ClearReflectionViews() if( m_activeView != MAIN_VIEW ) { - m_activeView = MAIN_VIEW; - m_lightBufferVariable = m_views[MAIN_VIEW].lightBuffer; - m_indexBufferVariable = m_views[MAIN_VIEW].indexBuffer; + SetActiveView( MAIN_VIEW ); } } @@ -294,7 +292,7 @@ void Tr2LightManager::SetViewFrustum( uint8_t viewIndex, const TriFrustum& frust m_views[viewIndex].frustum = frustum; } -void Tr2LightManager::SetActiveView( uint8_t viewIndex, const TriFrustum& frustum ) +void Tr2LightManager::SetActiveView( uint8_t viewIndex ) { if( viewIndex >= m_views.size() ) { @@ -304,7 +302,6 @@ void Tr2LightManager::SetActiveView( uint8_t viewIndex, const TriFrustum& frustu m_activeView = viewIndex; View& view = m_views[viewIndex]; - view.frustum = frustum; m_lightBufferVariable = view.lightBuffer; m_indexBufferVariable = view.indexBuffer; } @@ -765,9 +762,6 @@ void Tr2LightManager::ResolveView( View& view ) ALResult Tr2LightManager::UpdateLists( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ) { View& view = GetActiveView(); - m_lightBufferVariable = view.lightBuffer; - m_indexBufferVariable = view.indexBuffer; - if( view.lightData.empty() ) { return ClearLightIndices( view, renderContext ); @@ -808,10 +802,6 @@ bool Tr2LightManager::OnPrepareResources() } m_perFrameData.Create( sizeof( PerFrameData ), Tr2ConstantUsageAL::ONE_SHOT, nullptr, renderContext ); - // DoUpdateLists points the effect at the buffers of the view it works on - m_effect->SetParameter( BlueSharedString( "LightBuffer" ), m_views[MAIN_VIEW].lightBuffer ); - m_effect->SetParameter( BlueSharedString( "LightIndices" ), m_views[MAIN_VIEW].indexBuffer ); - m_effect->SetParameter( BlueSharedString( "LightIndexCount" ), m_views[MAIN_VIEW].indexBufferCounter ); m_effect->SetParameter( BlueSharedString( "DepthMap" ), Tr2TextureAL() ); return true; diff --git a/trinity/Tr2LightManager.h b/trinity/Tr2LightManager.h index beb31069f..bf42cbf52 100644 --- a/trinity/Tr2LightManager.h +++ b/trinity/Tr2LightManager.h @@ -138,7 +138,7 @@ class Tr2LightManager : public Tr2DeviceResource uint8_t AddReflectionView(); uint32_t GetReflectionViewCount() const; void SetViewFrustum( uint8_t viewIndex, const TriFrustum& frustum ); - void SetActiveView( uint8_t viewIndex, const TriFrustum& frustum ); + void SetActiveView( uint8_t viewIndex ); void AddPointLight( const Vector3& position, float radius, const Color& color, Float_16 innerRadius = Float_16( 0.f ), uint16_t flags = FLAG_DEFAULT, bool scaleBrightness = true ); void AddLight( PerLightData& data, bool scaleBrightness = true, const Tr2LightTags& tags = Tr2LightTags() ); From 3212684f77f7584ba19a8088f7618fa09d943317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Tue, 27 Oct 2026 13:24:58 +0000 Subject: [PATCH 08/20] Removing newroute So now the reflections always get Iights --- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 18 +++--------------- trinity/Eve/EveSpaceSceneRenderDriver.h | 4 ---- trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp | 6 ------ 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 9710d507c..982b13b4e 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -331,7 +331,6 @@ EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::Main() Features features; features.upscaling = true; features.antiAliasing = true; - features.reflections = true; features.ssao = true; features.shadows = true; features.velocityMap = true; @@ -347,7 +346,6 @@ EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::Reflect Features features; features.upscaling = false; features.antiAliasing = false; - features.reflections = false; // no reflections rendered in the reflection faces, this can be removed when m_newRoute is removed features.ssao = false; features.shadows = true; features.velocityMap = false; @@ -363,7 +361,6 @@ EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::Reflect Features features; features.upscaling = false; features.antiAliasing = false; - features.reflections = false; features.ssao = false; features.shadows = false; features.velocityMap = false; @@ -588,7 +585,7 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina reflectionFaces = PrepareReflectionFaces( renderContext ); UpdateLightManagerFaceViews( reflectionFaces ); - // the lights are gathered once, for the main view and the faces that have lights + // the lights are gathered once, for all views if( auto lightManager = Tr2LightManager::GetInstance() ) { lightManager->SetViewFrustum( static_cast( mainView.lightView ), mainView.camera.frustum ); @@ -603,10 +600,6 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina } m_scene->GatherDynamicLights( renderContext ); } - - // The new route renders the reflection faces through RenderView. The old route lets - // the scene render them from within the main view (see RenderView). - if( m_newRoute ) { TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); RenderReflectionFaces( reflectionFaces, realTime, simTime, rootTimer, renderContext ); @@ -731,12 +724,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); m_scene->BeginView( view.scene, renderContext ); } - // Old route: the scene renders the reflection faces itself, from within the main view - if( view.features.reflections && !m_newRoute ) - { - TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); - m_scene->RenderReflectionPass( m_gpuResourcePool, renderContext ); - } + GlobalStore().RegisterVariable( "DepthMap", targets.depth ); SetNamedOutput( outputs, "DepthMap", targets.depth ); @@ -855,7 +843,7 @@ EveSpaceSceneRenderDriver::ReflectionFaces EveSpaceSceneRenderDriver::PrepareRef { ReflectionFaces faces; - Tr2ReflectionProbe* probe = m_newRoute ? m_scene->GetReflectionProbe() : nullptr; + Tr2ReflectionProbe* probe = m_scene->GetReflectionProbe(); if( !probe || !probe->IsValid() || !m_scene->m_display ) { return faces; diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 4bb90a96a..747b59929 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -96,8 +96,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : { bool upscaling = false; bool antiAliasing = false; - // the old route renders the reflection probe faces from within this view - bool reflections = false; bool ssao = false; bool shadows = false; bool velocityMap = false; @@ -228,8 +226,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : Tr2ProfileTimer shadows; Tr2ProfileTimer ssao; } m_timers; - - bool m_newRoute = false; }; TYPEDEF_BLUECLASS( EveSpaceSceneRenderDriver ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp b/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp index 10974c1d9..802e44197 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp @@ -76,12 +76,6 @@ const Be::ClassInfo* EveSpaceSceneRenderDriver::ExposeToBlue() "", Be::READWRITE ) - MAP_ATTRIBUTE( - "newRoute", - m_newRoute, - "", - Be::READWRITE ) - MAP_ATTRIBUTE( "enableRendering", m_enableRendering, From ba1b16b057b3a38b3bbcb682f47f5c0e96094ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Tue, 27 Oct 2026 13:57:48 +0000 Subject: [PATCH 09/20] Removing redundant code Removing reflection rendering code from the EveSpaceScene. In turn there were unneeded functions in Tr2ReflectionProbe removed as well --- trinity/Eve/EveSpaceScene.cpp | 212 --------------------------------- trinity/Eve/EveSpaceScene.h | 1 - trinity/Tr2ReflectionProbe.cpp | 93 --------------- trinity/Tr2ReflectionProbe.h | 15 --- 4 files changed, 321 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index dadd21320..7212d7cb4 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1854,218 +1854,6 @@ Tr2QuadRenderer* EveSpaceScene::GetQuadRenderer() const return Tr2QuadRenderer::Instance(); } -void EveSpaceScene::RenderReflectionPass( Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ) -{ - if( !HasReflectionProbe() || !m_display ) - { - return; - } - - TRINITY_STATS_ZONE( __FUNCTION__ ); - - GPU_REGION( renderContext, "Reflection" ) - - m_volumetricsRenderer->SetIsReflectionProbe( true ); - ON_BLOCK_EXIT( [&] { - m_volumetricsRenderer->SetIsReflectionProbe( false ); - } ); - - auto tmp = m_currentReflectionIntensity; - m_currentReflectionIntensity = GetReflectionIntensityInReflections( m_currentReflectionIntensity ); - - { - // Override DepthMap in the variable store - ITr2TextureProvider* bkDepthMapTexVar = nullptr; - CTr2TextureReference faceDepthBuffer; - if( m_depthMapVar.GetType() == TRIVARIABLE_TEXTURE_RES ) - { - m_depthMapVar.GetValue( bkDepthMapTexVar ); - if( bkDepthMapTexVar ) - { - bkDepthMapTexVar->Lock(); - } - - m_depthMapVar = &faceDepthBuffer; - } - ON_BLOCK_EXIT( [&] { - if( m_depthMapVar.GetType() == TRIVARIABLE_TEXTURE_RES ) - { - m_depthMapVar = bkDepthMapTexVar; - if( bkDepthMapTexVar ) - { - bkDepthMapTexVar->Unlock(); - } - } - UpdateVariableStore(); - } ); - - auto bkProjection = m_projection; - ON_BLOCK_EXIT( [&] { m_projection = bkProjection; } ); - auto bkJitter = m_jitterMatrix; - m_jitterMatrix = IdentityMatrix(); - ON_BLOCK_EXIT( [&] { m_jitterMatrix = bkJitter; } ); - - // we change the frustum during reflection updates/renderings - // we need the old one for resetting - auto normalFrustum = m_updateContext.GetFrustum(); - - - m_reflectionProbe->InitRenderPass( renderContext ); - - m_projection = Tr2Renderer::GetProjectionTransform(); - - for( unsigned i = m_reflectionProbe->GetStartFace(); i < m_reflectionProbe->GetEndFace(); i++ ) - { - std::string contextName = "Reflection Face " + std::to_string( i ); - - GPU_REGION( renderContext, contextName.c_str() ); - m_reflectionProbe->StartRenderFace( i, renderContext ); - - PopulatePerFramePSData( m_perFramePS, renderContext ); - PopulatePerFrameVSData( m_perFrameVS, renderContext ); - - ApplyPerFrameData( renderContext ); - *faceDepthBuffer.GetTexture() = m_reflectionProbe->GetDepthBuffer( i ); - - { - Matrix orgViewMatrix = Tr2Renderer::GetViewTransform(); - Matrix planetViewMatrix = CreatePlanetViewMatrix( orgViewMatrix ); - Tr2Renderer::SetViewTransform( planetViewMatrix ); - - TriFrustum frustum; - Matrix planetProjection = EveCamera::ModifyClipPlanes( Tr2Renderer::GetProjectionTransform(), 0.01f, 1e5f ); - frustum.DeriveFrustum( &Tr2Renderer::GetViewTransform(), &Tr2Renderer::GetViewPosition(), &planetProjection, renderContext.m_esm.GetViewport() ); - m_updateContext.SetFrustum( frustum ); - - for( auto& planet : m_planets ) - { - planet->UpdatePlanetVisibility( m_updateContext, m_planetScale ); - } - - Tr2Renderer::SetViewTransform( orgViewMatrix ); - } - - TriFrustum currentFrustum = m_reflectionProbe->GetFrustum( renderContext ); - m_updateContext.SetFrustum( currentFrustum ); - // get the background reflection renderables from the component registry - RenderBackgroundPassObjects( {}, {}, renderContext, BackgroundRenderingReason::BACKGROUND_RENDER_REFLECTION ); - - if( g_lensflaresInReflections && !m_lensflares.empty() && g_eveReflectionMode == EntityComponents::REFLECTION_SETTING_ULTRA ) - { - GPU_REGION( renderContext, "Lens Flares in reflections" ); - std::vector visible; - - // lensflares - for( auto it = m_lensflares.cbegin(); it != m_lensflares.cend(); ++it ) - { - ( *it )->GetRenderables( currentFrustum, visible ); - } - - if( !visible.empty() ) - { - renderContext.SetReadOnlyDepth( true ); - RenderRenderables( visible, - m_secondaryBatches[TRIBATCHTYPE_ADDITIVE], - TRIBATCHTYPE_ADDITIVE, - Tr2EffectStateManager::RM_ALPHA_ADDITIVE, - renderContext, - Tr2RenderReason::TR2RENDERREASON_REFLECTION ); - renderContext.SetReadOnlyDepth( false ); - } - } - - if( m_dynamicObjectReflectionEnabled && m_reflectionProbe->ReadyForDynamicObjectReflections() ) - { - std::vector visibleRenderables; - visibleRenderables.reserve( m_componentRegistry->ComponentCount() ); - - // Filter out the non-visible reflection renderables based on the current frustum - m_componentRegistry->ProcessComponents( - [&]( ITr2Renderable* renderable ) -> void { - if( renderable->IsVisible( m_updateContext ) ) - { - visibleRenderables.push_back( renderable ); - } - } ); - - bool hasFog = m_volumetricsRenderer && m_volumetricsRenderer->HasFog(); - - ClearBatches( m_secondaryBatches ); - - bool hasInstancedBatches = m_instancedMeshManager->GetBatches( - m_updateContext.GetFrustum(), - m_updateContext.GetInvLodFactor(), - { - { TRIBATCHTYPE_OPAQUE, *m_secondaryBatches[TRIBATCHTYPE_OPAQUE] }, - { TRIBATCHTYPE_DECAL, *m_secondaryBatches[TRIBATCHTYPE_DECAL] }, - { TRIBATCHTYPE_ADDITIVE, *m_secondaryBatches[TRIBATCHTYPE_ADDITIVE] }, - { TRIBATCHTYPE_DEPTH, *m_secondaryBatches[TRIBATCHTYPE_DEPTH] }, - } ) > 0; - - m_instancedMeshManager->ReportUsedScreenSizes(); - - if( hasFog || !visibleRenderables.empty() || hasInstancedBatches ) - { - // clear planets - renderContext.Clear( CLEARFLAGS_ZBUFFER, 0, 0, 0 ); - - // We are rendering in a left hand coordinate system (for cubemaps) so don't override the cullmode!!! - renderContext.m_esm.BeginManagedRendering( CullMode::CULLMODE_NONE ); - - Tr2RenderableSortList transparentObjects; - GetAllBatchesFromRenderables( visibleRenderables, transparentObjects, false, m_secondaryBatches, Tr2RenderReason::TR2RENDERREASON_REFLECTION ); - - PrepareTransparentBatch( transparentObjects, m_secondaryBatches, Tr2RenderReason::TR2RENDERREASON_REFLECTION ); - FinalizeBatches( m_secondaryBatches ); - - renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_DEPTH_ONLY ); - renderContext.RenderBatches( m_secondaryBatches[TRIBATCHTYPE_OPAQUE], BlueSharedString( "Depth" ) ); - renderContext.RenderBatches( m_secondaryBatches[TRIBATCHTYPE_DEPTH], BlueSharedString( "Depth" ) ); - - EveSpaceScene::ShadowResources shadowResources = RenderReflectionShadows( m_reflectionProbe->GetDepthBuffer( i ), gpuResourcePool, renderContext ); - - // TODO: raytraced shadows here - RegisterWithVariableStore( shadowResources, gpuResourcePool ); - - renderContext.SetReadOnlyDepth( true ); - - RenderOpaqueBatches( m_secondaryBatches, renderContext ); - - Tr2GpuResourcePool::Texture froxelFog; - if( m_volumetricsRenderer ) - { - uint32_t width = Tr2Renderer::GetViewport().width; - uint32_t height = Tr2Renderer::GetViewport().height; - Vector3d origin = m_updateContext.GetOrigin(); - froxelFog = m_volumetricsRenderer->RenderFogIntoReflectionMap( renderContext, gpuResourcePool, width, height, m_sunData.DirWorld, m_currentSunColor, origin, Tr2Renderer::GetViewTransform(), Tr2Renderer::GetReversedDepthProjectionTransform() ); - } - GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", froxelFog ); - - RenderTransparentBatches( m_secondaryBatches, renderContext ); - renderContext.SetReadOnlyDepth( false ); - - GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", Tr2TextureAL{} ); - RegisterWithVariableStore( ShadowResources{}, gpuResourcePool ); - - ClearBatches( m_secondaryBatches ); - renderContext.m_esm.EndManagedRendering(); - } - } - } - - m_reflectionProbe->EndRenderPass( renderContext ); - - m_updateContext.SetFrustum( normalFrustum ); - - // reset the reflection intensity - m_currentReflectionIntensity = tmp; - } - - PopulatePerFramePSData( m_perFramePS, renderContext ); - PopulatePerFrameVSData( m_perFrameVS, renderContext ); - ApplyPerFrameData( renderContext ); -} - // -------------------------------------------------------------------------------------- // Description: // Render the background scene with all objects, and generate reflection map diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 70072b195..2cd363cb7 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -146,7 +146,6 @@ BLUE_CLASS( EveSpaceScene ) : void RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName = BlueSharedString( "Depth" ) ); bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); - void RenderReflectionPass( Tr2GpuResourcePool & gpuResourcePool, Tr2RenderContext & renderContext ); void BeginFrame( Tr2RenderContext& renderContext ); void BeginView( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void EndRender( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); diff --git a/trinity/Tr2ReflectionProbe.cpp b/trinity/Tr2ReflectionProbe.cpp index 55ef785e9..fc58a51b9 100644 --- a/trinity/Tr2ReflectionProbe.cpp +++ b/trinity/Tr2ReflectionProbe.cpp @@ -30,7 +30,6 @@ Tr2ReflectionProbe::Tr2ReflectionProbe( IRoot* lockobj ) : m_hasData( false ), m_position( 0, 0, 0 ), m_intermediateSize( FILTER_SIZE * 4 ), - m_prevCullInversion( false ), m_customSourceTexture(), m_renderFrequency( ONE_SIDE_PER_FRAME ), m_currentFrame( 0 ), @@ -70,13 +69,6 @@ bool Tr2ReflectionProbe::HasData() const return m_hasData && m_postFilterTarget->IsValid(); } -TriFrustum Tr2ReflectionProbe::GetFrustum( Tr2RenderContext& renderContext ) -{ - TriFrustum frustum = TriFrustum(); - frustum.DeriveFrustum( &Tr2Renderer::GetViewTransform(), &m_position, &Tr2Renderer::GetProjectionTransform(), renderContext.m_esm.GetViewport() ); - return frustum; -} - uint8_t Tr2ReflectionProbe::GetStartFace() const { if( m_renderFrequency == ALL_SIDES_PER_FRAME || !m_onePassDone ) @@ -105,62 +97,6 @@ bool Tr2ReflectionProbe::IsFirstPass() const return !m_onePassDone; } - -void Tr2ReflectionProbe::InitRenderPass( Tr2RenderContext& renderContext ) -{ - if( !m_lockPosition ) - { - m_position = Tr2Renderer::GetViewPosition(); - } - - renderContext.m_esm.PushViewport(); - Tr2Renderer::PushViewTransform(); - Tr2Renderer::PushProjection(); - renderContext.m_esm.PushRenderTarget(); - renderContext.m_esm.PushDepthStencilBuffer(); - - renderContext.m_esm.SetViewport( m_intermediateSize, m_intermediateSize, 0, 0, 0, 1 ); - - Tr2Renderer::SetProjectionTransform( GetFaceProjection( Tr2Renderer::GetProjectionTransform() ) ); - - // We need to invert cull-mode because of the left-handed coordinates of the cube rendertarget - m_prevCullInversion = renderContext.m_esm.IsCullModeInverted(); - renderContext.m_esm.SetInvertedCullMode( true ); - -#if !TRINITY_PLATFORM_SUPPORTS_RENDER_PASS_HINTS - { - GPU_REGION( renderContext, "Reflection Depth Clear" ); - - for( int i = GetStartFace(); i < GetEndFace(); i++ ) - { - renderContext.SetRenderTarget( *m_renderTargets[i], 0 ); - renderContext.m_esm.SetDepthStencilBuffer( m_stencilMaps[i] ); - CR( renderContext.Clear( CLEARFLAGS_ZBUFFER | CLEARFLAGS_TARGET, 0, 0, 0 ) ); - } - } -#endif -} - -Tr2TextureAL Tr2ReflectionProbe::GetDepthBuffer( unsigned face ) -{ - return m_stencilMaps[face]; -} - -void Tr2ReflectionProbe::StartRenderFace( unsigned face, Tr2RenderContext& renderContext ) -{ - renderContext.RenderPassHint( { Tr2LoadAction::CLEAR, Tr2StoreAction::STORE, 0 }, { Tr2LoadAction::CLEAR, Tr2StoreAction::DONT_CARE, 0.F } ); - - renderContext.m_esm.SetRenderTarget( 0, *m_renderTargets[face] ); - renderContext.m_esm.SetDepthStencilBuffer( m_stencilMaps[face] ); - - Tr2Renderer::SetViewTransform( GetFaceViewTransform( face, m_position ) ); -} - -unsigned Tr2ReflectionProbe::GetFaceSize() const -{ - return static_cast( m_intermediateSize ); -} - Matrix Tr2ReflectionProbe::GetFaceProjection( const Matrix& currentProjection ) const { // Square projection matrix, with near and far clip planes from the current projection @@ -238,28 +174,6 @@ void Tr2ReflectionProbe::FinishFaces( const Vector3& viewDirection, Tr2RenderCon } } -void Tr2ReflectionProbe::EndRenderPass( Tr2RenderContext& renderContext ) -{ - { - GPU_REGION( renderContext, "Reflection RT Copy" ); - - for( int i = GetStartFace(); i < GetEndFace(); i++ ) - { - CR( m_renderTargetCube->GetRenderTarget().CopySubresourceRegion( Tr2TextureSubresource( i, 0 ), *m_renderTargets[i], Tr2TextureSubresource( 0, 0 ), renderContext ) ); - } - } - - renderContext.m_esm.SetInvertedCullMode( m_prevCullInversion ); - - renderContext.m_esm.PopDepthStencilBuffer(); - renderContext.m_esm.PopRenderTarget(); - Tr2Renderer::PopProjection(); - Tr2Renderer::PopViewTransform(); - renderContext.m_esm.PopViewport(); - - FinishFaces( Tr2Renderer::GetInverseViewTransform().GetZ(), renderContext ); -} - Tr2RenderTargetPtr Tr2ReflectionProbe::GetReflection() { return m_postFilterTarget; @@ -306,12 +220,6 @@ bool Tr2ReflectionProbe::DoPrepareResources( ImageIO::PixelFormat rtFormat, Tr2P { CR_RETURN_VAL( m_renderTargets[i]->CreateManual( m_intermediateSize, m_intermediateSize, 1, rtFormat, 0, 0, EX_BIND_UNORDERED_ACCESS, TEX_TYPE_2D, Tr2CpuUsage::NONE, Tr2GpuUsage::RENDER_TARGET ), false ); } - - if( !m_stencilMaps[i].IsValid() ) - { - int stencilSize = ( m_customSourceTexture && m_customSourceTexture->IsValid() ) ? m_customSourceTexture->GetWidth() : m_intermediateSize; - CR_RETURN_VAL( m_stencilMaps[i].Create( Tr2BitmapDimensions( stencilSize, stencilSize, 1, PIXEL_FORMAT_D32_FLOAT ), Tr2GpuUsage::DEPTH_STENCIL | Tr2GpuUsage::SHADER_RESOURCE, renderContext ), false ); - } } if( !m_renderTargetCube->IsValid() ) @@ -390,7 +298,6 @@ void Tr2ReflectionProbe::DestroyRenderTargets() for( unsigned i = 0; i < 6; i++ ) { m_renderTargets[i]->Destroy(); - m_stencilMaps[i] = Tr2TextureAL(); } m_renderTargetCube->Destroy(); diff --git a/trinity/Tr2ReflectionProbe.h b/trinity/Tr2ReflectionProbe.h index d7c522e17..daa8482c6 100644 --- a/trinity/Tr2ReflectionProbe.h +++ b/trinity/Tr2ReflectionProbe.h @@ -33,13 +33,8 @@ BLUE_CLASS( Tr2ReflectionProbe ) : bool IsValid(); bool HasData() const; - void InitRenderPass( Tr2RenderContext & renderContext ); - void StartRenderFace( unsigned face, Tr2RenderContext& renderContext ); - void EndRenderPass( Tr2RenderContext & renderContext ); Tr2TextureAL GetDepthBuffer( unsigned face ); - TriFrustum GetFrustum( Tr2RenderContext& renderContext ); - Tr2RenderTargetPtr GetReflection(); void SetPosition( Vector3 position ); @@ -58,14 +53,6 @@ BLUE_CLASS( Tr2ReflectionProbe ) : // True until the first FinishFaces: until then every face is rendered, whatever the render frequency. bool IsFirstPass() const; - const Tr2TextureAL& GetDepthBuffer( unsigned face ) const - { - return m_stencilMaps[face]; - } - - // External path: faces are rendered elsewhere (e.g. render driver) and submitted here. - // Source textures must match GetFaceSize() and the probe's intermediate format. - unsigned GetFaceSize() const; Matrix GetFaceViewTransform( unsigned face, const Vector3& position ) const; Matrix GetFaceProjection( const Matrix& currentProjection ) const; void SubmitFace( unsigned face, const Tr2TextureAL& source, Tr2RenderContext& renderContext ); @@ -88,7 +75,6 @@ BLUE_CLASS( Tr2ReflectionProbe ) : int m_intermediateSize; std::array m_renderTargets; - std::array m_stencilMaps; Tr2RenderTargetPtr m_renderTargetCube; Tr2EffectPtr m_preFilterEffect; @@ -99,7 +85,6 @@ BLUE_CLASS( Tr2ReflectionProbe ) : ITriTextureResPtr m_customSourceTexture; - bool m_prevCullInversion; bool m_hdrOutput; ReflectionProbeRenderFrequency m_renderFrequency; uint8_t m_currentFrame; From 0f8efd43f72824cc5089715b2c88b4d13fe43ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Tue, 27 Oct 2026 16:30:04 +0000 Subject: [PATCH 10/20] Reflection improvements - Faces render into the cube's slices; the six face targets and the copy are gone. The cube is unbound before filtering (DX12 fix). - The probe's first pass renders all faces as normal faces; removed Features::ReflectionBackground. - Tr2LightManager::SetViews replaces the per-view count and frustum calls; a view's index is its position in the list. --- trinity/Eve/EveSpaceScene.cpp | 2 +- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 106 +++++++--------------- trinity/Eve/EveSpaceSceneRenderDriver.h | 11 +-- trinity/Tr2LightManager.cpp | 52 +++++------ trinity/Tr2LightManager.h | 35 ++++--- trinity/Tr2ReflectionProbe.cpp | 53 +---------- trinity/Tr2ReflectionProbe.h | 15 +-- 7 files changed, 85 insertions(+), 189 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index 7212d7cb4..c817efa9c 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1433,7 +1433,7 @@ void EveSpaceScene::ApplyViewLighting( const EveSceneView::ViewInfo& view ) // -------------------------------------------------------------------------------------- // Description: // Gather the dynamic lights visible in the frustum of each of the light manager's views. -// The caller sets the frustums (SetViewFrustum) and calls this once per frame, after the +// The caller sets the views (SetViews) and calls this once per frame, after the // main view's update and BeginFrame and before the first view is rendered. // -------------------------------------------------------------------------------------- void EveSpaceScene::GatherDynamicLights( Tr2RenderContext& renderContext ) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 982b13b4e..638a8272c 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -24,18 +24,18 @@ extern bool g_lensflaresInReflections; namespace { -void BeginRenderPass( Tr2RenderContext& renderContext, const std::initializer_list& colorAttachments, const Tr2TextureAL& depthAttachment ) +void BeginRenderPass( Tr2RenderContext& renderContext, const std::initializer_list>& colorAttachments, const Tr2TextureAL& depthAttachment ) { uint32_t index = 0; for( auto& colorAttachment : colorAttachments ) { - renderContext.m_esm.SetRenderTarget( index++, colorAttachment ); + renderContext.m_esm.SetRenderTarget( index++, colorAttachment.first, true, colorAttachment.second ); } // clear out remaining slots, generously assuming max 4 render targets const uint32_t maxRenderTargets = 4; for( ; index < maxRenderTargets; ++index ) { - renderContext.m_esm.SetRenderTarget( index++, Tr2TextureAL{} ); + renderContext.m_esm.SetRenderTarget( index, Tr2TextureAL{} ); } renderContext.m_esm.SetDepthStencilBuffer( depthAttachment ); renderContext.m_esm.SetFullScreenViewport(); @@ -64,7 +64,7 @@ void ApplyDistortion( const Tr2TextureAL& destination, const Tr2TextureAL& disto auto backBufferCopy = gpuResourcePool.GetTempTexture( "backBufferCopy", destination.GetWidth(), destination.GetHeight(), destination.GetFormat(), Tr2GpuUsage::COPY_DESTINATION | Tr2GpuUsage::SHADER_RESOURCE ); backBufferCopy->CopySubresourceRegion( {}, destination, {}, renderContext ); - BeginRenderPass( renderContext, { destination }, {} ); + BeginRenderPass( renderContext, { { destination, 0 } }, {} ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_FULLSCREEN ); effect->SetParameter( BlueSharedString( "BlitCurrent" ), backBufferCopy ); effect->SetParameter( BlueSharedString( "TexDistortion" ), distortion ); @@ -355,21 +355,6 @@ EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::Reflect return features; } -EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::ReflectionBackground() -{ - // the probe's first pass: nothing but the background - Features features; - features.upscaling = false; - features.antiAliasing = false; - features.ssao = false; - features.shadows = false; - features.velocityMap = false; - features.background = true; - features.overlay = false; - features.dynamicLights = false; - return features; -} - // -------------------------------------------------------------------------------------- // Description: // Set the camera's view transform to the renderer and the camera to the scene, which @@ -573,7 +558,7 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina auto prevVisualizeMethod = m_scene->m_visualizeMethod; m_scene->m_visualizeMethod = m_settings.visualizeMethod; ON_BLOCK_EXIT( [&] { m_scene->m_visualizeMethod = prevVisualizeMethod; } ); - BeginRenderPass( renderContext, { colorTarget }, {} ); + BeginRenderPass( renderContext, { { colorTarget, 0 } }, {} ); UpdateSceneFrame( mainView.camera, realTime, simTime, rootTimer, renderContext ); ReflectionFaces reflectionFaces; @@ -583,21 +568,9 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina m_scene->BeginFrame( renderContext ); reflectionFaces = PrepareReflectionFaces( renderContext ); - UpdateLightManagerFaceViews( reflectionFaces ); // the lights are gathered once, for all views - if( auto lightManager = Tr2LightManager::GetInstance() ) - { - lightManager->SetViewFrustum( static_cast( mainView.lightView ), mainView.camera.frustum ); - - for( const RenderInfo& faceView : reflectionFaces.views ) - { - if( faceView.features.dynamicLights ) - { - lightManager->SetViewFrustum( static_cast( faceView.lightView ), faceView.camera.frustum ); - } - } - } + SetLightViews( mainView, reflectionFaces, renderContext ); m_scene->GatherDynamicLights( renderContext ); } { @@ -684,10 +657,9 @@ float EveSpaceSceneRenderDriver::GetMipLodBias() const // -------------------------------------------------------------------------------------- // Description: // Render the scene from one view (the main view or a reflection probe face) into -// colorTarget. Returns the targets the view rendered, for post processing or for -// submitting to the reflection probe. +// colorTarget. Returns the targets the view rendered, for post processing // -------------------------------------------------------------------------------------- -EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) +EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext, const uint32_t colorTargetSlice ) { const TextureSize2D& renderSize = view.camera.renderSize; @@ -700,7 +672,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co { if( auto lightManager = Tr2LightManager::GetInstance() ) { - lightManager->SetActiveView( static_cast( view.lightView ) ); + lightManager->SetActiveView( view.lightView ); } } @@ -708,7 +680,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co if( renderBackgroundNode ) { // render background effect to back buffer before rendering the scene - BeginRenderPass( renderContext, { backBuffer }, {} ); + BeginRenderPass( renderContext, { { backBuffer, colorTargetSlice } }, {} ); renderContext.Clear( Tr2RenderContextEnum::CLEARFLAGS_TARGET, m_settings.clearColor, 0 ); m_background->Execute( { &backBuffer, 1 }, {}, realTime, simTime, rootTimer, renderContext ); @@ -717,7 +689,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co targets.depth = m_gpuResourcePool.GetTempTexture( "depthBuffer", renderSize, ImageIO::PIXEL_FORMAT_D32_FLOAT, Tr2GpuUsage::DEPTH_STENCIL | Tr2GpuUsage::SHADER_RESOURCE ); const Tr2TextureAL& depthBuffer = targets.depth; - BeginRenderPass( renderContext, { backBuffer }, depthBuffer ); + BeginRenderPass( renderContext, { { backBuffer, colorTargetSlice } }, depthBuffer ); renderContext.Clear( renderBackgroundNode ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); { @@ -806,7 +778,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co if( targets.opaqueColor.IsValid() ) { renderContext.RenderPassHint( { Tr2LoadAction::DONT_CARE, Tr2StoreAction::STORE }, {} ); - BeginRenderPass( renderContext, { targets.opaqueColor.Get() }, {} ); + BeginRenderPass( renderContext, { { targets.opaqueColor.Get(), 0 } }, {} ); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_FULLSCREEN ); Tr2Renderer::DrawTexture( renderContext, backBuffer ); } @@ -819,7 +791,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co GlobalStore().RegisterVariable( "SpaceSceneNormalMap", Tr2TextureAL{} ); normalMap = {}; - BeginRenderPass( renderContext, { backBuffer }, depthBuffer ); + BeginRenderPass( renderContext, { { backBuffer, colorTargetSlice } }, depthBuffer ); { renderContext.SetReadOnlyDepth( true ); m_scene->RunLensflareOcclusionQueries( depthBuffer, renderContext ); @@ -852,27 +824,17 @@ EveSpaceSceneRenderDriver::ReflectionFaces EveSpaceSceneRenderDriver::PrepareRef SetCameraToRenderer( renderContext ); probe->UpdatePosition( Tr2Renderer::GetViewPosition() ); const Matrix faceProjection = probe->GetFaceProjection( Tr2Renderer::GetProjectionTransform() ); - const TextureSize2D faceRenderSize = probe->GetFaceTarget( 0 ).GetDesc(); + const TextureSize2D faceRenderSize = probe->GetCubeTarget().GetDesc(); faces.probe = probe; - faces.firstPass = probe->IsFirstPass(); faces.startFace = probe->GetStartFace(); faces.viewDirection = Tr2Renderer::GetInverseViewTransform().GetZ(); const bool lensflares = g_lensflaresInReflections && m_settings.reflectionQuality == ReflectionQuality::ULTRA; for( unsigned face = probe->GetStartFace(); face < probe->GetEndFace(); ++face ) { - const EveSceneView::Camera camera = EveSceneView::Camera( probe->GetFaceViewTransform( face, probe->GetPosition() ), faceProjection, faceRenderSize ); - if( faces.firstPass ) - { - // the probe's first pass draws only the background: no lens flares and no light view - faces.views.push_back( { camera, EveSceneView::ViewInfo::ReflectionFace( false ), Features::ReflectionBackground() } ); - } - else - { - // with one side per frame, the rendered face always uses the first face light view - faces.views.push_back( { camera, EveSceneView::ViewInfo::ReflectionFace( lensflares ), Features::ReflectionFace(), 1 + ( face - faces.startFace ) } ); - } + const EveSceneView::Camera camera = EveSceneView::Camera( probe->GetFaceViewTransform( face ), faceProjection, faceRenderSize ); + faces.views.push_back( { camera, EveSceneView::ViewInfo::ReflectionFace( lensflares ), Features::ReflectionFace() } ); } return faces; @@ -880,12 +842,10 @@ EveSpaceSceneRenderDriver::ReflectionFaces EveSpaceSceneRenderDriver::PrepareRef // -------------------------------------------------------------------------------------- // Description: -// The light manager has one view per face rendered in a frame: one with one side per -// frame and six with all sides per frame. The views -// are only recreated when that number changes, which is when the probe's render frequency -// changes. +// Give the main view and every reflection face that has dynamic lights a view in the +// light manager, in that order. A view's lightView is its position in that list. // -------------------------------------------------------------------------------------- -void EveSpaceSceneRenderDriver::UpdateLightManagerFaceViews( const ReflectionFaces& faces ) +void EveSpaceSceneRenderDriver::SetLightViews( const RenderInfo& mainView, ReflectionFaces& faces, Tr2RenderContext& renderContext ) { auto lightManager = Tr2LightManager::GetInstance(); if( !lightManager ) @@ -893,20 +853,17 @@ void EveSpaceSceneRenderDriver::UpdateLightManagerFaceViews( const ReflectionFac return; } - uint32_t faceViewCount = 0; - if( faces.probe && !faces.firstPass ) + // the main view is first, matching its lightView of 0 + std::vector frustums = { mainView.camera.frustum }; + for( RenderInfo& faceView : faces.views ) { - faceViewCount = faces.probe->GetRenderFrequency() == Tr2ReflectionProbe::ALL_SIDES_PER_FRAME ? 6 : 1; - } - - if( lightManager->GetReflectionViewCount() != faceViewCount ) - { - lightManager->ClearReflectionViews(); - for( uint32_t i = 0; i < faceViewCount; ++i ) + if( faceView.features.dynamicLights ) { - lightManager->AddReflectionView(); + faceView.lightView = static_cast( frustums.size() ); + frustums.push_back( faceView.camera.frustum ); } } + lightManager->SetViews( frustums, renderContext ); } void EveSpaceSceneRenderDriver::RenderReflectionFaces( const ReflectionFaces& faces, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ) @@ -923,20 +880,19 @@ void EveSpaceSceneRenderDriver::RenderReflectionFaces( const ReflectionFaces& fa renderContext.m_esm.SetInvertedCullMode( true ); ON_BLOCK_EXIT( [&] { renderContext.m_esm.SetInvertedCullMode( prevCullInversion ); } ); + const Tr2TextureAL& faceTarget = probe->GetCubeTarget(); unsigned face = faces.startFace; for( const RenderInfo& faceView : faces.views ) { std::string regionName = "Reflection Face " + std::to_string( face ); GPU_REGION( renderContext, regionName.c_str() ); - // render straight into the probe's face target, then copy it into the cubemap - const Tr2TextureAL& faceTarget = probe->GetFaceTarget( face ); - RenderView( faceView, faceTarget, {}, realTime, simTime, rootTimer, renderContext ); - - probe->SubmitFace( face, faceTarget, renderContext ); - ++face; + // render straight into the probe's face + RenderView( faceView, faceTarget, {}, realTime, simTime, rootTimer, renderContext, face++ ); } + // unbind the probe cube so it can be used as a shader resource in the main view + renderContext.m_esm.SetRenderTarget( 0, Tr2TextureAL{} ); probe->FinishFaces( faces.viewDirection, renderContext ); SetCameraToRenderer( renderContext ); } diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 747b59929..6efb36ba8 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -107,8 +107,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : static Features Main(); static Features ReflectionFace(); - // the probe's first pass, which only draws the background into the faces - static Features ReflectionBackground(); }; // A view rendered by RenderView: its camera and what the scene renders from it, the @@ -118,7 +116,8 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : EveSceneView::Camera camera; EveSceneView::ViewInfo scene; Features features; - // 0 is the main view, 1.. are the reflection probe faces rendered in the frame + // the view's index in the Tr2LightManager, set by SetLightViews: 0 is the main view, + // then the reflection faces that have dynamic lights uint32_t lightView = 0; }; @@ -134,16 +133,14 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : struct ReflectionFaces { Tr2ReflectionProbe* probe = nullptr; - // the probe's first pass only draws the background into the faces - bool firstPass = false; unsigned startFace = 0; Vector3 viewDirection = Vector3( 0.0f, 0.0f, 0.0f ); std::vector views; }; - ViewTargets RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); + ViewTargets RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext, const uint32_t colorTargetSlice = 0 ); ReflectionFaces PrepareReflectionFaces( Tr2RenderContext& renderContext ); - void UpdateLightManagerFaceViews( const ReflectionFaces& faces ); + void SetLightViews( const RenderInfo& mainView, ReflectionFaces& faces, Tr2RenderContext& renderContext ); void RenderReflectionFaces( const ReflectionFaces& faces, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetupUpscaling( const TextureSize2D& displaySize ); void PropagateSettings(); diff --git a/trinity/Tr2LightManager.cpp b/trinity/Tr2LightManager.cpp index a427b0e93..178f006d6 100644 --- a/trinity/Tr2LightManager.cpp +++ b/trinity/Tr2LightManager.cpp @@ -250,49 +250,41 @@ void Tr2LightManager::CreateViewBuffers( View& view, Tr2RenderContext& renderCon ClearLightIndices( view, renderContext ); } -void Tr2LightManager::ClearReflectionViews() +void Tr2LightManager::SetViews( const std::vector& frustums, Tr2RenderContext& renderContext ) { - while( m_views.size() > MAIN_VIEW + 1 ) + if( frustums.empty() ) { - m_views.pop_back(); + CCP_LOGERR( "Tr2LightManager: SetViews needs at least the main view" ); + return; } - if( m_activeView != MAIN_VIEW ) + if( m_views.size() > frustums.size() ) { - SetActiveView( MAIN_VIEW ); + m_views.erase( m_views.begin() + frustums.size(), m_views.end() ); } -} - -uint8_t Tr2LightManager::AddReflectionView() -{ - m_views.emplace_back( MAX_NUM_VOLUMETRIC_LIGHTS_IN_REFLECTIONS, 0u, INDEX_BUFFER_SIZE_IN_REFLECTIONS, true ); - - if( Tr2Renderer::IsResourceCreationAllowed() ) + while( m_views.size() < frustums.size() ) { - USE_MAIN_THREAD_RENDER_CONTEXT(); - CreateViewBuffers( m_views.back(), renderContext ); + m_views.emplace_back( MAX_NUM_VOLUMETRIC_LIGHTS_IN_REFLECTIONS, 0u, INDEX_BUFFER_SIZE_IN_REFLECTIONS, true ); + // during a device reset OnPrepareResources creates the buffers later + if( Tr2Renderer::IsResourceCreationAllowed() ) + { + CreateViewBuffers( m_views.back(), renderContext ); + } } - return static_cast( m_views.size() - 1 ); -} - -uint32_t Tr2LightManager::GetReflectionViewCount() const -{ - return static_cast( m_views.size() - ( MAIN_VIEW + 1 ) ); -} - -void Tr2LightManager::SetViewFrustum( uint8_t viewIndex, const TriFrustum& frustum ) -{ - if( viewIndex >= m_views.size() ) + auto frustum = frustums.begin(); + for( auto& view : m_views ) { - CCP_LOGERR( "Tr2LightManager: invalid view index %u", viewIndex ); - return; + view.frustum = *frustum++; } - m_views[viewIndex].frustum = frustum; + if( m_activeView >= m_views.size() ) + { + SetActiveView( MAIN_VIEW ); + } } -void Tr2LightManager::SetActiveView( uint8_t viewIndex ) +void Tr2LightManager::SetActiveView( uint32_t viewIndex ) { if( viewIndex >= m_views.size() ) { @@ -308,7 +300,7 @@ void Tr2LightManager::SetActiveView( uint8_t viewIndex ) void Tr2LightManager::Clear( Tr2RenderContext& renderContext ) { - for( auto& view: m_views ) + for( auto& view : m_views ) { ClearLightIndices( view, renderContext ); for( auto& data : view.tlsLightData ) diff --git a/trinity/Tr2LightManager.h b/trinity/Tr2LightManager.h index bf42cbf52..d4f9d2176 100644 --- a/trinity/Tr2LightManager.h +++ b/trinity/Tr2LightManager.h @@ -57,16 +57,20 @@ struct Tr2LightTags // Description: // Manages light sources for tiled/clustered lighting. An object of this class does not // store lights permanently. Rather a user adds lights every frame and then the object -// updates GPU buffers needed for tiled lighting. Normally the per-frame operations -// for the light manager would look like: -// lightManager.Clear(); -// lightManager.SetFrustum( frustum ); +// updates GPU buffers needed for tiled lighting. The lights are gathered once per frame +// for every view (the main view and the reflection probe faces), and each view has its +// own lights and buffers. Normally the per-frame operations for the light manager would +// look like: +// lightManager.SetViews( frustums, renderContext ); // the main view first +// lightManager.Clear( renderContext ); // for each in scene.lights -// lightManager.AddPointLight( each ); // this can be done in parallel -// lightManager.ResolveLightData(); -// lightManager.UpdateLists(); -// renderYourStuff(); -// Light manager GPU buffers are accessed through variable store with names +// lightManager.AddLight( each ); // this can be done in parallel +// lightManager.ResolveLightData(); +// for each view +// lightManager.SetActiveView( position of the view in frustums ); +// lightManager.UpdateLists( depthMap, renderContext ); +// renderYourStuff(); +// The active view's GPU buffers are accessed through variable store with names // "LightBuffer" and "LightIndexBuffer". // -------------------------------------------------------------------------------------- class Tr2LightManager : public Tr2DeviceResource @@ -134,11 +138,12 @@ class Tr2LightManager : public Tr2DeviceResource void Clear( Tr2RenderContext& renderContext ); - void ClearReflectionViews(); - uint8_t AddReflectionView(); - uint32_t GetReflectionViewCount() const; - void SetViewFrustum( uint8_t viewIndex, const TriFrustum& frustum ); - void SetActiveView( uint8_t viewIndex ); + // The views the lights are gathered for this frame, set before the gather. The first + // frustum is the main view's, the others are reflection probe faces. A view's index for + // SetActiveView is its position in frustums. Views are created when the list grows and + // released when it shrinks. + void SetViews( const std::vector& frustums, Tr2RenderContext& renderContext ); + void SetActiveView( uint32_t viewIndex ); void AddPointLight( const Vector3& position, float radius, const Color& color, Float_16 innerRadius = Float_16( 0.f ), uint16_t flags = FLAG_DEFAULT, bool scaleBrightness = true ); void AddLight( PerLightData& data, bool scaleBrightness = true, const Tr2LightTags& tags = Tr2LightTags() ); @@ -239,7 +244,7 @@ class Tr2LightManager : public Tr2DeviceResource bool GetShadowMapAtlasEntry( uint32_t lightIndex, uint32_t width, uint32_t height, uint32_t& out_posX, uint32_t& out_posY ); void CreateShadowMapAtlas( std::vector& lightData, uint32_t numShadowCastingLights, const std::vector& lightTuples ); - static const uint32_t MAIN_VIEW = 0; // faces are 1..6 + static const uint32_t MAIN_VIEW = 0; // the reflection probe faces follow it std::vector m_views; uint32_t m_activeView = MAIN_VIEW; diff --git a/trinity/Tr2ReflectionProbe.cpp b/trinity/Tr2ReflectionProbe.cpp index fc58a51b9..27be1841f 100644 --- a/trinity/Tr2ReflectionProbe.cpp +++ b/trinity/Tr2ReflectionProbe.cpp @@ -40,11 +40,6 @@ Tr2ReflectionProbe::Tr2ReflectionProbe( IRoot* lockobj ) : m_backlightColor( 1, 1, 1, 1 ), m_backlightContrast( 16 ) { - for( unsigned i = 0; i < 6; i++ ) - { - m_renderTargets[i].CreateInstance(); - } - m_renderTargetCube.CreateInstance(); m_preFilterEffect.CreateInstance(); m_copyMipEffect.CreateInstance(); @@ -87,16 +82,6 @@ uint8_t Tr2ReflectionProbe::GetEndFace() const return m_currentFrame + 1; } -Tr2ReflectionProbe::ReflectionProbeRenderFrequency Tr2ReflectionProbe::GetRenderFrequency() const -{ - return m_renderFrequency; -} - -bool Tr2ReflectionProbe::IsFirstPass() const -{ - return !m_onePassDone; -} - Matrix Tr2ReflectionProbe::GetFaceProjection( const Matrix& currentProjection ) const { // Square projection matrix, with near and far clip planes from the current projection @@ -108,7 +93,7 @@ Matrix Tr2ReflectionProbe::GetFaceProjection( const Matrix& currentProjection ) return newProjection; } -Matrix Tr2ReflectionProbe::GetFaceViewTransform( unsigned face, const Vector3& position ) const +Matrix Tr2ReflectionProbe::GetFaceViewTransform( unsigned face ) const { static const Vector3 faceDirections[] = { Vector3( 0, 1, 0 ), Vector3( 1, 0, 0 ), //+X @@ -130,18 +115,7 @@ Matrix Tr2ReflectionProbe::GetFaceViewTransform( unsigned face, const Vector3& p const Vector3& up = faceDirections[face * 2]; const Vector3& at = faceDirections[face * 2 + 1]; - return LookAtMatrix( position, position + at, up ) * xInverter; -} - -void Tr2ReflectionProbe::SubmitFace( unsigned face, const Tr2TextureAL& source, Tr2RenderContext& renderContext ) -{ - if( face >= 6 || !source.IsValid() || !IsValid() ) - { - return; - } - - GPU_REGION( renderContext, "Reflection External Face Copy" ); - CR( m_renderTargetCube->GetRenderTarget().CopySubresourceRegion( Tr2TextureSubresource( face, 0 ), source, Tr2TextureSubresource( 0, 0 ), renderContext ) ); + return LookAtMatrix( m_position, m_position + at, up ) * xInverter; } void Tr2ReflectionProbe::UpdatePosition( const Vector3& viewPosition ) @@ -152,14 +126,9 @@ void Tr2ReflectionProbe::UpdatePosition( const Vector3& viewPosition ) } } -const Vector3& Tr2ReflectionProbe::GetPosition() const +const Tr2TextureAL& Tr2ReflectionProbe::GetCubeTarget() const { - return m_position; -} - -const Tr2TextureAL& Tr2ReflectionProbe::GetFaceTarget( unsigned face ) const -{ - return m_renderTargets[face]->GetRenderTarget(); + return m_renderTargetCube->GetRenderTarget(); } void Tr2ReflectionProbe::FinishFaces( const Vector3& viewDirection, Tr2RenderContext& renderContext ) @@ -213,15 +182,6 @@ bool Tr2ReflectionProbe::OnPrepareResources() bool Tr2ReflectionProbe::DoPrepareResources( ImageIO::PixelFormat rtFormat, Tr2PrimaryRenderContext& renderContext ) { - - for( int i = 0; i < 6; i++ ) - { - if( !m_renderTargets[i]->IsValid() ) - { - CR_RETURN_VAL( m_renderTargets[i]->CreateManual( m_intermediateSize, m_intermediateSize, 1, rtFormat, 0, 0, EX_BIND_UNORDERED_ACCESS, TEX_TYPE_2D, Tr2CpuUsage::NONE, Tr2GpuUsage::RENDER_TARGET ), false ); - } - } - if( !m_renderTargetCube->IsValid() ) { CR_RETURN_VAL( m_renderTargetCube->Create( m_intermediateSize, m_intermediateSize, 1, rtFormat, 0, 0, EX_BIND_UNORDERED_ACCESS, TEX_TYPE_CUBE ), false ); @@ -295,11 +255,6 @@ bool Tr2ReflectionProbe::OnModified( Be::Var* value ) void Tr2ReflectionProbe::DestroyRenderTargets() { - for( unsigned i = 0; i < 6; i++ ) - { - m_renderTargets[i]->Destroy(); - } - m_renderTargetCube->Destroy(); m_preFilterTarget->Destroy(); m_postFilterTarget->Destroy(); diff --git a/trinity/Tr2ReflectionProbe.h b/trinity/Tr2ReflectionProbe.h index daa8482c6..f673084dd 100644 --- a/trinity/Tr2ReflectionProbe.h +++ b/trinity/Tr2ReflectionProbe.h @@ -3,8 +3,6 @@ #pragma once #include "Tr2DeviceResource.h" -#include -#include "TriFrustum.h" BLUE_DECLARE( Tr2Effect ); BLUE_DECLARE( Tr2TextureReference ); @@ -33,7 +31,6 @@ BLUE_CLASS( Tr2ReflectionProbe ) : bool IsValid(); bool HasData() const; - Tr2TextureAL GetDepthBuffer( unsigned face ); Tr2RenderTargetPtr GetReflection(); void SetPosition( Vector3 position ); @@ -49,17 +46,12 @@ BLUE_CLASS( Tr2ReflectionProbe ) : uint8_t GetStartFace() const; uint8_t GetEndFace() const; - ReflectionProbeRenderFrequency GetRenderFrequency() const; - // True until the first FinishFaces: until then every face is rendered, whatever the render frequency. - bool IsFirstPass() const; - Matrix GetFaceViewTransform( unsigned face, const Vector3& position ) const; + Matrix GetFaceViewTransform( unsigned face ) const; Matrix GetFaceProjection( const Matrix& currentProjection ) const; - void SubmitFace( unsigned face, const Tr2TextureAL& source, Tr2RenderContext& renderContext ); void UpdatePosition( const Vector3& viewPosition ); - const Vector3& GetPosition() const; - const Tr2TextureAL& GetFaceTarget( unsigned face ) const; - // Filters the rendered faces and advances to the next faces. Called by both paths. + const Tr2TextureAL& GetCubeTarget() const; + // Filters the rendered faces and advances to the next faces. void FinishFaces( const Vector3& viewDirection, Tr2RenderContext& renderContext ); private: @@ -74,7 +66,6 @@ BLUE_CLASS( Tr2ReflectionProbe ) : Vector3 m_position; int m_intermediateSize; - std::array m_renderTargets; Tr2RenderTargetPtr m_renderTargetCube; Tr2EffectPtr m_preFilterEffect; From 200f7e453e05cc416ebcf22c4aec64b0dac87116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Wed, 28 Oct 2026 10:46:20 +0000 Subject: [PATCH 11/20] Fixing small issues --- trinity/Eve/EveSceneView.h | 2 +- trinity/Eve/EveSpaceScene.cpp | 67 ++++++++++------------- trinity/Eve/EveSpaceScene.h | 6 +- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 9 +-- trinity/Eve/EveSpaceSceneRenderDriver.h | 2 +- trinity/Tr2LightManager.cpp | 1 - 6 files changed, 36 insertions(+), 51 deletions(-) diff --git a/trinity/Eve/EveSceneView.h b/trinity/Eve/EveSceneView.h index 57220acf4..10ba956d8 100644 --- a/trinity/Eve/EveSceneView.h +++ b/trinity/Eve/EveSceneView.h @@ -8,7 +8,7 @@ namespace EveSceneView { -// Where a view looks from. Create derives the frustum from the other fields, so build a +// Where a view looks from. Constructor derives the frustum from the other fields, so build a // new Camera rather than changing one. struct Camera { diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index c817efa9c..cf54b87a1 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1270,6 +1270,21 @@ bool EveSpaceScene::RenderDistortionBatches( BatchMap& batches, const Tr2Texture return true; } +// -------------------------------------------------------------------------------------- +// Description: +// Lower the reflection intensity for the objects rendered into the reflection (so the +// reflections in the reflections don't get brighter and brighter). We cap it at 0.8 +// for no good reason, just felt like a good number to cap it at. +// -------------------------------------------------------------------------------------- +static float GetReflectionIntensityInReflections( float reflectionIntensity ) +{ + if( reflectionIntensity == 0.0f ) + { + return reflectionIntensity; + } + return min( 0.8f, 1.0f / ( reflectionIntensity * reflectionIntensity ) ); +} + // -------------------------------------------------------------------------------------- // Description: // Work that does not depend on the view being rendered. Called once per frame, after @@ -1326,8 +1341,7 @@ void EveSpaceScene::BeginView( const EveSceneView::ViewInfo& view, Tr2RenderCont renderContext.AddGpuMarker( __FUNCTION__ ); m_velocityMapDirty = false; - - ApplyViewLighting( view ); + m_currentReflectionIntensity = view.IsReflection() ? GetReflectionIntensityInReflections( m_baseReflectionIntensity ) : m_baseReflectionIntensity; // Set for every view rather than set and reset in pairs, so a view never inherits // the previous view's value. It is read whenever the per-frame data is populated. @@ -1368,8 +1382,8 @@ void EveSpaceScene::EnsureVisualizerEffect() // -------------------------------------------------------------------------------------- // Description: -// Blend the lighting overrides into the per-frame base values. Views derive their -// m_current* values from these in ApplyViewLighting. +// Blend the lighting overrides into the frame's sun color and nebula intensity, and +// into the base reflection intensity that BeginView derives each view's from. // -------------------------------------------------------------------------------------- void EveSpaceScene::ResolveLightingOverrides() { @@ -1399,37 +1413,11 @@ void EveSpaceScene::ResolveLightingOverrides() overrides.push_back( baseline ); auto over = SimplePriorityBlend( overrides ); - m_baseSunColor = over.sunColor * over.sunIntensity; - m_baseNebulaIntensity = over.backgroundIntensity; + m_currentSunColor = over.sunColor * over.sunIntensity; + m_currentNebulaIntensity = over.backgroundIntensity; m_baseReflectionIntensity = over.reflectionIntensity; } -// -------------------------------------------------------------------------------------- -// Description: -// Lower the reflection intensity for the objects rendered into the reflection (so the -// reflections in the reflections don't get brighter and brighter). We cap it at 0.8 -// for no good reason, just felt like a good number to cap it at. -// -------------------------------------------------------------------------------------- -static float GetReflectionIntensityInReflections( float reflectionIntensity ) -{ - if( reflectionIntensity == 0.0f ) - { - return reflectionIntensity; - } - return min( 0.8f, 1.0f / ( reflectionIntensity * reflectionIntensity ) ); -} - -// -------------------------------------------------------------------------------------- -// Description: -// Derive the lighting values for the current view from the per-frame base values. -// -------------------------------------------------------------------------------------- -void EveSpaceScene::ApplyViewLighting( const EveSceneView::ViewInfo& view ) -{ - m_currentSunColor = m_baseSunColor; - m_currentNebulaIntensity = m_baseNebulaIntensity; - m_currentReflectionIntensity = view.IsReflection() ? GetReflectionIntensityInReflections( m_baseReflectionIntensity ) : m_baseReflectionIntensity; -} - // -------------------------------------------------------------------------------------- // Description: // Gather the dynamic lights visible in the frustum of each of the light manager's views. @@ -2645,18 +2633,19 @@ bool EveSpaceScene::RenderMainPass( PopulateAndApplyPerFrameData( renderContext ); std::pair froxelFogTextures; - if( view.reason == TR2RENDERREASON_NORMAL ) + switch( view.reason ) { + case TR2RENDERREASON_NORMAL: froxelFogTextures = RenderVolumetrics( depthMap, gpuResourcePool, renderContext ); - } - else if( view.reason == TR2RENDERREASON_REFLECTION ) - { + break; + case TR2RENDERREASON_REFLECTION: froxelFogTextures = RenderReflectionVolumetrics( depthMap, gpuResourcePool, renderContext ); - } - else - { + break; + case TR2RENDERREASON_VOLUMETRIC: froxelFogTextures = { Tr2VolumetricsRenderer::GetEmptyFogTexture( gpuResourcePool ), Tr2VolumetricsRenderer::GetEmptyVolumetricTexture( gpuResourcePool ) }; + break; } + GlobalStore().RegisterVariable( "EveSceneFroxelFogMap", froxelFogTextures.first ); GlobalStore().RegisterVariable( "EveSceneFogVolumeMap", froxelFogTextures.second ); diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 2cd363cb7..8b73b64ff 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -495,10 +495,7 @@ BLUE_CLASS( EveSpaceScene ) : Color m_currentSunColor; float m_currentNebulaIntensity; - // Lighting resolved once per frame in ResolveLightingOverrides. The m_current* values - // are derived from these per view in ApplyViewLighting. - Color m_baseSunColor = Color( 1.0f, 1.0f, 1.0f, 1.0f ); - float m_baseNebulaIntensity = 1.0f; + // Base value for the reflection intensity, derived from the lighting overrides and used to derive the per-view values in BeginView. float m_baseReflectionIntensity = 1.0f; float m_fogStart; // Depth at which fogging starts @@ -609,7 +606,6 @@ BLUE_CLASS( EveSpaceScene ) : void UpdateFrame( Be::Time realTime, Be::Time simTime ); void EnsureVisualizerEffect(); void ResolveLightingOverrides(); - void ApplyViewLighting( const EveSceneView::ViewInfo& view ); void GatherDynamicLights( Tr2RenderContext& renderContext ); void PrepareLensflares(); void GatherSceneObjectRenderables( std::vector& allObjects, std::vector& renderables, Tr2RenderContext& renderContext ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 638a8272c..d243b1fe2 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -581,6 +581,10 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina targets = RenderView( mainView, colorTarget, outputs, realTime, simTime, rootTimer, renderContext ); } + // store the last view and projection matrices for the next frame, so the scene can use them for temporal effects + m_viewLast = m_scene->m_viewLast; + m_projectionLast = m_scene->m_projectionLast; + renderContext.m_esm.SetRenderTarget( 0, *destinations.data ); renderContext.m_esm.SetDepthStencilBuffer( {} ); @@ -657,7 +661,7 @@ float EveSpaceSceneRenderDriver::GetMipLodBias() const // -------------------------------------------------------------------------------------- // Description: // Render the scene from one view (the main view or a reflection probe face) into -// colorTarget. Returns the targets the view rendered, for post processing +// colorTarget. Returns the targets the view rendered, for post processing. // -------------------------------------------------------------------------------------- EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext, const uint32_t colorTargetSlice ) { @@ -801,9 +805,6 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co TimeSection endRenderSection( m_timers.endRender, "EndRender", rootTimer, renderContext ); m_scene->EndRender( view.scene, renderContext ); } - // non-temporal views leave the scene's history untouched, so this is a no-op for them - m_viewLast = m_scene->m_viewLast; - m_projectionLast = m_scene->m_projectionLast; m_scene->PopulateAndApplyPerFrameData( renderContext ); GlobalStore().RegisterVariable( "EveSpaceSceneOpaqueMap", Tr2TextureAL{} ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 6efb36ba8..915e2c13e 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -90,7 +90,7 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : private: // The driver's features a view uses. What the scene renders is described by the - // SceneRender::ViewInfo in RenderInfo. The features are off by default and every factory + // EveSceneView::ViewInfo in RenderInfo. The features are off by default and every factory // sets every feature, so a new feature has to be decided for each kind of view. struct Features { diff --git a/trinity/Tr2LightManager.cpp b/trinity/Tr2LightManager.cpp index 178f006d6..10ca623f0 100644 --- a/trinity/Tr2LightManager.cpp +++ b/trinity/Tr2LightManager.cpp @@ -46,7 +46,6 @@ const uint32_t TILE_HEIGHT = 16; const uint32_t HIGH_QUALITY_ATLAS_SIZE_LOG2 = 14; const uint32_t HIGH_QUALITY_ATLAS_ENTRY_MAX_SIZE = 1 << 13; const uint32_t MAX_NUM_SHADOWCASTING_LIGHTS = 16; -const uint32_t MAX_NUM_SHADOWCASTING_LIGHTS_IN_REFLECTIONS = 4; const uint32_t MAX_NUM_VOLUMETRIC_LIGHTS = 16; const uint32_t MAX_NUM_VOLUMETRIC_LIGHTS_IN_REFLECTIONS = 16; From da0cddeba024693131f3341a4a15cabe4d6f8723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Wed, 28 Oct 2026 10:54:45 +0000 Subject: [PATCH 12/20] Lensflares in reflection fix Fixing lensflares in the reflections. They rendered into opposite faces (f.ex -y and +y) and would dissappear if the main view looks away from the lensflare --- trinity/Eve/EveSpaceScene.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index cf54b87a1..e95ab31ec 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1454,9 +1454,12 @@ void EveSpaceScene::GatherDynamicLights( Tr2RenderContext& renderContext ) void EveSpaceScene::PrepareLensflares() { // the lensflares need a special pre-render update + // visibility is updated for every view, after PrepareRender, so a reflection face + // doesn't use the main view's visibility or the previous view's transform for( auto it = m_lensflares.cbegin(); it != m_lensflares.cend(); ++it ) { ( *it )->PrepareRender( m_updateContext.GetFrustum() ); + ( *it )->UpdateVisibility( m_updateContext ); } } @@ -1542,12 +1545,6 @@ void EveSpaceScene::GatherSceneObjectRenderables( std::vector Tr2ParallelDo( m_staticParticles.begin(), m_staticParticles.end(), [&]( EveSceneStaticParticles* staticParticles ) { staticParticles->UpdateVisibility( m_updateContext ); } ); - - // until we have proper support for multiple lensflares we just do it in a list... - for( auto& lensflare : m_lensflares ) - { - lensflare->UpdateVisibility( m_updateContext ); - } } // objects will be batched up. From 8e2b9672f61d34efa31b4c084dbbf7b32dbade9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Thu, 29 Oct 2026 09:40:29 +0000 Subject: [PATCH 13/20] EveSpaceScene Renaming EndRenderto EndView --- trinity/Eve/EveSpaceScene.cpp | 2 +- trinity/Eve/EveSpaceScene.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index e95ab31ec..8845595bb 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -2700,7 +2700,7 @@ void EveSpaceScene::RunLensflareOcclusionQueries( const Tr2TextureAL& depthMap, // Description: // Render lensflares. Clear primary batches and frame dependant object lists. // -------------------------------------------------------------------------------------- -void EveSpaceScene::EndRender( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ) +void EveSpaceScene::EndView( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ) { TRINITY_STATS_ZONE( __FUNCTION__ ); diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 8b73b64ff..ae7caef9c 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -148,7 +148,7 @@ BLUE_CLASS( EveSpaceScene ) : bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void BeginFrame( Tr2RenderContext& renderContext ); void BeginView( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); - void EndRender( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); + void EndView( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void Render3DUI( Tr2RenderContext & renderContext ); void PopulateAndApplyPerFrameData( Tr2RenderContext& renderContext ); void ApplyUpscalingToPerFrameData( uint32_t width, uint32_t height, Tr2RenderContext& renderContext ); @@ -419,7 +419,7 @@ BLUE_CLASS( EveSpaceScene ) : PEveDistanceFieldVector m_distanceFields; // Primary batches, gathered in BeginView and - // cleared in EndRender + // cleared in EndView BatchMap m_primaryBatches; // Secondary batches used to render planets, stars, shadowed objects // and so forth. Should be finalized and cleared each time they're used. From 6061ca2c25add532263099e226adb9d215230696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Thu, 29 Oct 2026 09:40:59 +0000 Subject: [PATCH 14/20] Timer fixup Fixing the way we time frame, mainview and reflection views. So it now shows up correctly --- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 46 ++++++++++++----------- trinity/Eve/EveSpaceSceneRenderDriver.h | 35 +++++++++++------ 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index d243b1fe2..10e11b174 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -27,9 +27,9 @@ namespace void BeginRenderPass( Tr2RenderContext& renderContext, const std::initializer_list>& colorAttachments, const Tr2TextureAL& depthAttachment ) { uint32_t index = 0; - for( auto& colorAttachment : colorAttachments ) + for( auto& [colorAttachment, slice] : colorAttachments ) { - renderContext.m_esm.SetRenderTarget( index++, colorAttachment.first, true, colorAttachment.second ); + renderContext.m_esm.SetRenderTarget( index++, colorAttachment, true, slice ); } // clear out remaining slots, generously assuming max 4 render targets const uint32_t maxRenderTargets = 4; @@ -80,7 +80,7 @@ struct TimeSection { if( rootTimer.GetCaptureCpuTime() || rootTimer.GetCaptureGpuTime() ) { - m_timer.SetStatName( ( rootTimer.GetStatName() + std::string( "/" ) + name ).c_str() ); + m_timer.SetStatName( rootTimer.GetStatName().c_str() + "/" + name ); m_timer.SetCaptureCpuTime( rootTimer.GetCaptureCpuTime() ); m_timer.SetCaptureGpuTime( rootTimer.GetCaptureGpuTime() ); m_timer.Begin( renderContext ); @@ -563,8 +563,7 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina UpdateSceneFrame( mainView.camera, realTime, simTime, rootTimer, renderContext ); ReflectionFaces reflectionFaces; { - TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); - + TimeSection beginFrameSection( m_timers.beginFrame, "BeginFrame", rootTimer, renderContext ); m_scene->BeginFrame( renderContext ); reflectionFaces = PrepareReflectionFaces( renderContext ); @@ -575,10 +574,13 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina } { TimeSection reflectionsSection( m_timers.reflections, "RenderReflections", rootTimer, renderContext ); - RenderReflectionFaces( reflectionFaces, realTime, simTime, rootTimer, renderContext ); + // set the frame reflection timer as the root timer so we get correct timer paths + RenderReflectionFaces( reflectionFaces, realTime, simTime, m_timers.reflections, renderContext ); + } + { + TimeSection totalSection( m_timers.mainView.total, "MainView", rootTimer, renderContext ); + targets = RenderView( mainView, colorTarget, outputs, m_timers.mainView, realTime, simTime, m_timers.mainView.total, renderContext ); } - - targets = RenderView( mainView, colorTarget, outputs, realTime, simTime, rootTimer, renderContext ); } // store the last view and projection matrices for the next frame, so the scene can use them for temporal effects @@ -629,7 +631,7 @@ Matrix EveSpaceSceneRenderDriver::GetJitterMatrix( uint32_t renderWidth, uint32_ } else if( m_scene && m_scene->GetPostProcess() && m_scene->GetPostProcess()->GetTaaIfAvailable() != nullptr ) { - auto frameIndex = renderContext.GetPrimaryRenderContext().GetRenderedFrameNumber(); + auto frameIndex = renderContext.GetPrimaryRenderContext().GetRecordingFrameNumber(); jitterMatrix = Tr2PostProcessRenderer::GetJitter( frameIndex, renderWidth, renderHeight ); } else @@ -663,7 +665,7 @@ float EveSpaceSceneRenderDriver::GetMipLodBias() const // Render the scene from one view (the main view or a reflection probe face) into // colorTarget. Returns the targets the view rendered, for post processing. // -------------------------------------------------------------------------------------- -EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext, const uint32_t colorTargetSlice ) +EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, ViewTimers& timers, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext, const uint32_t colorTargetSlice ) { const TextureSize2D& renderSize = view.camera.renderSize; @@ -697,7 +699,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co renderContext.Clear( renderBackgroundNode ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); { - TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); + TimeSection beginViewSection( timers.beginView, "BeginView", rootTimer, renderContext ); m_scene->BeginView( view.scene, renderContext ); } @@ -711,7 +713,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co bool hasBackgroundDistortionBatches = false; { - TimeSection backgroundSection( m_timers.background, "BackgroundPass", rootTimer, renderContext ); + TimeSection backgroundSection( timers.background, "BackgroundPass", rootTimer, renderContext ); hasBackgroundDistortionBatches = m_scene->RenderBackgroundPass( depthBuffer, distortionMap, velocityMap, view.scene, renderContext ); } if( view.scene.distortion && hasBackgroundDistortionBatches ) @@ -725,7 +727,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co Tr2GpuResourcePool::Texture normalMap = GetNormalMapIfNeeded( renderSize, outputs, view.features ); Tr2GpuResourcePool::Texture customStencil = GetCustomStencilMapIfNeeded( renderSize, outputs ); { - TimeSection depthPassSection( m_timers.depthPass, "DepthPass", rootTimer, renderContext ); + TimeSection depthPassSection( timers.depthPass, "DepthPass", rootTimer, renderContext ); m_scene->RenderDepthPass( depthBuffer, normalMap, customStencil, view.scene, renderContext, m_depthPassTechnique ); } GlobalStore().RegisterVariable( "SpaceSceneNormalMap", normalMap ); @@ -737,10 +739,10 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co { if( view.features.shadows ) { - TimeSection shadowsSection( m_timers.shadows, "Shadows", rootTimer, renderContext ); + TimeSection shadowsSection( timers.shadows, "Shadows", rootTimer, renderContext ); shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, view.scene, renderContext ); } - TimeSection ssaoSection( m_timers.ssao, "SSAO", rootTimer, renderContext ); + TimeSection ssaoSection( timers.ssao, "SSAO", rootTimer, renderContext ); auto ssao = view.features.ssao ? RenderSSAO( depthBuffer, normalMap, renderContext ) : GetEmptySSAO( m_gpuResourcePool ); ssaoSection.Stop(); @@ -762,7 +764,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co bool hasDistortionBatches = false; distortionMap = GetDistortionMapIfNeeded( renderSize, view.scene.distortion ); { - TimeSection mainPassSection( m_timers.mainPass, "MainPass", rootTimer, renderContext ); + TimeSection mainPassSection( timers.mainPass, "MainPass", rootTimer, renderContext ); hasDistortionBatches = m_scene->RenderMainPass( backBuffer, depthBuffer, distortionMap, velocityMap, targets.opaqueColor, m_gpuResourcePool, view.scene, renderContext ); } RegisterWithVariableStore( {}, m_gpuResourcePool ); @@ -802,8 +804,8 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co renderContext.SetReadOnlyDepth( false ); } { - TimeSection endRenderSection( m_timers.endRender, "EndRender", rootTimer, renderContext ); - m_scene->EndRender( view.scene, renderContext ); + TimeSection endViewSection( timers.endView, "EndView", rootTimer, renderContext ); + m_scene->EndView( view.scene, renderContext ); } m_scene->PopulateAndApplyPerFrameData( renderContext ); @@ -885,11 +887,13 @@ void EveSpaceSceneRenderDriver::RenderReflectionFaces( const ReflectionFaces& fa unsigned face = faces.startFace; for( const RenderInfo& faceView : faces.views ) { - std::string regionName = "Reflection Face " + std::to_string( face ); + auto& timers = m_timers.reflectionTimers[face]; + const std::string regionName = "Face" + std::to_string( face ); GPU_REGION( renderContext, regionName.c_str() ); - + TimeSection totalSection( timers.total, regionName.c_str(), rootTimer, renderContext ); // render straight into the probe's face - RenderView( faceView, faceTarget, {}, realTime, simTime, rootTimer, renderContext, face++ ); + RenderView( faceView, faceTarget, {}, timers, realTime, simTime, timers.total, renderContext, face ); + ++face; } // unbind the probe cube so it can be used as a shader resource in the main view diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 915e2c13e..76d1ac538 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -109,6 +109,19 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : static Features ReflectionFace(); }; + // Timer collection for the view's rendering, used by RenderView and RenderReflectionFaces + struct ViewTimers + { + Tr2ProfileTimer beginView; + Tr2ProfileTimer endView; + Tr2ProfileTimer background; + Tr2ProfileTimer depthPass; + Tr2ProfileTimer mainPass; + Tr2ProfileTimer shadows; + Tr2ProfileTimer ssao; + Tr2ProfileTimer total; + }; + // A view rendered by RenderView: its camera and what the scene renders from it, the // driver's features, and its view in the Tr2LightManager struct RenderInfo @@ -138,7 +151,7 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : std::vector views; }; - ViewTargets RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext, const uint32_t colorTargetSlice = 0 ); + ViewTargets RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, ViewTimers& timers, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext, const uint32_t colorTargetSlice = 0 ); ReflectionFaces PrepareReflectionFaces( Tr2RenderContext& renderContext ); void SetLightViews( const RenderInfo& mainView, ReflectionFaces& faces, Tr2RenderContext& renderContext ); void RenderReflectionFaces( const ReflectionFaces& faces, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); @@ -209,20 +222,20 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : TriTextureResPtr m_reflectionCorrectionMap; TriTextureResPtr m_blackReflectionCorrectionMap; - struct + struct FrameTimers { + // once per frame timers + Tr2ProfileTimer beginFrame; Tr2ProfileTimer update; - Tr2ProfileTimer beginRender; - Tr2ProfileTimer endRender; + Tr2ProfileTimer reflections; Tr2ProfileTimer postProcess; Tr2ProfileTimer ui3d; - Tr2ProfileTimer background; - Tr2ProfileTimer depthPass; - Tr2ProfileTimer mainPass; - Tr2ProfileTimer reflections; - Tr2ProfileTimer shadows; - Tr2ProfileTimer ssao; - } m_timers; + + // one set of timers for the main rendering view, and one set of timers for each reflection cube face + ViewTimers mainView; + std::array reflectionTimers; + }; + FrameTimers m_timers; }; TYPEDEF_BLUECLASS( EveSpaceSceneRenderDriver ); From caa0b708aed2e1e7551f5227ecae5c906e3b3685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Thu, 29 Oct 2026 09:44:39 +0000 Subject: [PATCH 15/20] Update EveSpaceSceneRenderDriver.cpp fixing a compiler error... --- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 10e11b174..f2ce4a16b 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -80,7 +80,7 @@ struct TimeSection { if( rootTimer.GetCaptureCpuTime() || rootTimer.GetCaptureGpuTime() ) { - m_timer.SetStatName( rootTimer.GetStatName().c_str() + "/" + name ); + m_timer.SetStatName( (rootTimer.GetStatName() + "/" + name).c_str() ); m_timer.SetCaptureCpuTime( rootTimer.GetCaptureCpuTime() ); m_timer.SetCaptureGpuTime( rootTimer.GetCaptureGpuTime() ); m_timer.Begin( renderContext ); From e3138796f4dcce006e017e6593ccf30c52e6fce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Thu, 29 Oct 2026 11:42:44 +0000 Subject: [PATCH 16/20] Update EveSpaceScene.cpp fixing calls to EveInstancedMeshManager::CollectMeshes for every view --- trinity/Eve/EveSpaceScene.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index 8845595bb..0cbc68bda 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1512,8 +1512,6 @@ void EveSpaceScene::GatherBatches( const EveSceneView::ViewInfo& view, Tr2Render if( gatherObjects ) { - m_instancedMeshManager->CollectMeshes( *m_componentRegistry ); - // passing the reason filters out instanced meshes that should not be reflected m_instancedMeshManager->GetBatches( m_updateContext.GetFrustum(), m_updateContext.GetInvLodFactor(), { { TRIBATCHTYPE_OPAQUE, *m_primaryBatches[TRIBATCHTYPE_OPAQUE] }, { TRIBATCHTYPE_DECAL, *m_primaryBatches[TRIBATCHTYPE_DECAL] }, { TRIBATCHTYPE_ADDITIVE, *m_primaryBatches[TRIBATCHTYPE_ADDITIVE] }, { TRIBATCHTYPE_DEPTH, *m_primaryBatches[TRIBATCHTYPE_DEPTH] }, { TRIBATCHTYPE_DISTORTION, *m_primaryBatches[TRIBATCHTYPE_DISTORTION] } }, view.reason ); From 8b80f34fbaffbba30e281f46e0c55ea353c8be08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Thu, 29 Oct 2026 11:49:17 +0000 Subject: [PATCH 17/20] Update EveSpaceScene.cpp forgot to move the CollectMeshes (last commit only deleted it). lupdatePostProcessAttrributes is now only called once --- trinity/Eve/EveSpaceScene.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index 0cbc68bda..e6e69f3cc 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -503,9 +503,6 @@ void EveSpaceScene::UpdateFrame( Be::Time realTime, Be::Time simTime ) m_updateContext.UpdateOrigin( m_ballpark ); m_updateContext.SetDataTextureManager( m_dataTextureMgr ); - // update the combined postprocess attributes - UpdatePostProcessAttributes(); - { TRINITY_STATS_ZONE( "UpdateBackgroundObjects" ); @@ -1312,6 +1309,8 @@ void EveSpaceScene::BeginFrame( Tr2RenderContext& renderContext ) m_dataTextureMgr->SetVariables(); } + m_instancedMeshManager->CollectMeshes( *m_componentRegistry ); + ResolveLightingOverrides(); // fog settings and the environment map accumulate over time, so they must only advance once per frame From 657556fe3bad046f65a55ac9292c78f7ae4749b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Thu, 29 Oct 2026 11:58:23 +0000 Subject: [PATCH 18/20] code formatting fixes --- trinity/Eve/EveSpaceScene.h | 4 ++-- trinity/Eve/EveSpaceSceneRenderDriver.cpp | 6 +++--- trinity/Eve/EveSpaceSceneRenderDriver.h | 3 +-- trinity/Tr2LightManager.cpp | 2 +- trinity/Tr2LightManager.h | 2 +- trinity/Tr2ReflectionProbe.cpp | 3 ++- trinity/Tr2ReflectionProbe.h | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index ae7caef9c..0da9abc88 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -155,7 +155,7 @@ BLUE_CLASS( EveSpaceScene ) : void GatherBatches( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void PrepareRaytracedShadows( Tr2RenderContext & renderContext ); - + void SetCamera( const EveSceneView::Camera& camera ); Tr2ReflectionProbe* GetReflectionProbe() const; @@ -720,7 +720,7 @@ BLUE_CLASS( EveSpaceScene ) : // reprojection matrix Matrix m_reprojectionMatrix; - + std::unique_ptr m_instancedMeshManager; friend class EveSpaceSceneRenderDriver; diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index f2ce4a16b..63b7745cb 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -80,7 +80,7 @@ struct TimeSection { if( rootTimer.GetCaptureCpuTime() || rootTimer.GetCaptureGpuTime() ) { - m_timer.SetStatName( (rootTimer.GetStatName() + "/" + name).c_str() ); + m_timer.SetStatName( ( rootTimer.GetStatName() + "/" + name ).c_str() ); m_timer.SetCaptureCpuTime( rootTimer.GetCaptureCpuTime() ); m_timer.SetCaptureGpuTime( rootTimer.GetCaptureGpuTime() ); m_timer.Begin( renderContext ); @@ -697,7 +697,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co BeginRenderPass( renderContext, { { backBuffer, colorTargetSlice } }, depthBuffer ); renderContext.Clear( renderBackgroundNode ? Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER : ( Tr2RenderContextEnum::CLEARFLAGS_TARGET | Tr2RenderContextEnum::CLEARFLAGS_ZBUFFER ), m_settings.clearColor, 0 ); - + { TimeSection beginViewSection( timers.beginView, "BeginView", rootTimer, renderContext ); m_scene->BeginView( view.scene, renderContext ); @@ -745,7 +745,7 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co TimeSection ssaoSection( timers.ssao, "SSAO", rootTimer, renderContext ); auto ssao = view.features.ssao ? RenderSSAO( depthBuffer, normalMap, renderContext ) : GetEmptySSAO( m_gpuResourcePool ); ssaoSection.Stop(); - + GlobalStore().RegisterVariable( "SSAOMap", ssao ); auto lightManager = Tr2LightManager::GetInstance(); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 76d1ac538..e44e66434 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -88,7 +88,6 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : EXPOSE_TO_BLUE(); private: - // The driver's features a view uses. What the scene renders is described by the // EveSceneView::ViewInfo in RenderInfo. The features are off by default and every factory // sets every feature, so a new feature has to be decided for each kind of view. @@ -164,7 +163,7 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : EveSceneView::Camera GetMainCamera( const TextureSize2D& renderSize, const Matrix& jitterMatrix, float mipLodBias ) const; Matrix GetJitterMatrix( uint32_t renderWidth, uint32_t renderHeight, Tr2RenderContext& renderContext ) const; - float GetMipLodBias( ) const; + float GetMipLodBias() const; Tr2GpuResourcePool::Texture GetDistortionMapIfNeeded( const TextureSize2D& size, bool distortion ); Tr2GpuResourcePool::Texture GetVelocityMapIfNeeded( const TextureSize2D& size, const Span& outputs, const Features& featureSet ); diff --git a/trinity/Tr2LightManager.cpp b/trinity/Tr2LightManager.cpp index 10ca623f0..b2e12d8b0 100644 --- a/trinity/Tr2LightManager.cpp +++ b/trinity/Tr2LightManager.cpp @@ -310,7 +310,7 @@ void Tr2LightManager::Clear( Tr2RenderContext& renderContext ) view.shadowCastingLights.clear(); view.volumetricLights.clear(); } - + // shadow map is only on the main view currently m_ShadowMap.m_atlasNodes.resize( 1 ); m_ShadowMap.m_atlasNodes[0].x = 0; diff --git a/trinity/Tr2LightManager.h b/trinity/Tr2LightManager.h index d4f9d2176..6143cafc1 100644 --- a/trinity/Tr2LightManager.h +++ b/trinity/Tr2LightManager.h @@ -147,7 +147,7 @@ class Tr2LightManager : public Tr2DeviceResource void AddPointLight( const Vector3& position, float radius, const Color& color, Float_16 innerRadius = Float_16( 0.f ), uint16_t flags = FLAG_DEFAULT, bool scaleBrightness = true ); void AddLight( PerLightData& data, bool scaleBrightness = true, const Tr2LightTags& tags = Tr2LightTags() ); - void ResolveLightData(); + void ResolveLightData(); ALResult UpdateLists( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ); void AdjustLightCutoff( float lodFactor ); diff --git a/trinity/Tr2ReflectionProbe.cpp b/trinity/Tr2ReflectionProbe.cpp index 27be1841f..7a5503ed9 100644 --- a/trinity/Tr2ReflectionProbe.cpp +++ b/trinity/Tr2ReflectionProbe.cpp @@ -96,7 +96,8 @@ Matrix Tr2ReflectionProbe::GetFaceProjection( const Matrix& currentProjection ) Matrix Tr2ReflectionProbe::GetFaceViewTransform( unsigned face ) const { static const Vector3 faceDirections[] = { - Vector3( 0, 1, 0 ), Vector3( 1, 0, 0 ), //+X + Vector3( 0, 1, 0 ), + Vector3( 1, 0, 0 ), //+X Vector3( 0, 1, 0 ), Vector3( -1, 0, 0 ), //-X Vector3( 0, 0, -1 ), diff --git a/trinity/Tr2ReflectionProbe.h b/trinity/Tr2ReflectionProbe.h index f673084dd..93346d2f6 100644 --- a/trinity/Tr2ReflectionProbe.h +++ b/trinity/Tr2ReflectionProbe.h @@ -56,7 +56,7 @@ BLUE_CLASS( Tr2ReflectionProbe ) : private: void RunFilter(); - void Filter( const Vector3& viewDirection, Tr2RenderContext & renderContext ); + void Filter( const Vector3& viewDirection, Tr2RenderContext& renderContext ); bool DoPrepareResources( ImageIO::PixelFormat targetFormat, Tr2PrimaryRenderContext & renderContext ); void DestroyRenderTargets(); From 1f326679a9eca2c71cabfcfc28b83e09d375c86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Thu, 29 Oct 2026 14:09:39 +0000 Subject: [PATCH 19/20] Addressing some code review issues --- trinity/Eve/EveSpaceScene.cpp | 15 ++++++---- trinity/Eve/EveSpaceScene.h | 5 ++++ trinity/Eve/EveSpaceSceneRenderDriver.cpp | 34 ++++++++++++++--------- trinity/Eve/EveSpaceSceneRenderDriver.h | 4 ++- 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index e6e69f3cc..5c2f46327 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -450,7 +450,9 @@ void EveSpaceScene::Update( Be::Time realTime, Be::Time simTime ) if( frame == m_lastUpdateFrame ) { - // already updated this frame + // already updated this frame, but another render driver may have changed the + // post-process settings since + UpdatePostProcessAttributes(); return; } m_lastUpdateFrame = frame; @@ -1297,6 +1299,12 @@ void EveSpaceScene::BeginFrame( Tr2RenderContext& renderContext ) TRINITY_STATS_ZONE( __FUNCTION__ ); renderContext.AddGpuMarker( __FUNCTION__ ); + // Ensure that the camera attachment parent is updated to the current view transform, + // so that any attached objects are in the correct position for the frame. + m_cameraAttachmentParent->SetTransform( Tr2Renderer::GetInverseViewTransform() ); + m_cameraAttachmentParent->UpdateSyncronous( m_updateContext ); + m_cameraAttachmentParent->UpdateAsyncronous( m_updateContext ); + EnsureVisualizerEffect(); if( m_shLightingManager ) @@ -1534,9 +1542,6 @@ void EveSpaceScene::GatherSceneObjectRenderables( std::vector { TRINITY_STATS_ZONE( "UpdateVisibility" ); - m_cameraAttachmentParent->SetTransform( Tr2Renderer::GetInverseViewTransform() ); - m_cameraAttachmentParent->UpdateSyncronous( m_updateContext ); - m_cameraAttachmentParent->UpdateAsyncronous( m_updateContext ); m_cameraAttachmentParent->UpdateVisibility( m_updateContext, identity ); Tr2ParallelDo( m_staticParticles.begin(), m_staticParticles.end(), [&]( EveSceneStaticParticles* staticParticles ) { @@ -2666,7 +2671,7 @@ bool EveSpaceScene::RenderMainPass( // -------------------------------------------------------------------------------------- // Description: -// Non-temporal views use the reflection fog, so they don't disturb the main view's fog +// Reflection views use the reflection fog, so they don't disturb the main view's fog // history. // -------------------------------------------------------------------------------------- std::pair EveSpaceScene::RenderReflectionVolumetrics( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ) diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 0da9abc88..14eabec8a 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -215,6 +215,11 @@ BLUE_CLASS( EveSpaceScene ) : return m_objects; } Tr2PostProcess2Ptr GetPostProcess(); + // the scene's own post process, which the render driver sets the TAA on + const Tr2PostProcess2Ptr& GetDefaultPostProcess() const + { + return m_sceneDefaultPostProcess; + } Matrix GetReprojectionMatrix() const; diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index 63b7745cb..97e486097 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -220,21 +220,21 @@ void EveSpaceSceneRenderDriver::SetupUpscaling( const TextureSize2D& displaySize void EveSpaceSceneRenderDriver::PropagateSettings() { // AA setting - if( m_scene->m_sceneDefaultPostProcess ) + if( const auto& defaultPostProcess = m_scene->GetDefaultPostProcess() ) { if( m_settings.antiAliasingQuality == AntiAliasingQuality::Disabled ) { - m_scene->m_sceneDefaultPostProcess->SetTaa( nullptr ); + defaultPostProcess->SetTaa( nullptr ); } else { - if( !m_scene->m_sceneDefaultPostProcess->GetTaaIfAvailable() ) + if( !defaultPostProcess->GetTaaIfAvailable() ) { Tr2PPTaaEffectPtr taa; taa.CreateInstance(); - m_scene->m_sceneDefaultPostProcess->SetTaa( taa ); + defaultPostProcess->SetTaa( taa ); } - m_scene->m_sceneDefaultPostProcess->GetTaaIfAvailable()->m_quality = (Tr2PPTaaEffect::Quality)m_settings.antiAliasingQuality; + defaultPostProcess->GetTaaIfAvailable()->m_quality = (Tr2PPTaaEffect::Quality)m_settings.antiAliasingQuality; } } @@ -332,7 +332,7 @@ EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::Main() features.upscaling = true; features.antiAliasing = true; features.ssao = true; - features.shadows = true; + features.raytracedShadows = true; features.velocityMap = true; features.background = true; features.overlay = true; @@ -347,7 +347,7 @@ EveSpaceSceneRenderDriver::Features EveSpaceSceneRenderDriver::Features::Reflect features.upscaling = false; features.antiAliasing = false; features.ssao = false; - features.shadows = true; + features.raytracedShadows = false; features.velocityMap = false; features.background = false; features.overlay = false; @@ -449,7 +449,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::RenderSSAO( const Tr2Text { renderContext.SetReadOnlyDepth( true ); - auto scenePostProcess = m_scene->m_sceneDefaultPostProcess; + const auto& scenePostProcess = m_scene->GetDefaultPostProcess(); bool temporal = m_upscalingInfo.temporal || ( scenePostProcess != nullptr && scenePostProcess->GetTaaIfAvailable() != nullptr ); ssao = m_ssao->Filter( depthMap, normalMap, m_gpuResourcePool, renderContext, temporal ); @@ -622,6 +622,9 @@ Matrix EveSpaceSceneRenderDriver::GetJitterMatrix( uint32_t renderWidth, uint32_ { Matrix jitterMatrix; + // The TAA is read from the scene's default post process, which PropagateSettings has set + // up for this driver. The combined post process only picks it up in the scene update, + // which runs after the main view's camera is built. if( m_upscalingInfo.temporal ) { float jitterX = 0.0f; @@ -629,7 +632,7 @@ Matrix EveSpaceSceneRenderDriver::GetJitterMatrix( uint32_t renderWidth, uint32_ m_upscalingContext->GetJitter( jitterX, jitterY ); jitterMatrix = TranslationMatrix( Vector3( jitterX, jitterY, 0 ) ); } - else if( m_scene && m_scene->GetPostProcess() && m_scene->GetPostProcess()->GetTaaIfAvailable() != nullptr ) + else if( m_scene && m_scene->GetDefaultPostProcess() && m_scene->GetDefaultPostProcess()->GetTaaIfAvailable() != nullptr ) { auto frameIndex = renderContext.GetPrimaryRenderContext().GetRecordingFrameNumber(); jitterMatrix = Tr2PostProcessRenderer::GetJitter( frameIndex, renderWidth, renderHeight ); @@ -648,10 +651,11 @@ float EveSpaceSceneRenderDriver::GetMipLodBias() const float mipLodBias = m_upscalingInfo.mipLevelBias; // The post process bias belongs to its TAA. A temporal upscaler replaces the TAA and - // its bias already accounts for it. + // its bias already accounts for it. Read from the default post process, as in + // GetJitterMatrix. if( !m_upscalingInfo.temporal && m_scene ) { - auto postprocess = m_scene->GetPostProcess(); + const auto& postprocess = m_scene->GetDefaultPostProcess(); if( postprocess && postprocess->GetTaaIfAvailable() != nullptr ) { mipLodBias += postprocess->GetMipLodBias(); @@ -690,6 +694,9 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co renderContext.Clear( Tr2RenderContextEnum::CLEARFLAGS_TARGET, m_settings.clearColor, 0 ); m_background->Execute( { &backBuffer, 1 }, {}, realTime, simTime, rootTimer, renderContext ); + + // the background node can set its own camera + ApplyCamera( view.camera ); } targets.depth = m_gpuResourcePool.GetTempTexture( "depthBuffer", renderSize, ImageIO::PIXEL_FORMAT_D32_FLOAT, Tr2GpuUsage::DEPTH_STENCIL | Tr2GpuUsage::SHADER_RESOURCE ); @@ -737,7 +744,6 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co EveSpaceScene::ShadowResources shadowResources; if( m_scene->m_display && m_mainPassRenderingEnabled ) { - if( view.features.shadows ) { TimeSection shadowsSection( timers.shadows, "Shadows", rootTimer, renderContext ); shadowResources = m_scene->RenderShadows( depthBuffer, normalMap, m_gpuResourcePool, view.scene, renderContext ); @@ -798,6 +804,8 @@ EveSpaceSceneRenderDriver::ViewTargets EveSpaceSceneRenderDriver::RenderView( co normalMap = {}; BeginRenderPass( renderContext, { { backBuffer, colorTargetSlice } }, depthBuffer ); + // each lens flare has one occlusion result, which belongs to the main view + if( view.scene.temporal ) { renderContext.SetReadOnlyDepth( true ); m_scene->RunLensflareOcclusionQueries( depthBuffer, renderContext ); @@ -963,7 +971,7 @@ Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetVelocityMapIfNeeded( c Tr2GpuResourcePool::Texture EveSpaceSceneRenderDriver::GetNormalMapIfNeeded( const TextureSize2D& size, const Span& outputs, const Features& featureSet ) { // SSAO and raytraced shadows read the normal map - const bool needsNormalMap = ( featureSet.ssao && m_settings.aoQuality != AmbientOcclusionQuality::Disabled ) || ( featureSet.shadows && m_settings.shadowQuality == ShadowQuality::SHADOW_RAYTRACED ); + const bool needsNormalMap = ( featureSet.ssao && m_settings.aoQuality != AmbientOcclusionQuality::Disabled ) || ( featureSet.raytracedShadows && m_settings.shadowQuality == ShadowQuality::SHADOW_RAYTRACED ); auto namedOutput = FindNamedOutput( outputs, "NormalMap" ); if( ( needsNormalMap && Tr2Renderer::GetShaderModel() != TR2SM_3_0_LO ) || m_settings.forceNormalMap || namedOutput ) { diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index e44e66434..ea8b3ad19 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -96,7 +96,9 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : bool upscaling = false; bool antiAliasing = false; bool ssao = false; - bool shadows = false; + // the view gets raytraced shadows when they are selected, which read the normal map; + // reflection faces get the scene's cascaded reflection shadows instead + bool raytracedShadows = false; bool velocityMap = false; // special background rendering (f.ex texture background) bool background = false; From e170e38e57c7f8e2e42b0412eb92cf5d72ab3ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20=C3=9E=C3=B3r=20Gunnarsson?= Date: Fri, 30 Oct 2026 10:26:40 +0000 Subject: [PATCH 20/20] Formatting fixes --- trinity/Eve/EveSpaceScene.cpp | 2 +- trinity/Eve/EveSpaceScene.h | 12 ++++++------ trinity/Eve/EveSpaceSceneRenderDriver.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index b1ae4a104..45f38264b 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1353,7 +1353,7 @@ void EveSpaceScene::BeginFrame( Tr2RenderContext& renderContext ) TRINITY_STATS_ZONE( __FUNCTION__ ); renderContext.AddGpuMarker( __FUNCTION__ ); - // Ensure that the camera attachment parent is updated to the current view transform, + // Ensure that the camera attachment parent is updated to the current view transform, // so that any attached objects are in the correct position for the frame. m_cameraAttachmentParent->SetTransform( Tr2Renderer::GetInverseViewTransform() ); m_cameraAttachmentParent->UpdateSyncronous( m_updateContext ); diff --git a/trinity/Eve/EveSpaceScene.h b/trinity/Eve/EveSpaceScene.h index 14eabec8a..7de9f58f7 100644 --- a/trinity/Eve/EveSpaceScene.h +++ b/trinity/Eve/EveSpaceScene.h @@ -146,11 +146,11 @@ BLUE_CLASS( EveSpaceScene ) : void RenderDepthPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& normalMap, const Tr2TextureAL& customStencil, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext, const BlueSharedString& techniqueName = BlueSharedString( "Depth" ) ); bool RenderBackgroundPass( const Tr2TextureAL& depthMap, const Tr2TextureAL& distortionMap, const Tr2TextureAL& velocityMap, const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); - void BeginFrame( Tr2RenderContext& renderContext ); + void BeginFrame( Tr2RenderContext & renderContext ); void BeginView( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void EndView( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); void Render3DUI( Tr2RenderContext & renderContext ); - void PopulateAndApplyPerFrameData( Tr2RenderContext& renderContext ); + void PopulateAndApplyPerFrameData( Tr2RenderContext & renderContext ); void ApplyUpscalingToPerFrameData( uint32_t width, uint32_t height, Tr2RenderContext& renderContext ); void GatherBatches( const EveSceneView::ViewInfo& view, Tr2RenderContext& renderContext ); @@ -611,12 +611,12 @@ BLUE_CLASS( EveSpaceScene ) : void UpdateFrame( Be::Time realTime, Be::Time simTime ); void EnsureVisualizerEffect(); void ResolveLightingOverrides(); - void GatherDynamicLights( Tr2RenderContext& renderContext ); + void GatherDynamicLights( Tr2RenderContext & renderContext ); void PrepareLensflares(); - void GatherSceneObjectRenderables( std::vector& allObjects, std::vector& renderables, Tr2RenderContext& renderContext ); - void GatherReflectionRenderables( std::vector& renderables ); + void GatherSceneObjectRenderables( std::vector & allObjects, std::vector & renderables, Tr2RenderContext & renderContext ); + void GatherReflectionRenderables( std::vector & renderables ); ShadowResources RenderReflectionShadows( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); - + std::pair RenderVolumetrics( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); std::pair RenderReflectionVolumetrics( const Tr2TextureAL& depthMap, Tr2GpuResourcePool& gpuResourcePool, Tr2RenderContext& renderContext ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index ea8b3ad19..249a64beb 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -153,7 +153,7 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : }; ViewTargets RenderView( const RenderInfo& view, const Tr2TextureAL& colorTarget, const Span& outputs, ViewTimers& timers, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext, const uint32_t colorTargetSlice = 0 ); - ReflectionFaces PrepareReflectionFaces( Tr2RenderContext& renderContext ); + ReflectionFaces PrepareReflectionFaces( Tr2RenderContext & renderContext ); void SetLightViews( const RenderInfo& mainView, ReflectionFaces& faces, Tr2RenderContext& renderContext ); void RenderReflectionFaces( const ReflectionFaces& faces, Be::Time realTime, Be::Time simTime, const Tr2ProfileTimer& rootTimer, Tr2RenderContext& renderContext ); void SetupUpscaling( const TextureSize2D& displaySize );