Optimize decals rendering
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user