diff --git a/Source/Engine/Renderer/ProbesRenderer.cpp b/Source/Engine/Renderer/ProbesRenderer.cpp index ac19cd309..ae94385e2 100644 --- a/Source/Engine/Renderer/ProbesRenderer.cpp +++ b/Source/Engine/Renderer/ProbesRenderer.cpp @@ -106,6 +106,8 @@ private: Array _probesToBake; ProbeEntry _current; + int32 _workStep; + float _customCullingNear; AssetReference _shader; GPUPipelineState* _psFilterFace = nullptr; @@ -134,6 +136,7 @@ ProbesRendererService ProbesRendererServiceInstance; TimeSpan ProbesRenderer::UpdateDelay(0, 0, 0, 0, 100); TimeSpan ProbesRenderer::ReleaseTimeout(0, 0, 0, 30); +int32 ProbesRenderer::MaxWorkPerFrame = 1; Delegate ProbesRenderer::OnRegisterBake; Delegate ProbesRenderer::OnFinishBake; @@ -293,6 +296,7 @@ void ProbesRendererService::Update() // Clear flag _updateFrameNumber = 0; + _workStep = 0; _current.Type = ProbeEntry::Types::Invalid; } else if (_current.Type == ProbeEntry::Types::Invalid && timeSinceUpdate > ProbesRenderer::UpdateDelay) @@ -321,6 +325,7 @@ void ProbesRendererService::Update() _probesToBake.RemoveAtKeepOrder(firstValidEntryIndex); _task->Enabled = true; _updateFrameNumber = 0; + _workStep = 0; _lastProbeUpdate = timeNow; } // Check if need to release data @@ -408,72 +413,76 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context) PROFILE_GPU("Render Probe"); // Init - float customCullingNear = -1; const int32 probeResolution = _current.GetResolution(); const PixelFormat probeFormat = _current.GetFormat(); - if (_current.Type == ProbeEntry::Types::EnvProbe) + if (_workStep == 0) { - auto envProbe = (EnvironmentProbe*)_current.Actor.Get(); - Vector3 position = envProbe->GetPosition(); - float radius = envProbe->GetScaledRadius(); - float nearPlane = Math::Max(0.1f, envProbe->CaptureNearPlane); + _customCullingNear = -1; + if (_current.Type == ProbeEntry::Types::EnvProbe) + { + auto envProbe = (EnvironmentProbe*)_current.Actor.Get(); + Vector3 position = envProbe->GetPosition(); + float radius = envProbe->GetScaledRadius(); + float nearPlane = Math::Max(0.1f, envProbe->CaptureNearPlane); - // Adjust far plane distance - float farPlane = Math::Max(radius, nearPlane + 100.0f); - farPlane *= farPlane < 10000 ? 10 : 4; - Function f(&FixFarPlane); - SceneQuery::TreeExecute(f, position, farPlane); + // Adjust far plane distance + float farPlane = Math::Max(radius, nearPlane + 100.0f); + farPlane *= farPlane < 10000 ? 10 : 4; + Function f(&FixFarPlane); + SceneQuery::TreeExecute(f, position, farPlane); - // Setup view - LargeWorlds::UpdateOrigin(_task->View.Origin, position); - _task->View.SetUpCube(nearPlane, farPlane, position - _task->View.Origin); + // Setup view + LargeWorlds::UpdateOrigin(_task->View.Origin, position); + _task->View.SetUpCube(nearPlane, farPlane, position - _task->View.Origin); + } + else if (_current.Type == ProbeEntry::Types::SkyLight) + { + auto skyLight = (SkyLight*)_current.Actor.Get(); + Vector3 position = skyLight->GetPosition(); + float nearPlane = 10.0f; + float farPlane = Math::Max(nearPlane + 1000.0f, skyLight->SkyDistanceThreshold * 2.0f); + _customCullingNear = skyLight->SkyDistanceThreshold; + + // Setup view + LargeWorlds::UpdateOrigin(_task->View.Origin, position); + _task->View.SetUpCube(nearPlane, farPlane, position - _task->View.Origin); + } + + // Resize buffers + bool resizeFailed = _output->Resize(probeResolution, probeResolution, probeFormat); + resizeFailed |= _probe->Resize(probeResolution, probeResolution, probeFormat); + resizeFailed |= _tmpFace->Resize(probeResolution, probeResolution, probeFormat); + resizeFailed |= _task->Resize(probeResolution, probeResolution); + if (resizeFailed) + LOG(Error, "Failed to resize probe"); } - else if (_current.Type == ProbeEntry::Types::SkyLight) - { - auto skyLight = (SkyLight*)_current.Actor.Get(); - Vector3 position = skyLight->GetPosition(); - float nearPlane = 10.0f; - float farPlane = Math::Max(nearPlane + 1000.0f, skyLight->SkyDistanceThreshold * 2.0f); - customCullingNear = skyLight->SkyDistanceThreshold; - - // Setup view - LargeWorlds::UpdateOrigin(_task->View.Origin, position); - _task->View.SetUpCube(nearPlane, farPlane, position - _task->View.Origin); - } - _task->CameraCut(); - - // Resize buffers - bool resizeFailed = _output->Resize(probeResolution, probeResolution, probeFormat); - resizeFailed |= _probe->Resize(probeResolution, probeResolution, probeFormat); - resizeFailed |= _tmpFace->Resize(probeResolution, probeResolution, probeFormat); - resizeFailed |= _task->Resize(probeResolution, probeResolution); - if (resizeFailed) - LOG(Error, "Failed to resize probe"); // Disable actor during baking (it cannot influence own results) const bool isActorActive = _current.Actor->GetIsActive(); _current.Actor->SetIsActive(false); // Lower quality when rendering probes in-game to gain performance - _task->View.MaxShadowsQuality = Engine::IsPlayMode() ? Quality::Low : Quality::Ultra; + _task->View.MaxShadowsQuality = Engine::IsPlayMode() || probeResolution <= 128 ? Quality::Low : Quality::Ultra; // Render scene for all faces - for (int32 faceIndex = 0; faceIndex < 6; faceIndex++) + int32 workLeft = ProbesRenderer::MaxWorkPerFrame; + const int32 lastFace = Math::Min(_workStep + workLeft, 6); + for (int32 faceIndex = _workStep; faceIndex < lastFace; faceIndex++) { + _task->CameraCut(); _task->View.SetFace(faceIndex); // Handle custom frustum for the culling (used to skip objects near the camera) - if (customCullingNear > 0) + if (_customCullingNear > 0) { Matrix p; - Matrix::PerspectiveFov(PI_OVER_2, 1.0f, customCullingNear, _task->View.Far, p); + Matrix::PerspectiveFov(PI_OVER_2, 1.0f, _customCullingNear, _task->View.Far, p); _task->View.CullingFrustum.SetMatrix(_task->View.View, p); } // Render frame Renderer::Render(_task); context->ClearState(); - _task->CameraCut(); // Copy frame to cube face { @@ -483,12 +492,17 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context) context->Draw(_output->View()); context->ResetRenderTarget(); } + + // Move to the next face + _workStep++; + workLeft--; } // Enable actor back _current.Actor->SetIsActive(isActorActive); // Filter all lower mip levels + if (workLeft > 0) { PROFILE_GPU("Filtering"); Data data; @@ -520,11 +534,18 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context) context->Draw(_tmpFace->View(0, mipIndex)); } } + + // End + workLeft--; + _workStep++; } // Cleanup context->ClearState(); + if (_workStep < 7) + return; // Continue rendering next frame + // Mark as rendered _updateFrameNumber = Engine::FrameCount; _task->Enabled = false; diff --git a/Source/Engine/Renderer/ProbesRenderer.h b/Source/Engine/Renderer/ProbesRenderer.h index 0e2007a37..73d8b4132 100644 --- a/Source/Engine/Renderer/ProbesRenderer.h +++ b/Source/Engine/Renderer/ProbesRenderer.h @@ -23,6 +23,11 @@ public: /// static TimeSpan ReleaseTimeout; + /// + /// Maximum amount of cubemap faces or filtering passes that can be performed per-frame (in total). Set it to 7 to perform whole cubemap capture within a single frame, lower values spread the work across multiple frames. + /// + static int32 MaxWorkPerFrame; + static Delegate OnRegisterBake; static Delegate OnFinishBake;