Optimize decals rendering

This commit is contained in:
Wojtek Figat
2024-03-26 14:05:24 +01:00
parent 2dfb1058b2
commit 01d91bf102
6 changed files with 92 additions and 95 deletions

View File

@@ -34,8 +34,7 @@ void DecalMaterialShader::Bind(BindParameters& params)
ASSERT_LOW_LAYER(cb.Length() >= sizeof(DecalMaterialShaderData)); ASSERT_LOW_LAYER(cb.Length() >= sizeof(DecalMaterialShaderData));
auto materialData = reinterpret_cast<DecalMaterialShaderData*>(cb.Get()); auto materialData = reinterpret_cast<DecalMaterialShaderData*>(cb.Get());
cb = Span<byte>(cb.Get() + sizeof(DecalMaterialShaderData), cb.Length() - sizeof(DecalMaterialShaderData)); cb = Span<byte>(cb.Get() + sizeof(DecalMaterialShaderData), cb.Length() - sizeof(DecalMaterialShaderData));
int32 srv = 0; const bool isCameraInside = OrientedBoundingBox(Vector3::Half, drawCall.World).Contains(view.Position) == ContainmentType::Contains;
const bool isCameraInside = OrientedBoundingBox(Vector3::Half, params.FirstDrawCall->World).Contains(view.Position) == ContainmentType::Contains;
// Setup parameters // Setup parameters
MaterialParameter::BindMeta bindMeta; MaterialParameter::BindMeta bindMeta;

View File

@@ -69,20 +69,26 @@ void Decal::OnLayerChanged()
void Decal::Draw(RenderContext& renderContext) void Decal::Draw(RenderContext& renderContext)
{ {
MaterialBase* material = Material;
if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Decals) && if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Decals) &&
EnumHasAnyFlags(renderContext.View.Pass, DrawPass::GBuffer) && EnumHasAnyFlags(renderContext.View.Pass, DrawPass::GBuffer) &&
Material && material &&
Material->IsLoaded() && material->IsLoaded() &&
Material->IsDecal()) material->IsDecal())
{ {
// Check if decal is being culled
const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View);
const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(_sphere.Center - renderContext.View.Origin, (float)_sphere.Radius, *lodView) * renderContext.View.ModelLODDistanceFactorSqrt; const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(_sphere.Center - renderContext.View.Origin, (float)_sphere.Radius, *lodView) * renderContext.View.ModelLODDistanceFactorSqrt;
// Check if decal is being culled
if (Math::Square(DrawMinScreenSize * 0.5f) > screenRadiusSquared) if (Math::Square(DrawMinScreenSize * 0.5f) > screenRadiusSquared)
return; return;
renderContext.List->Decals.Add(this); RenderDecalData data;
Transform transform = GetTransform();
transform.Scale *= _size;
renderContext.View.GetWorldMatrix(transform, data.World);
data.SortOrder = SortOrder;
data.Material = material;
renderContext.List->Decals.Add(data);
} }
} }

View File

