Optimize shadow projections rendering contexts to collect draw call indices only (draw calls from main render list)

This commit is contained in:
Wojtek Figat
2022-11-03 19:59:56 +01:00
parent 996d38b61d
commit 183636289a
7 changed files with 128 additions and 46 deletions

View File

@@ -411,7 +411,7 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
if (drawModes == DrawPass::None)
return;
// Submit draw call
// Setup draw call
DrawCall drawCall;
drawCall.Geometry.IndexBuffer = _indexBuffer;
drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0];
@@ -434,6 +434,8 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
drawCall.Surface.LODDitherFactor = 0.0f;
drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = perInstanceRandom;
// Push draw call to the render list
renderContext.List->AddDrawCall(drawModes, flags, drawCall, receiveDecals);
}
@@ -454,14 +456,14 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
material = GPUDevice::Instance->GetDefaultMaterial();
if (!material || !material->IsSurface())
return;
// Check if skip rendering
const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode);
const auto drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)material->GetDrawModes());
if (drawModes == DrawPass::None)
return;
// Submit draw call
// Setup draw call
DrawCall drawCall;
drawCall.Geometry.IndexBuffer = _indexBuffer;
drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0];
@@ -493,6 +495,8 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
// Push draw call to the render list
renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals);
}
@@ -514,7 +518,7 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
if (!material || !material->IsSurface())
return;
// Submit draw call
// Setup draw call
DrawCall drawCall;
drawCall.Geometry.IndexBuffer = _indexBuffer;
drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0];
@@ -547,17 +551,10 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
// Push draw call to every context
// Push draw call to the render lists
const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode);
const auto materialDrawModes = material->GetDrawModes();
for (const RenderContext& renderContext : renderContextBatch.Contexts)
{
const DrawPass drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)materialDrawModes);
if (drawModes != DrawPass::None && renderContext.View.CullingFrustum.Intersects(info.Bounds))
{
renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals);
}
}
const DrawPass drawModes = (DrawPass)(info.DrawModes & material->GetDrawModes());
renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, drawModes, info.Flags, shadowsMode, info.Bounds, drawCall, entry.ReceiveDecals);
}
bool Mesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const

View File

@@ -174,7 +174,7 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
if (drawModes == DrawPass::None)
return;
// Submit draw call
// Setup draw call
DrawCall drawCall;
drawCall.Geometry.IndexBuffer = _indexBuffer;
BlendShapesInstance::MeshInstance* blendShapeMeshInstance;
@@ -211,6 +211,8 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
// Push draw call to the render list
renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals);
}
@@ -232,7 +234,7 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
if (!material || !material->IsSurface())
return;
// Submit draw call
// Setup draw call
DrawCall drawCall;
drawCall.Geometry.IndexBuffer = _indexBuffer;
BlendShapesInstance::MeshInstance* blendShapeMeshInstance;
@@ -270,17 +272,10 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
// Push draw call to every context
// Push draw call to the render lists
const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode);
const auto materialDrawModes = material->GetDrawModes();
for (const RenderContext& renderContext : renderContextBatch.Contexts)
{
const DrawPass drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)materialDrawModes);
if (drawModes != DrawPass::None && renderContext.View.CullingFrustum.Intersects(info.Bounds))
{
renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals);
}
}
const DrawPass drawModes = (DrawPass)(info.DrawModes & material->GetDrawModes());
renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, drawModes, StaticFlags::None, shadowsMode, info.Bounds, drawCall, entry.ReceiveDecals);
}
bool SkinnedMesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const

View File

