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