@@ -130,17 +130,14 @@ void MaterialComplexityMaterialShader::Draw(RenderContext& renderContext, GPUCon
MaterialBase::BindParameters bindParams(context, renderContext, drawCall); MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
bindParams.BindViewData(); bindParams.BindViewData();
drawCall.WorldDeterminantSign = 1.0f; drawCall.WorldDeterminantSign = 1.0f;
drawCall.PerInstanceRandom = 0.0f;
context->SetRenderTarget(lightBuffer); context->SetRenderTarget(lightBuffer);
for (int32 i = 0; i < decals.Count(); i++) for (int32 i = 0; i < decals.Count(); i++)
{ {
const auto decal = decals[i]; const RenderDecalData& decal = decals.Get()[i];
ASSERT(decal && decal->Material); drawCall.World = decal.World;
Transform transform = decal->GetTransform();
transform.Scale *= decal->GetSize();
renderContext.View.GetWorldMatrix(transform, drawCall.World);
drawCall.ObjectPosition = drawCall.World.GetTranslation(); drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.Material = decal->Material; drawCall.Material = decal.Material;
drawCall.PerInstanceRandom = decal->GetPerInstanceRandom();
decalsWrapper.Bind(bindParams); decalsWrapper.Bind(bindParams);
boxModel->Render(context); boxModel->Render(context);
} }

View File

@@ -49,7 +49,8 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context,
context->BindUA(1, overdrawTexture->View()); context->BindUA(1, overdrawTexture->View());
context->BindUA(2, liveCountTexture->View()); context->BindUA(2, liveCountTexture->View());
DrawCall drawCall; DrawCall drawCall;
drawCall.PerInstanceRandom = 1.0f; drawCall.WorldDeterminantSign = 1.0f;
drawCall.PerInstanceRandom = 0.0f;
MaterialBase::BindParameters bindParams(context, renderContext, drawCall); MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
bindParams.BindViewData(); bindParams.BindViewData();
renderContext.View.Pass = DrawPass::QuadOverdraw; renderContext.View.Pass = DrawPass::QuadOverdraw;
@@ -62,13 +63,8 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context,
// Draw decals // Draw decals
for (int32 i = 0; i < renderContext.List->Decals.Count(); i++) for (int32 i = 0; i < renderContext.List->Decals.Count(); i++)
{ {
const auto decal = renderContext.List->Decals[i]; const RenderDecalData& decal = renderContext.List->Decals.Get()[i];
ASSERT(decal && decal->Material); drawCall.World = decal.World;
Transform transform = decal->GetTransform();
transform.Scale *= decal->GetSize();
renderContext.View.GetWorldMatrix(transform, drawCall.World);
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.PerInstanceRandom = decal->GetPerInstanceRandom();
defaultMaterial->Bind(bindParams); defaultMaterial->Bind(bindParams);
boxModel->Render(context); boxModel->Render(context);
} }

View File

@@ -59,13 +59,8 @@ bool GBufferPass::Init()
bool GBufferPass::setupResources() bool GBufferPass::setupResources()
{ {
ASSERT(_gBufferShader); if (!_gBufferShader || !_gBufferShader->IsLoaded())
// Check if shader has not been loaded
if (!_gBufferShader->IsLoaded())
{
return true; return true;
}
auto gbuffer = _gBufferShader->GetShader(); auto gbuffer = _gBufferShader->GetShader();
// Validate shader constant buffers sizes // Validate shader constant buffers sizes
@@ -228,9 +223,13 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTexture* lightBuffer)
context->ResetRenderTarget(); context->ResetRenderTarget();
} }
bool SortDecal(Decal* const& a, Decal* const& b) bool SortDecal(RenderDecalData const& a, RenderDecalData const& b)
{ {
return a->SortOrder < b->SortOrder; if (a.SortOrder == b.SortOrder)
{
return (uintptr)a.Material < (uintptr)b.Material;
}
return a.SortOrder < b.SortOrder;
} }
void GBufferPass::RenderDebug(RenderContext& renderContext) void GBufferPass::RenderDebug(RenderContext& renderContext)
@@ -428,96 +427,89 @@ void GBufferPass::DrawSky(RenderContext& renderContext, GPUContext* context)
void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* lightBuffer) void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* lightBuffer)
{ {
// Skip if no decals to render
auto& decals = renderContext.List->Decals; auto& decals = renderContext.List->Decals;
if (decals.IsEmpty() || _boxModel == nullptr || !_boxModel->CanBeRendered() || EnumHasNoneFlags(renderContext.View.Flags, ViewFlags::Decals)) const auto boxModel = _boxModel.Get();
if (decals.IsEmpty() || boxModel == nullptr || !boxModel->CanBeRendered() || EnumHasNoneFlags(renderContext.View.Flags, ViewFlags::Decals))
return; return;
PROFILE_GPU_CPU("Decals"); PROFILE_GPU_CPU("Decals");
auto context = GPUDevice::Instance->GetMainContext();
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto model = _boxModel.Get();
auto buffers = renderContext.Buffers; auto buffers = renderContext.Buffers;
// Sort decals from the lowest order to the highest order // Sort decals from the lowest order to the highest order
Sorting::QuickSort(decals.Get(), (int32)decals.Count(), &SortDecal); Sorting::QuickSort(decals.Get(), decals.Count(), &SortDecal);
// TODO: batch decals using the same material
// TODO: sort decals by the blending mode within the same order
// Prepare // Prepare
DrawCall drawCall; DrawCall drawCall;
MaterialBase::BindParameters bindParams(context, renderContext, drawCall); MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
bindParams.BindViewData(); bindParams.BindViewData();
drawCall.Material = nullptr; MaterialDecalBlendingMode decalBlendingMode = (MaterialDecalBlendingMode)-1;
drawCall.WorldDeterminantSign = 1.0f; MaterialUsageFlags usageFlags = (MaterialUsageFlags)-1;
boxModel->LODs.Get()->Meshes.Get()->GetDrawCallGeometry(drawCall);
context->BindVB(ToSpan(drawCall.Geometry.VertexBuffers, 3));
context->BindIB(drawCall.Geometry.IndexBuffer);
context->ResetRenderTarget();
// Draw all decals // Draw all decals
for (int32 i = 0; i < decals.Count(); i++) for (int32 i = 0; i < decals.Count(); i++)
{ {
const auto decal = decals[i]; const RenderDecalData& decal = decals.Get()[i];
ASSERT(decal && decal->Material);
Transform transform = decal->GetTransform();
transform.Scale *= decal->GetSize();
renderContext.View.GetWorldMatrix(transform, drawCall.World);
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = decal->GetSphere().Radius;
context->ResetRenderTarget(); // Bind output (skip if won't change in-between decals)
const MaterialInfo& info = decal.Material->GetInfo();
// Bind output const MaterialUsageFlags infoUsageFlags = info.UsageFlags & (MaterialUsageFlags::UseEmissive | MaterialUsageFlags::UseNormal);
const MaterialInfo& info = decal->Material->GetInfo(); if (decalBlendingMode != info.DecalBlendingMode || usageFlags != infoUsageFlags)
switch (info.DecalBlendingMode)
{ {
case MaterialDecalBlendingMode::Translucent: decalBlendingMode = info.DecalBlendingMode;
{ usageFlags = infoUsageFlags;
GPUTextureView* targetBuffers[4]; switch (decalBlendingMode)
int32 count = 2;
targetBuffers[0] = buffers->GBuffer0->View();
targetBuffers[1] = buffers->GBuffer2->View();
if (EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive))
{ {
count++; case MaterialDecalBlendingMode::Translucent:
targetBuffers[2] = lightBuffer; {
GPUTextureView* targetBuffers[4];
if (EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseNormal)) int32 count = 2;
targetBuffers[0] = buffers->GBuffer0->View();
targetBuffers[1] = buffers->GBuffer2->View();
if (EnumHasAnyFlags(usageFlags, MaterialUsageFlags::UseEmissive))
{ {
count++; count++;
targetBuffers[3] = buffers->GBuffer1->View(); targetBuffers[2] = lightBuffer;
if (EnumHasAnyFlags(usageFlags, MaterialUsageFlags::UseNormal))
{
count++;
targetBuffers[3] = buffers->GBuffer1->View();
}
} }
else if (EnumHasAnyFlags(usageFlags, MaterialUsageFlags::UseNormal))
{
count++;
targetBuffers[2] = buffers->GBuffer1->View();
}
context->SetRenderTarget(nullptr, ToSpan(targetBuffers, count));
break;
} }
else if (EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseNormal)) case MaterialDecalBlendingMode::Stain:
{ {
count++; context->SetRenderTarget(buffers->GBuffer0->View());
targetBuffers[2] = buffers->GBuffer1->View(); break;
}
case MaterialDecalBlendingMode::Normal:
{
context->SetRenderTarget(buffers->GBuffer1->View());
break;
}
case MaterialDecalBlendingMode::Emissive:
{
context->SetRenderTarget(lightBuffer);
break;
}
} }
context->SetRenderTarget(nullptr, ToSpan(targetBuffers, count));
break;
}
case MaterialDecalBlendingMode::Stain:
{
context->SetRenderTarget(buffers->GBuffer0->View());
break;
}
case MaterialDecalBlendingMode::Normal:
{
context->SetRenderTarget(buffers->GBuffer1->View());
break;
}
case MaterialDecalBlendingMode::Emissive:
{
context->SetRenderTarget(lightBuffer);
break;
}
} }
// Draw decal // Draw decal
drawCall.PerInstanceRandom = decal->GetPerInstanceRandom(); drawCall.World = decal.World;
decal->Material->Bind(bindParams); decal.Material->Bind(bindParams);
model->Render(context); // TODO: use hardware instancing
context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, 1, 0, 0, 0);
} }
context->ResetSR(); context->ResetSR();

View File

@@ -106,6 +106,13 @@ struct RenderSkyLightData : RenderLightData
void SetupLightData(ShaderLightData* data, bool useShadow) const; void SetupLightData(ShaderLightData* data, bool useShadow) const;
}; };
struct RenderDecalData
{
Matrix World;
MaterialBase* Material;
int32 SortOrder;
};
/// <summary> /// <summary>
/// The draw calls list types. /// The draw calls list types.
/// </summary> /// </summary>
@@ -288,7 +295,7 @@ public:
/// <summary> /// <summary>
/// Decals registered for the rendering. /// Decals registered for the rendering.
/// </summary> /// </summary>
Array<Decal*> Decals; Array<RenderDecalData> Decals;
/// <summary> /// <summary>
/// Local volumetric fog particles registered for the rendering. /// Local volumetric fog particles registered for the rendering.