@@ -84,7 +84,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
else if (origin.IsZero() && frustumsCount == 1)
{
// Fast path for no origin shifting with a single context
auto& renderContext = renderContextBatch.Contexts[0];
RenderContext& renderContext = renderContextBatch.GetMainContext();
for (int32 i = 0; i < list.Count(); i++)
{
auto e = list.Get()[i];

View File

@@ -391,6 +391,7 @@ void RenderList::Clear()
BatchedDrawCalls.Clear();
for (auto& list : DrawCallsLists)
list.Clear();
ShadowDepthDrawCallsList.Clear();
PointLights.Clear();
SpotLights.Clear();
SkyLights.Clear();
@@ -445,6 +446,60 @@ void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCa
}
}
void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals)
{
#if ENABLE_ASSERTION_LOW_LAYERS
// Ensure that draw modes are non-empty and in conjunction with material draw modes
auto materialDrawModes = drawCall.Material->GetDrawModes();
ASSERT_LOW_LAYER(drawModes != DrawPass::None && ((uint32)drawModes & ~(uint32)materialDrawModes) == 0);
#endif
// Append draw call data
const int32 index = DrawCalls.Count();
DrawCalls.Add(drawCall);
// Add draw call to proper draw lists
const RenderContext& mainRenderContext = renderContextBatch.Contexts.Get()[0];
DrawPass modes = (DrawPass)(drawModes & mainRenderContext.View.GetShadowsDrawPassMask(shadowsMode));
drawModes = (DrawPass)(modes & mainRenderContext.View.Pass);
if (drawModes != DrawPass::None && mainRenderContext.View.CullingFrustum.Intersects(bounds))
{
if (drawModes & DrawPass::Depth)
{
DrawCallsLists[(int32)DrawCallsListType::Depth].Indices.Add(index);
}
if (drawModes & (DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas))
{
if (receivesDecals)
DrawCallsLists[(int32)DrawCallsListType::GBuffer].Indices.Add(index);
else
DrawCallsLists[(int32)DrawCallsListType::GBufferNoDecals].Indices.Add(index);
}
if (drawModes & DrawPass::Forward)
{
DrawCallsLists[(int32)DrawCallsListType::Forward].Indices.Add(index);
}
if (drawModes & DrawPass::Distortion)
{
DrawCallsLists[(int32)DrawCallsListType::Distortion].Indices.Add(index);
}
if (drawModes & DrawPass::MotionVectors && (staticFlags & StaticFlags::Transform) == 0)
{
DrawCallsLists[(int32)DrawCallsListType::MotionVectors].Indices.Add(index);
}
}
for (int32 i = 1; i < renderContextBatch.Contexts.Count(); i++)
{
const RenderContext& renderContext = renderContextBatch.Contexts.Get()[i];
ASSERT_LOW_LAYER(renderContext.View.Pass == DrawPass::Depth);
drawModes = (DrawPass)(modes & renderContext.View.Pass);
if (drawModes != DrawPass::None && renderContext.View.CullingFrustum.Intersects(bounds))
{
renderContext.List->ShadowDepthDrawCallsList.Indices.Add(index);
}
}
}
namespace
{
/// <summary>
@@ -466,7 +521,7 @@ namespace
}
}
void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list)
void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const DrawCall* drawCalls)
{
PROFILE_CPU();
@@ -486,7 +541,7 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
const uint32 sortKeyXor = reverseDistance ? MAX_uint32 : 0;
for (int32 i = 0; i < listSize; i++)
{
auto& drawCall = DrawCalls[list.Indices[i]];
const DrawCall& drawCall = drawCalls[list.Indices[i]];
const float distance = Float3::Dot(planeNormal, drawCall.ObjectPosition) - planePoint;
const uint32 sortKey = RenderTools::ComputeDistanceSortKey(distance) ^ sortKeyXor;
int32 batchKey = GetHash(drawCall.Geometry.IndexBuffer);
@@ -516,14 +571,14 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
list.Batches.Clear();
for (int32 i = 0; i < listSize;)
{
const auto& drawCall = DrawCalls[list.Indices[i]];
const DrawCall& drawCall = drawCalls[list.Indices[i]];
int32 batchSize = 1;
int32 instanceCount = drawCall.InstanceCount;
// Check the following draw calls to merge them (using instancing)
for (int32 j = i + 1; j < listSize; j++)
{
const auto& other = DrawCalls[list.Indices[j]];
const DrawCall& other = drawCalls[list.Indices[j]];
if (!CanBatchWith(drawCall, other))
break;
@@ -550,7 +605,7 @@ bool CanUseInstancing(DrawPass pass)
return pass == DrawPass::GBuffer || pass == DrawPass::Depth;
}
void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, GPUTextureView* input)
void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, const DrawCall* drawCalls, GPUTextureView* input)
{
if (list.IsEmpty())
return;
@@ -595,10 +650,10 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
if (batch.BatchSize > 1)
{
IMaterial::InstancingHandler handler;
DrawCalls[list.Indices[batch.StartIndex]].Material->CanUseInstancing(handler);
drawCalls[list.Indices[batch.StartIndex]].Material->CanUseInstancing(handler);
for (int32 j = 0; j < batch.BatchSize; j++)
{
auto& drawCall = DrawCalls[list.Indices[batch.StartIndex + j]];
auto& drawCall = drawCalls[list.Indices[batch.StartIndex + j]];
handler.WriteDrawCall(instanceData, drawCall);
instanceData++;
}
@@ -631,7 +686,7 @@ DRAW:
for (int32 i = 0; i < list.Batches.Count(); i++)
{
auto& batch = list.Batches[i];
auto& drawCall = DrawCalls[list.Indices[batch.StartIndex]];
const DrawCall& drawCall = drawCalls[list.Indices[batch.StartIndex]];
int32 vbCount = 0;
while (vbCount < ARRAY_COUNT(drawCall.Geometry.VertexBuffers) && drawCall.Geometry.VertexBuffers[vbCount])
@@ -738,7 +793,7 @@ DRAW:
for (int32 j = 0; j < batch.BatchSize; j++)
{
auto& drawCall = DrawCalls[list.Indices[batch.StartIndex + j]];
const DrawCall& drawCall = drawCalls[list.Indices[batch.StartIndex + j]];
bindParams.FirstDrawCall = &drawCall;
drawCall.Material->Bind(bindParams);
@@ -785,7 +840,7 @@ DRAW:
// Draw calls list has nto been batched so execute draw calls separately
for (int32 j = 0; j < list.Indices.Count(); j++)
{
auto& drawCall = DrawCalls[list.Indices[j]];
const DrawCall& drawCall = drawCalls[list.Indices[j]];
bindParams.FirstDrawCall = &drawCall;
drawCall.Material->Bind(bindParams);

View File

@@ -18,6 +18,7 @@ class LightWithShadow;
class IPostFxSettingsProvider;
class CubeTexture;
struct RenderContext;
struct RenderContextBatch;
struct RendererDirectionalLightData
{
@@ -301,6 +302,11 @@ public:
/// </summary>
DrawCallsList DrawCallsLists[(int32)DrawCallsListType::MAX];
/// <summary>
/// The additional draw calls list for Depth drawing into Shadow Projections that use DrawCalls from main render context. This assumes that RenderContextBatch contains main context and shadow projections only.
/// </summary>
DrawCallsList ShadowDepthDrawCallsList;
/// <summary>
/// Light pass members - directional lights
/// </summary>
@@ -477,6 +483,18 @@ public:
/// <param name="receivesDecals">True if the rendered mesh can receive decals.</param>
void AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals);
/// <summary>
/// Adds the draw call to the draw lists and references it in other render contexts. Performs additional per-context frustum culling.
/// </summary>
/// <param name="renderContextBatch">The rendering context batch. This assumes that RenderContextBatch contains main context and shadow projections only.</param>
/// <param name="drawModes">The object draw modes.</param>
/// <param name="staticFlags">The object static flags.</param>
/// <param name="shadowsMode">The object shadows casting mode.</param>
/// <param name="bounds">The object bounds.</param>
/// <param name="drawCall">The draw call data.</param>
/// <param name="receivesDecals">True if the rendered mesh can receive decals.</param>
void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals);
/// <summary>
/// Sorts the collected draw calls list.
/// </summary>
@@ -485,7 +503,7 @@ public:
/// <param name="listType">The collected draw calls list type.</param>
API_FUNCTION() FORCE_INLINE void SortDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, bool reverseDistance, DrawCallsListType listType)
{
SortDrawCalls(renderContext, reverseDistance, DrawCallsLists[(int32)listType]);
SortDrawCalls(renderContext, reverseDistance, DrawCallsLists[(int32)listType], DrawCalls.Get());
}
/// <summary>
@@ -493,8 +511,9 @@ public:
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="reverseDistance">If set to <c>true</c> reverse draw call distance to the view. Results in back to front sorting.</param>
/// <param name="list">The collected draw calls list.</param>
void SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list);
/// <param name="list">The collected draw calls indices list.</param>
/// <param name="drawCalls">The collected draw calls list.</param>
void SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const DrawCall* drawCalls);
/// <summary>
/// Executes the collected draw calls.
@@ -504,16 +523,28 @@ public:
/// <param name="input">The input scene color. It's optional and used in forward/postFx rendering.</param>
API_FUNCTION() FORCE_INLINE void ExecuteDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, DrawCallsListType listType, GPUTextureView* input = nullptr)
{
ExecuteDrawCalls(renderContext, DrawCallsLists[(int32)listType], input);
ExecuteDrawCalls(renderContext, DrawCallsLists[(int32)listType], DrawCalls.Get(), input);
}
/// <summary>
/// Executes the collected draw calls.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="list">The collected draw calls list.</param>
/// <param name="list">The collected draw calls indices list.</param>
/// <param name="input">The input scene color. It's optional and used in forward/postFx rendering.</param>
void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, GPUTextureView* input = nullptr);
FORCE_INLINE void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, GPUTextureView* input = nullptr)
{
ExecuteDrawCalls(renderContext, list, DrawCalls.Get(), input);
}
/// <summary>
/// Executes the collected draw calls.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="list">The collected draw calls indices list.</param>
/// <param name="drawCalls">The collected draw calls list.</param>
/// <param name="input">The input scene color. It's optional and used in forward/postFx rendering.</param>
void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, const DrawCall* drawCalls, GPUTextureView* input);
};
/// <summary>

