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));
auto materialData = reinterpret_cast<DecalMaterialShaderData*>(cb.Get());
cb = Span<byte>(cb.Get() + sizeof(DecalMaterialShaderData), cb.Length() - sizeof(DecalMaterialShaderData));
int32 srv = 0;
const bool isCameraInside = OrientedBoundingBox(Vector3::Half, params.FirstDrawCall->World).Contains(view.Position) == ContainmentType::Contains;
const bool isCameraInside = OrientedBoundingBox(Vector3::Half, drawCall.World).Contains(view.Position) == ContainmentType::Contains;
// Setup parameters
MaterialParameter::BindMeta bindMeta;

View File

@@ -69,20 +69,26 @@ void Decal::OnLayerChanged()
void Decal::Draw(RenderContext& renderContext)
{
MaterialBase* material = Material;
if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Decals) &&
EnumHasAnyFlags(renderContext.View.Pass, DrawPass::GBuffer) &&
Material &&
Material->IsLoaded() &&
Material->IsDecal())
material &&
material->IsLoaded() &&
material->IsDecal())
{
// Check if decal is being culled
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;
// Check if decal is being culled
if (Math::Square(DrawMinScreenSize * 0.5f) > screenRadiusSquared)
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);
bindParams.BindViewData();
drawCall.WorldDeterminantSign = 1.0f;
drawCall.PerInstanceRandom = 0.0f;
context->SetRenderTarget(lightBuffer);
for (int32 i = 0; i < decals.Count(); i++)
{
const auto decal = decals[i];
ASSERT(decal && decal->Material);
Transform transform = decal->GetTransform();
transform.Scale *= decal->GetSize();
renderContext.View.GetWorldMatrix(transform, drawCall.World);
const RenderDecalData& decal = decals.Get()[i];
drawCall.World = decal.World;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.Material = decal->Material;
drawCall.PerInstanceRandom = decal->GetPerInstanceRandom();
drawCall.Material = decal.Material;
decalsWrapper.Bind(bindParams);
boxModel->Render(context);
}

View File

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

View File

@@ -59,13 +59,8 @@ bool GBufferPass::Init()
bool GBufferPass::setupResources()
{
ASSERT(_gBufferShader);
// Check if shader has not been loaded
if (!_gBufferShader->IsLoaded())
{
if (!_gBufferShader || !_gBufferShader->IsLoaded())
return true;
}
auto gbuffer = _gBufferShader->GetShader();
// Validate shader constant buffers sizes
@@ -228,9 +223,13 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTexture* lightBuffer)
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)
@@ -428,96 +427,89 @@ void GBufferPass::DrawSky(RenderContext& renderContext, GPUContext* context)
void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* lightBuffer)
{
// Skip if no decals to render
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;
PROFILE_GPU_CPU("Decals");
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto model = _boxModel.Get();
auto context = GPUDevice::Instance->GetMainContext();
auto buffers = renderContext.Buffers;
// Sort decals from the lowest order to the highest order
Sorting::QuickSort(decals.Get(), (int32)decals.Count(), &SortDecal);
// TODO: batch decals using the same material
// TODO: sort decals by the blending mode within the same order
Sorting::QuickSort(decals.Get(), decals.Count(), &SortDecal);
// Prepare
DrawCall drawCall;
MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
bindParams.BindViewData();
drawCall.Material = nullptr;
drawCall.WorldDeterminantSign = 1.0f;
MaterialDecalBlendingMode decalBlendingMode = (MaterialDecalBlendingMode)-1;
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
for (int32 i = 0; i < decals.Count(); i++)
{
const auto decal = decals[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;
const RenderDecalData& decal = decals.Get()[i];
context->ResetRenderTarget();
// Bind output
const MaterialInfo& info = decal->Material->GetInfo();
switch (info.DecalBlendingMode)
// Bind output (skip if won't change in-between decals)
const MaterialInfo& info = decal.Material->GetInfo();
const MaterialUsageFlags infoUsageFlags = info.UsageFlags & (MaterialUsageFlags::UseEmissive | MaterialUsageFlags::UseNormal);
if (decalBlendingMode != info.DecalBlendingMode || usageFlags != infoUsageFlags)
{
case MaterialDecalBlendingMode::Translucent:
{
GPUTextureView* targetBuffers[4];
int32 count = 2;
targetBuffers[0] = buffers->GBuffer0->View();
targetBuffers[1] = buffers->GBuffer2->View();
if (EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive))
decalBlendingMode = info.DecalBlendingMode;
usageFlags = infoUsageFlags;
switch (decalBlendingMode)
{
count++;
targetBuffers[2] = lightBuffer;
if (EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseNormal))
case MaterialDecalBlendingMode::Translucent:
{
GPUTextureView* targetBuffers[4];
int32 count = 2;
targetBuffers[0] = buffers->GBuffer0->View();
targetBuffers[1] = buffers->GBuffer2->View();
if (EnumHasAnyFlags(usageFlags, MaterialUsageFlags::UseEmissive))
{
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++;
targetBuffers[2] = buffers->GBuffer1->View();
context->SetRenderTarget(buffers->GBuffer0->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
drawCall.PerInstanceRandom = decal->GetPerInstanceRandom();
decal->Material->Bind(bindParams);
model->Render(context);
drawCall.World = decal.World;
decal.Material->Bind(bindParams);
// TODO: use hardware instancing
context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, 1, 0, 0, 0);
}
context->ResetSR();

View File

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