Optimize ProbesRenderer to use time-slicing for cubemap faces rendering and filtering

This commit is contained in:
Wojtek Figat
2025-07-03 11:43:56 +02:00
parent 094a6562b8
commit 33e58c12cb
2 changed files with 66 additions and 40 deletions

View File

@@ -106,6 +106,8 @@ private:
Array<ProbeEntry> _probesToBake;
ProbeEntry _current;
int32 _workStep;
float _customCullingNear;
AssetReference<Shader> _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<Actor*> ProbesRenderer::OnRegisterBake;
Delegate<Actor*> 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<bool(Actor*, const Vector3&, float&)> f(&FixFarPlane);
SceneQuery::TreeExecute<const Vector3&, float&>(f, position, farPlane);
// Adjust far plane distance
float farPlane = Math::Max(radius, nearPlane + 100.0f);
farPlane *= farPlane < 10000 ? 10 : 4;
Function<bool(Actor*, const Vector3&, float&)> f(&FixFarPlane);
SceneQuery::TreeExecute<const Vector3&, float&>(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;