Optimize Particles drawing to use a whole RenderContextBatch

This commit is contained in:
Wojtek Figat
2025-08-07 09:27:28 +02:00
parent 959371a995
commit 3ffb067e55
5 changed files with 110 additions and 70 deletions

View File

@@ -133,7 +133,6 @@ void GPUParticles::CopyParticlesCount(GPUContext* context, ParticleEmitter* emit
void GPUParticles::Execute(GPUContext* context, ParticleEmitter* emitter, ParticleEffect* effect, int32 emitterIndex, ParticleEmitterInstance& data) void GPUParticles::Execute(GPUContext* context, ParticleEmitter* emitter, ParticleEffect* effect, int32 emitterIndex, ParticleEmitterInstance& data)
{ {
PROFILE_CPU_ASSET(emitter); PROFILE_CPU_ASSET(emitter);
PROFILE_GPU("GPUParticles");
ASSERT(emitter->Graph.Version == data.Version); ASSERT(emitter->Graph.Version == data.Version);
ASSERT(emitter->Graph.Version == data.Buffer->Version); ASSERT(emitter->Graph.Version == data.Buffer->Version);
uint32 counterDefaultValue = 0; uint32 counterDefaultValue = 0;

View File

@@ -576,8 +576,21 @@ void ParticleEffect::Draw(RenderContext& renderContext)
{ {
if (renderContext.View.Pass == DrawPass::GlobalSDF || renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas) if (renderContext.View.Pass == DrawPass::GlobalSDF || renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
return; return;
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(GetPosition(), renderContext.View.Position)); _lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(GetPosition(), renderContext.View.WorldPosition));
Particles::DrawParticles(renderContext, this); RenderContextBatch renderContextBatch(renderContext);
Particles::DrawParticles(renderContextBatch, this);
}
void ParticleEffect::Draw(RenderContextBatch& renderContextBatch)
{
Particles::DrawParticles(renderContextBatch, this);
// Cull again against the main context (if using multiple ones) to skip caching draw distance from shadow projections
const RenderView& mainView = renderContextBatch.GetMainContext().View;
const BoundingSphere bounds(_sphere.Center - mainView.Origin, _sphere.Radius);
if (renderContextBatch.Contexts.Count() > 1 && !mainView.CullingFrustum.Intersects(bounds))
return;
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(bounds.Center, mainView.Position));
} }
#if USE_EDITOR #if USE_EDITOR

View File

@@ -404,6 +404,7 @@ public:
// [Actor] // [Actor]
bool HasContentLoaded() const override; bool HasContentLoaded() const override;
void Draw(RenderContext& renderContext) override; void Draw(RenderContext& renderContext) override;
void Draw(RenderContextBatch& renderContextBatch) override;
#if USE_EDITOR #if USE_EDITOR
void OnDebugDrawSelected() override; void OnDebugDrawSelected() override;
void OnDebugDraw() override; void OnDebugDraw() override;

View File

@@ -178,9 +178,7 @@ void Particles::OnEffectDestroy(ParticleEffect* effect)
#endif #endif
} }
typedef Array<uint8, FixedAllocation<PARTICLE_EMITTER_MAX_MODULES>> RenderModulesIndices; void DrawEmitterCPU(RenderContextBatch& renderContextBatch, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, const BoundingSphere& bounds, uint32 renderModulesIndices, int8 sortOrder)
void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int8 sortOrder)
{ {
// Skip if CPU buffer is empty // Skip if CPU buffer is empty
if (buffer->CPU.Count == 0) if (buffer->CPU.Count == 0)
@@ -189,7 +187,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
auto emitter = buffer->Emitter; auto emitter = buffer->Emitter;
// Check if need to perform any particles sorting // Check if need to perform any particles sorting
if (emitter->Graph.SortModules.HasItems() && renderContext.View.Pass != DrawPass::Depth && (buffer->CPU.Count != 0 || buffer->GPU.SortedIndices)) if (emitter->Graph.SortModules.HasItems() && EnumHasAnyFlags(drawModes, DrawPass::Forward) && (buffer->CPU.Count != 0 || buffer->GPU.SortedIndices))
{ {
// Prepare sorting data // Prepare sorting data
if (!buffer->GPU.SortedIndices) if (!buffer->GPU.SortedIndices)
@@ -210,10 +208,11 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
if (listSize < 500) if (listSize < 500)
{ {
// Use fast stack allocator from RenderList // Use fast stack allocator from RenderList
sortingKeys[0] = renderContext.List->Memory.Allocate<uint32>(listSize); auto& memory = renderContextBatch.GetMainContext().List->Memory;
sortingKeys[1] = renderContext.List->Memory.Allocate<uint32>(listSize); sortingKeys[0] = memory.Allocate<uint32>(listSize);
sortingIndices[0] = renderContext.List->Memory.Allocate<int32>(listSize); sortingKeys[1] = memory.Allocate<uint32>(listSize);
sortingIndices[1] = renderContext.List->Memory.Allocate<int32>(listSize); sortingIndices[0] = memory.Allocate<int32>(listSize);
sortingIndices[1] = memory.Allocate<int32>(listSize);
} }
else else
{ {
@@ -236,7 +235,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
const int32 positionOffset = emitter->Graph.GetPositionAttributeOffset(); const int32 positionOffset = emitter->Graph.GetPositionAttributeOffset();
if (positionOffset == -1) if (positionOffset == -1)
break; break;
const Matrix viewProjection = renderContext.View.ViewProjection(); const Matrix viewProjection = renderContextBatch.GetMainContext().View.ViewProjection();
const byte* positionPtr = buffer->CPU.Buffer.Get() + positionOffset; const byte* positionPtr = buffer->CPU.Buffer.Get() + positionOffset;
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local) if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
{ {
@@ -262,7 +261,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
const int32 positionOffset = emitter->Graph.GetPositionAttributeOffset(); const int32 positionOffset = emitter->Graph.GetPositionAttributeOffset();
if (positionOffset == -1) if (positionOffset == -1)
break; break;
const Float3 viewPosition = renderContext.View.Position; const Float3 viewPosition = renderContextBatch.GetMainContext().View.Position;
const byte* positionPtr = buffer->CPU.Buffer.Get() + positionOffset; const byte* positionPtr = buffer->CPU.Buffer.Get() + positionOffset;
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local) if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
{ {
@@ -356,9 +355,10 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
auto& vertexBuffer = buffer->GPU.RibbonVertexBufferDynamic->Data; auto& vertexBuffer = buffer->GPU.RibbonVertexBufferDynamic->Data;
// Setup all ribbon modules // Setup all ribbon modules
for (int32 index = 0; index < renderModulesIndices.Count(); index++) for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count(); moduleIndex++)
{ {
const int32 moduleIndex = renderModulesIndices[index]; if ((renderModulesIndices & (1u << moduleIndex)) == 0)
continue;
auto module = emitter->Graph.RenderModules[moduleIndex]; auto module = emitter->Graph.RenderModules[moduleIndex];
if (module->TypeID != 404 || ribbonModuleIndex >= PARTICLE_EMITTER_MAX_RIBBONS) if (module->TypeID != 404 || ribbonModuleIndex >= PARTICLE_EMITTER_MAX_RIBBONS)
continue; continue;
@@ -454,7 +454,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Setup ribbon data // Setup ribbon data
ribbonModulesSegmentCount[ribbonModuleIndex] = segmentCount; ribbonModulesSegmentCount[ribbonModuleIndex] = segmentCount;
ribbonModulesDrawIndicesCount[index] = indices; ribbonModulesDrawIndicesCount[ribbonModuleIndex] = indices;
ribbonModulesDrawIndicesPos += indices; ribbonModulesDrawIndicesPos += indices;
ribbonModuleIndex++; ribbonModuleIndex++;
@@ -472,9 +472,10 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Execute all rendering modules // Execute all rendering modules
ribbonModuleIndex = 0; ribbonModuleIndex = 0;
for (int32 index = 0; index < renderModulesIndices.Count(); index++) for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count(); moduleIndex++)
{ {
const int32 moduleIndex = renderModulesIndices[index]; if ((renderModulesIndices & (1u << moduleIndex)) == 0)
continue;
auto module = emitter->Graph.RenderModules[moduleIndex]; auto module = emitter->Graph.RenderModules[moduleIndex];
drawCall.Particle.Module = module; drawCall.Particle.Module = module;
@@ -493,7 +494,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Submit draw call // Submit draw call
SpriteRenderer.SetupDrawCall(drawCall); SpriteRenderer.SetupDrawCall(drawCall);
drawCall.InstanceCount = buffer->CPU.Count; drawCall.InstanceCount = buffer->CPU.Count;
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder); renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, dp, staticFlags, ShadowsCastingMode::DynamicOnly, bounds, drawCall, false, sortOrder);
break; break;
} }
@@ -521,7 +522,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Submit draw call // Submit draw call
mesh.GetDrawCallGeometry(drawCall); mesh.GetDrawCallGeometry(drawCall);
drawCall.InstanceCount = buffer->CPU.Count; drawCall.InstanceCount = buffer->CPU.Count;
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder); renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, dp, staticFlags, ShadowsCastingMode::DynamicOnly, bounds, drawCall, false, sortOrder);
} }
break; break;
@@ -580,7 +581,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.Draw.StartIndex = ribbonModulesDrawIndicesStart[ribbonModuleIndex]; drawCall.Draw.StartIndex = ribbonModulesDrawIndicesStart[ribbonModuleIndex];
drawCall.Draw.IndicesCount = ribbonModulesDrawIndicesCount[ribbonModuleIndex]; drawCall.Draw.IndicesCount = ribbonModulesDrawIndicesCount[ribbonModuleIndex];
drawCall.InstanceCount = 1; drawCall.InstanceCount = 1;
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder); renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, dp, staticFlags, ShadowsCastingMode::DynamicOnly, bounds, drawCall, false, sortOrder);
ribbonModuleIndex++; ribbonModuleIndex++;
@@ -610,7 +611,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
Float3::Transform(drawCall.Particle.VolumetricFog.Position, drawCall.World, drawCall.Particle.VolumetricFog.Position); Float3::Transform(drawCall.Particle.VolumetricFog.Position, drawCall.World, drawCall.Particle.VolumetricFog.Position);
drawCall.Particle.VolumetricFog.Radius = hasRadius ? radiusData[i] : 100.0f; drawCall.Particle.VolumetricFog.Radius = hasRadius ? radiusData[i] : 100.0f;
drawCall.Particle.VolumetricFog.ParticleIndex = i; drawCall.Particle.VolumetricFog.ParticleIndex = i;
renderContext.List->VolumetricFogParticles.Add(drawCall); renderContextBatch.GetMainContext().List->VolumetricFogParticles.Add(drawCall);
} }
break; break;
} }
@@ -649,7 +650,7 @@ void CleanupGPUParticlesSorting()
GPUParticlesSorting = nullptr; GPUParticlesSorting = nullptr;
} }
void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, const RenderModulesIndices& renderModulesIndices, int8 sortOrder) void DrawEmitterGPU(RenderContextBatch& renderContextBatch, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, const BoundingSphere& bounds, uint32 renderModulesIndices, int8 sortOrder)
{ {
if (!IsInMainThread()) if (!IsInMainThread())
{ {
@@ -659,9 +660,9 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// When rendering in async, delay GPU particles drawing to be in sync by moving drawing into delayed callback post scene drawing to use GPUContext safely // When rendering in async, delay GPU particles drawing to be in sync by moving drawing into delayed callback post scene drawing to use GPUContext safely
// Move drawing into delayed callback post scene drawing to use GPUContext safely // Move drawing into delayed callback post scene drawing to use GPUContext safely
renderContext.List->AddDelayedDraw([buffer, drawCallCopy, drawModes, staticFlags, renderModulesIndices, sortOrder](RenderContext& renderContext) renderContextBatch.GetMainContext().List->AddDelayedDraw([&renderContextBatch, buffer, drawCallCopy, drawModes, staticFlags, bounds, renderModulesIndices, sortOrder](RenderContext& renderContext)
{ {
DrawEmitterGPU(renderContext, buffer, *(DrawCall*)drawCallCopy, drawModes, staticFlags, renderModulesIndices, sortOrder); DrawEmitterGPU(renderContextBatch, buffer, *(DrawCall*)drawCallCopy, drawModes, staticFlags, bounds, renderModulesIndices, sortOrder);
}); });
return; return;
} }
@@ -669,7 +670,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
auto emitter = buffer->Emitter; auto emitter = buffer->Emitter;
// Check if need to perform any particles sorting // Check if need to perform any particles sorting
if (emitter->Graph.SortModules.HasItems() && renderContext.View.Pass != DrawPass::Depth && buffer->GPU.ParticlesCountMax != 0) if (emitter->Graph.SortModules.HasItems() && renderContextBatch.GetMainContext().View.Pass != DrawPass::Depth && buffer->GPU.ParticlesCountMax != 0)
{ {
PROFILE_GPU_CPU_NAMED("Sort Particles"); PROFILE_GPU_CPU_NAMED("Sort Particles");
@@ -720,7 +721,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
permutationIndex = 0; permutationIndex = 0;
sortAscending = false; sortAscending = false;
data.PositionOffset = emitter->Graph.GetPositionAttributeOffset(); data.PositionOffset = emitter->Graph.GetPositionAttributeOffset();
const Matrix viewProjection = renderContext.View.ViewProjection(); const Matrix viewProjection = renderContextBatch.GetMainContext().View.ViewProjection();
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local) if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
{ {
Matrix matrix; Matrix matrix;
@@ -738,7 +739,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
permutationIndex = 1; permutationIndex = 1;
sortAscending = false; sortAscending = false;
data.PositionOffset = emitter->Graph.GetPositionAttributeOffset(); data.PositionOffset = emitter->Graph.GetPositionAttributeOffset();
data.ViewPosition = renderContext.View.Position; data.ViewPosition = renderContextBatch.GetMainContext().View.Position;
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local) if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
{ {
Matrix::Transpose(drawCall.World, data.PositionTransform); Matrix::Transpose(drawCall.World, data.PositionTransform);
@@ -780,9 +781,10 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Count draw calls to perform during this emitter rendering // Count draw calls to perform during this emitter rendering
int32 drawCalls = 0; int32 drawCalls = 0;
for (int32 index = 0; index < renderModulesIndices.Count(); index++) for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count(); moduleIndex++)
{ {
int32 moduleIndex = renderModulesIndices.Get()[index]; if ((renderModulesIndices & (1u << moduleIndex)) == 0)
continue;
auto module = emitter->Graph.RenderModules.Get()[moduleIndex]; auto module = emitter->Graph.RenderModules.Get()[moduleIndex];
switch (module->TypeID) switch (module->TypeID)
{ {
@@ -835,9 +837,10 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Execute all rendering modules using indirect draw arguments // Execute all rendering modules using indirect draw arguments
int32 indirectDrawCallIndex = 0; int32 indirectDrawCallIndex = 0;
for (int32 index = 0; index < renderModulesIndices.Count(); index++) for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count(); moduleIndex++)
{ {
int32 moduleIndex = renderModulesIndices.Get()[index]; if ((renderModulesIndices & (1u << moduleIndex)) == 0)
continue;
auto module = emitter->Graph.RenderModules.Get()[moduleIndex]; auto module = emitter->Graph.RenderModules.Get()[moduleIndex];
drawCall.Particle.Module = module; drawCall.Particle.Module = module;
switch (module->TypeID) switch (module->TypeID)
@@ -863,7 +866,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.InstanceCount = 0; drawCall.InstanceCount = 0;
drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer; drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer;
drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs); drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs);
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder); renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, dp, staticFlags, ShadowsCastingMode::DynamicOnly, bounds, drawCall, false, sortOrder);
indirectDrawCallIndex++; indirectDrawCallIndex++;
break; break;
} }
@@ -897,7 +900,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.InstanceCount = 0; drawCall.InstanceCount = 0;
drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer; drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer;
drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs); drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs);
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder); renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, dp, staticFlags, ShadowsCastingMode::DynamicOnly, bounds, drawCall, false, sortOrder);
indirectDrawCallIndex++; indirectDrawCallIndex++;
} }
break; break;
@@ -920,43 +923,65 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
#endif #endif
void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effect) void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEffect* effect)
{ {
// Setup PROFILE_CPU();
auto& view = renderContext.View;
const DrawPass drawModes = view.Pass & effect->DrawModes;
if (drawModes == DrawPass::None)
return;
PROFILE_MEM(Particles); PROFILE_MEM(Particles);
// Drawing assumes that all views within a batch have the same Origin
const Vector3& viewOrigin = renderContextBatch.GetMainContext().View.Origin;
BoundingSphere bounds = effect->GetSphere();
bounds.Center -= viewOrigin;
// Cull particles against all views
uint64 viewsMask = 0;
ASSERT_LOW_LAYER(renderContextBatch.Contexts.Count() <= 64);
DrawPass viewsDrawModes = DrawPass::None;
for (int32 i = 0; i < renderContextBatch.Contexts.Count(); i++)
{
const RenderView& view = renderContextBatch.Contexts.Get()[i].View;
const bool visible = (view.Pass & effect->DrawModes) != DrawPass::None && (view.IsCullingDisabled || view.CullingFrustum.Intersects(bounds));
if (visible)
viewsMask |= 1ull << (uint64)i;
viewsDrawModes |= view.Pass;
}
if (viewsMask == 0)
return;
viewsDrawModes &= effect->DrawModes;
// Setup
ConcurrentSystemLocker::ReadScope systemScope(SystemLocker); ConcurrentSystemLocker::ReadScope systemScope(SystemLocker);
Matrix worlds[2]; Matrix worlds[2];
Matrix::Translation(-renderContext.View.Origin, worlds[0]); // World Matrix::Translation(-viewOrigin, worlds[0]); // World
renderContext.View.GetWorldMatrix(effect->GetTransform(), worlds[1]); // Local renderContextBatch.GetMainContext().View.GetWorldMatrix(effect->GetTransform(), worlds[1]); // Local
float worldDeterminantSigns[2]; float worldDeterminantSigns[2];
worldDeterminantSigns[0] = Math::FloatSelect(worlds[0].RotDeterminant(), 1, -1); worldDeterminantSigns[0] = Math::FloatSelect(worlds[0].RotDeterminant(), 1, -1);
worldDeterminantSigns[1] = Math::FloatSelect(worlds[1].RotDeterminant(), 1, -1); worldDeterminantSigns[1] = Math::FloatSelect(worlds[1].RotDeterminant(), 1, -1);
const StaticFlags staticFlags = effect->GetStaticFlags(); const StaticFlags staticFlags = effect->GetStaticFlags();
const int8 sortOrder = effect->SortOrder; const int8 sortOrder = effect->SortOrder;
// Draw lights // Draw lights (only to into the main view)
for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++) if ((viewsMask & 1) == 1 && renderContextBatch.GetMainContext().View.Pass != DrawPass::Depth)
{ {
auto& emitterData = effect->Instance.Emitters[emitterIndex]; for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++)
const auto buffer = emitterData.Buffer; {
if (!buffer || (buffer->Mode == ParticlesSimulationMode::CPU && buffer->CPU.Count == 0)) auto& emitterData = effect->Instance.Emitters[emitterIndex];
continue; const auto buffer = emitterData.Buffer;
auto emitter = buffer->Emitter; if (!buffer || (buffer->Mode == ParticlesSimulationMode::CPU && buffer->CPU.Count == 0))
if (!emitter || !emitter->IsLoaded()) continue;
continue; auto emitter = buffer->Emitter;
if (!emitter || !emitter->IsLoaded())
continue;
buffer->Emitter->GraphExecutorCPU.Draw(buffer->Emitter, effect, emitterData, renderContext, worlds[(int32)emitter->SimulationSpace]); buffer->Emitter->GraphExecutorCPU.Draw(buffer->Emitter, effect, emitterData, renderContextBatch.GetMainContext(), worlds[(int32)emitter->SimulationSpace]);
}
} }
// Setup a draw call common data // Setup a draw call common data
DrawCall drawCall; DrawCall drawCall;
drawCall.PerInstanceRandom = effect->GetPerInstanceRandom(); drawCall.PerInstanceRandom = effect->GetPerInstanceRandom();
drawCall.ObjectPosition = effect->GetSphere().Center - view.Origin; drawCall.ObjectPosition = bounds.Center;
drawCall.ObjectRadius = (float)effect->GetSphere().Radius; drawCall.ObjectRadius = (float)bounds.Radius;
// Draw all emitters // Draw all emitters
for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++) for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++)
@@ -974,8 +999,8 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
drawCall.Particle.Particles = buffer; drawCall.Particle.Particles = buffer;
// Check if need to render any module // Check if need to render any module
RenderModulesIndices renderModulesIndices; uint32 renderModulesIndices = 0;
for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count() && renderModulesIndices.Count() < PARTICLE_EMITTER_MAX_MODULES; moduleIndex++) for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count() && moduleIndex < 32; moduleIndex++)
{ {
auto module = emitter->Graph.RenderModules[moduleIndex]; auto module = emitter->Graph.RenderModules[moduleIndex];
@@ -989,10 +1014,10 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
if (!material || if (!material ||
!material->IsReady() || !material->IsReady() ||
!material->IsParticle() || !material->IsParticle() ||
(view.Pass & material->GetDrawModes() & moduleDrawModes) == DrawPass::None (viewsDrawModes & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
) )
break; break;
renderModulesIndices.Add(moduleIndex); renderModulesIndices |= 1u << moduleIndex;
break; break;
} }
// Model Rendering // Model Rendering
@@ -1008,10 +1033,10 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
if (!material || if (!material ||
!material->IsReady() || !material->IsReady() ||
!material->IsParticle() || !material->IsParticle() ||
(view.Pass & material->GetDrawModes() & moduleDrawModes) == DrawPass::None (viewsDrawModes & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
) )
break; break;
renderModulesIndices.Add(moduleIndex); renderModulesIndices |= 1u << moduleIndex;
break; break;
} }
// Ribbon Rendering // Ribbon Rendering
@@ -1022,10 +1047,10 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
if (!material || if (!material ||
!material->IsReady() || !material->IsReady() ||
!material->IsParticle() || !material->IsParticle() ||
(view.Pass & material->GetDrawModes() & moduleDrawModes) == DrawPass::None (viewsDrawModes & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
) )
break; break;
renderModulesIndices.Add(moduleIndex); renderModulesIndices |= 1u << moduleIndex;
break; break;
} }
// Volumetric Fog Rendering // Volumetric Fog Rendering
@@ -1035,26 +1060,27 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
if (!material || if (!material ||
!material->IsReady() || !material->IsReady() ||
material->GetInfo().Domain != MaterialDomain::VolumeParticle || material->GetInfo().Domain != MaterialDomain::VolumeParticle ||
(view.Flags & ViewFlags::Fog) == ViewFlags::None (renderContextBatch.GetMainContext().View.Flags & ViewFlags::Fog) == ViewFlags::None ||
(viewsMask & 1) == 0
) )
break; break;
renderModulesIndices.Add(moduleIndex); renderModulesIndices |= 1u << moduleIndex;
break; break;
} }
} }
} }
if (renderModulesIndices.IsEmpty()) if (renderModulesIndices == 0)
continue; continue;
// Draw // Draw
switch (buffer->Mode) switch (buffer->Mode)
{ {
case ParticlesSimulationMode::CPU: case ParticlesSimulationMode::CPU:
DrawEmitterCPU(renderContext, buffer, drawCall, drawModes, staticFlags, emitterData, renderModulesIndices, sortOrder); DrawEmitterCPU(renderContextBatch, buffer, drawCall, viewsDrawModes, staticFlags, bounds, renderModulesIndices, sortOrder);
break; break;
#if COMPILE_WITH_GPU_PARTICLES #if COMPILE_WITH_GPU_PARTICLES
case ParticlesSimulationMode::GPU: case ParticlesSimulationMode::GPU:
DrawEmitterGPU(renderContext, buffer, drawCall, drawModes, staticFlags, renderModulesIndices, sortOrder); DrawEmitterGPU(renderContextBatch, buffer, drawCall, viewsDrawModes, staticFlags, bounds, renderModulesIndices, sortOrder);
break; break;
#endif #endif
} }
@@ -1090,6 +1116,7 @@ void UpdateGPU(RenderTask* task, GPUContext* context)
ScopeLock lock(GpuUpdateListLocker); ScopeLock lock(GpuUpdateListLocker);
if (GpuUpdateList.IsEmpty()) if (GpuUpdateList.IsEmpty())
return; return;
PROFILE_CPU_NAMED("GPUParticles");
PROFILE_GPU("GPU Particles"); PROFILE_GPU("GPU Particles");
PROFILE_MEM(Particles); PROFILE_MEM(Particles);

View File

@@ -6,7 +6,7 @@
#include "Engine/Threading/ConcurrentSystemLocker.h" #include "Engine/Threading/ConcurrentSystemLocker.h"
class TaskGraphSystem; class TaskGraphSystem;
struct RenderContext; struct RenderContextBatch;
struct RenderView; struct RenderView;
class ParticleEmitter; class ParticleEmitter;
class ParticleSystemInstance; class ParticleSystemInstance;
@@ -48,9 +48,9 @@ public:
/// <summary> /// <summary>
/// Draws the particles. /// Draws the particles.
/// </summary> /// </summary>
/// <param name="renderContext">The rendering context.</param> /// <param name="renderContextBatch">The rendering context.</param>
/// <param name="effect">The owning actor.</param> /// <param name="effect">The owning actor.</param>
static void DrawParticles(RenderContext& renderContext, ParticleEffect* effect); static void DrawParticles(RenderContextBatch& renderContextBatch, ParticleEffect* effect);
#if USE_EDITOR #if USE_EDITOR
/// <summary> /// <summary>