View File

@@ -355,6 +355,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
{
auto& shadowContext = renderContextBatch.Contexts[i];
shadowContext.List->SortDrawCalls(shadowContext, false, DrawCallsListType::Depth);
shadowContext.List->SortDrawCalls(shadowContext, false, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls.Get());
}
}

View File

@@ -609,7 +609,7 @@ void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererP
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
RenderContext& renderContext = renderContextBatch.GetMainContext();
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float sphereModelScale = 3.0f;
auto& view = renderContext.View;
@@ -631,6 +631,7 @@ void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererP
context->ClearDepth(rt);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
shadowContext.List->ExecuteDrawCalls(shadowContext, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls.Get(), nullptr);
}
// Restore GPU context
@@ -686,7 +687,7 @@ void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererS
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
RenderContext& renderContext = renderContextBatch.GetMainContext();
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float sphereModelScale = 3.0f;
auto& view = renderContext.View;
@@ -708,6 +709,7 @@ void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererS
context->ClearDepth(rt);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
shadowContext.List->ExecuteDrawCalls(shadowContext, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls.Get(), nullptr);
}
// Restore GPU context
@@ -763,7 +765,7 @@ void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererD
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
RenderContext& renderContext = renderContextBatch.GetMainContext();
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float shadowMapsSizeCSM = (float)_shadowMapsSizeCSM;
context->SetViewportAndScissors(shadowMapsSizeCSM, shadowMapsSizeCSM);
@@ -777,6 +779,7 @@ void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererD
context->ClearDepth(rt);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + cascadeIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
shadowContext.List->ExecuteDrawCalls(shadowContext, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls.Get(), nullptr);
}
// Restore GPU context