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)
{
PROFILE_CPU_ASSET(emitter);
PROFILE_GPU("GPUParticles");
ASSERT(emitter->Graph.Version == data.Version);
ASSERT(emitter->Graph.Version == data.Buffer->Version);
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)
return;
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(GetPosition(), renderContext.View.Position));
Particles::DrawParticles(renderContext, this);
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(GetPosition(), renderContext.View.WorldPosition));
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

View File

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

View File

@@ -178,9 +178,7 @@ void Particles::OnEffectDestroy(ParticleEffect* effect)
#endif
}
typedef Array<uint8, FixedAllocation<PARTICLE_EMITTER_MAX_MODULES>> RenderModulesIndices;
void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int8 sortOrder)
void DrawEmitterCPU(RenderContextBatch& renderContextBatch, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, const BoundingSphere& bounds, uint32 renderModulesIndices, int8 sortOrder)
{
// Skip if CPU buffer is empty
if (buffer->CPU.Count == 0)
@@ -189,7 +187,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
auto emitter = buffer->Emitter;
// 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
if (!buffer->GPU.SortedIndices)
@@ -210,10 +208,11 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
if (listSize < 500)
{
// Use fast stack allocator from RenderList
sortingKeys[0] = renderContext.List->Memory.Allocate<uint32>(listSize);
sortingKeys[1] = renderContext.List->Memory.Allocate<uint32>(listSize);
sortingIndices[0] = renderContext.List->Memory.Allocate<int32>(listSize);
sortingIndices[1] = renderContext.List->Memory.Allocate<int32>(listSize);
auto& memory = renderContextBatch.GetMainContext().List->Memory;
sortingKeys[0] = memory.Allocate<uint32>(listSize);
sortingKeys[1] = memory.Allocate<uint32>(listSize);
sortingIndices[0] = memory.Allocate<int32>(listSize);
sortingIndices[1] = memory.Allocate<int32>(listSize);
}
else
{
@@ -236,7 +235,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
const int32 positionOffset = emitter->Graph.GetPositionAttributeOffset();
if (positionOffset == -1)
break;
const Matrix viewProjection = renderContext.View.ViewProjection();
const Matrix viewProjection = renderContextBatch.GetMainContext().View.ViewProjection();
const byte* positionPtr = buffer->CPU.Buffer.Get() + positionOffset;
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
{
@@ -262,7 +261,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
const int32 positionOffset = emitter->Graph.GetPositionAttributeOffset();
if (positionOffset == -1)
break;
const Float3 viewPosition = renderContext.View.Position;
const Float3 viewPosition = renderContextBatch.GetMainContext().View.Position;
const byte* positionPtr = buffer->CPU.Buffer.Get() + positionOffset;
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
{
@@ -356,9 +355,10 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
auto& vertexBuffer = buffer->GPU.RibbonVertexBufferDynamic->Data;
// 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];
if (module->TypeID != 404 || ribbonModuleIndex >= PARTICLE_EMITTER_MAX_RIBBONS)
continue;
@@ -454,7 +454,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Setup ribbon data
ribbonModulesSegmentCount[ribbonModuleIndex] = segmentCount;
ribbonModulesDrawIndicesCount[index] = indices;
ribbonModulesDrawIndicesCount[ribbonModuleIndex] = indices;
ribbonModulesDrawIndicesPos += indices;
ribbonModuleIndex++;
@@ -472,9 +472,10 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Execute all rendering modules
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];
drawCall.Particle.Module = module;
@@ -493,7 +494,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Submit draw call
SpriteRenderer.SetupDrawCall(drawCall);
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;
}
@@ -521,7 +522,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Submit draw call
mesh.GetDrawCallGeometry(drawCall);
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;
@@ -580,7 +581,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.Draw.StartIndex = ribbonModulesDrawIndicesStart[ribbonModuleIndex];
drawCall.Draw.IndicesCount = ribbonModulesDrawIndicesCount[ribbonModuleIndex];
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++;
@@ -610,7 +611,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
Float3::Transform(drawCall.Particle.VolumetricFog.Position, drawCall.World, drawCall.Particle.VolumetricFog.Position);
drawCall.Particle.VolumetricFog.Radius = hasRadius ? radiusData[i] : 100.0f;
drawCall.Particle.VolumetricFog.ParticleIndex = i;
renderContext.List->VolumetricFogParticles.Add(drawCall);
renderContextBatch.GetMainContext().List->VolumetricFogParticles.Add(drawCall);
}
break;
}
@@ -649,7 +650,7 @@ void CleanupGPUParticlesSorting()
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())
{
@@ -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
// 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;
}
@@ -669,7 +670,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
auto emitter = buffer->Emitter;
// 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");
@@ -720,7 +721,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
permutationIndex = 0;
sortAscending = false;
data.PositionOffset = emitter->Graph.GetPositionAttributeOffset();
const Matrix viewProjection = renderContext.View.ViewProjection();
const Matrix viewProjection = renderContextBatch.GetMainContext().View.ViewProjection();
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
{
Matrix matrix;
@@ -738,7 +739,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
permutationIndex = 1;
sortAscending = false;
data.PositionOffset = emitter->Graph.GetPositionAttributeOffset();
data.ViewPosition = renderContext.View.Position;
data.ViewPosition = renderContextBatch.GetMainContext().View.Position;
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
{
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
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];
switch (module->TypeID)
{
@@ -835,9 +837,10 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Execute all rendering modules using indirect draw arguments
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];
drawCall.Particle.Module = module;
switch (module->TypeID)
@@ -863,7 +866,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.InstanceCount = 0;
drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer;
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++;
break;
}
@@ -897,7 +900,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.InstanceCount = 0;
drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer;
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++;
}
break;
@@ -920,43 +923,65 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
#endif
void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effect)
void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEffect* effect)
{
// Setup
auto& view = renderContext.View;
const DrawPass drawModes = view.Pass & effect->DrawModes;
if (drawModes == DrawPass::None)
return;
PROFILE_CPU();
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);
Matrix worlds[2];
Matrix::Translation(-renderContext.View.Origin, worlds[0]); // World
renderContext.View.GetWorldMatrix(effect->GetTransform(), worlds[1]); // Local
Matrix::Translation(-viewOrigin, worlds[0]); // World
renderContextBatch.GetMainContext().View.GetWorldMatrix(effect->GetTransform(), worlds[1]); // Local
float worldDeterminantSigns[2];
worldDeterminantSigns[0] = Math::FloatSelect(worlds[0].RotDeterminant(), 1, -1);
worldDeterminantSigns[1] = Math::FloatSelect(worlds[1].RotDeterminant(), 1, -1);
const StaticFlags staticFlags = effect->GetStaticFlags();
const int8 sortOrder = effect->SortOrder;
// Draw lights
for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++)
// Draw lights (only to into the main view)
if ((viewsMask & 1) == 1 && renderContextBatch.GetMainContext().View.Pass != DrawPass::Depth)
{
auto& emitterData = effect->Instance.Emitters[emitterIndex];
const auto buffer = emitterData.Buffer;
if (!buffer || (buffer->Mode == ParticlesSimulationMode::CPU && buffer->CPU.Count == 0))
continue;
auto emitter = buffer->Emitter;
if (!emitter || !emitter->IsLoaded())
continue;
for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++)
{
auto& emitterData = effect->Instance.Emitters[emitterIndex];
const auto buffer = emitterData.Buffer;
if (!buffer || (buffer->Mode == ParticlesSimulationMode::CPU && buffer->CPU.Count == 0))
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
DrawCall drawCall;
drawCall.PerInstanceRandom = effect->GetPerInstanceRandom();
drawCall.ObjectPosition = effect->GetSphere().Center - view.Origin;
drawCall.ObjectRadius = (float)effect->GetSphere().Radius;
drawCall.ObjectPosition = bounds.Center;
drawCall.ObjectRadius = (float)bounds.Radius;
// Draw all emitters
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;
// Check if need to render any module
RenderModulesIndices renderModulesIndices;
for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count() && renderModulesIndices.Count() < PARTICLE_EMITTER_MAX_MODULES; moduleIndex++)
uint32 renderModulesIndices = 0;
for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count() && moduleIndex < 32; moduleIndex++)
{
auto module = emitter->Graph.RenderModules[moduleIndex];
@@ -989,10 +1014,10 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
if (!material ||
!material->IsReady() ||
!material->IsParticle() ||
(view.Pass & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
(viewsDrawModes & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
)
break;
renderModulesIndices.Add(moduleIndex);
renderModulesIndices |= 1u << moduleIndex;
break;
}
// Model Rendering
@@ -1008,10 +1033,10 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
if (!material ||
!material->IsReady() ||
!material->IsParticle() ||
(view.Pass & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
(viewsDrawModes & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
)
break;
renderModulesIndices.Add(moduleIndex);
renderModulesIndices |= 1u << moduleIndex;
break;
}
// Ribbon Rendering
@@ -1022,10 +1047,10 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
if (!material ||
!material->IsReady() ||
!material->IsParticle() ||
(view.Pass & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
(viewsDrawModes & material->GetDrawModes() & moduleDrawModes) == DrawPass::None
)
break;
renderModulesIndices.Add(moduleIndex);
renderModulesIndices |= 1u << moduleIndex;
break;
}
// Volumetric Fog Rendering
@@ -1035,26 +1060,27 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
if (!material ||
!material->IsReady() ||
material->GetInfo().Domain != MaterialDomain::VolumeParticle ||
(view.Flags & ViewFlags::Fog) == ViewFlags::None
(renderContextBatch.GetMainContext().View.Flags & ViewFlags::Fog) == ViewFlags::None ||
(viewsMask & 1) == 0
)
break;
renderModulesIndices.Add(moduleIndex);
renderModulesIndices |= 1u << moduleIndex;
break;
}
}
}
if (renderModulesIndices.IsEmpty())
if (renderModulesIndices == 0)
continue;
// Draw
switch (buffer->Mode)
{
case ParticlesSimulationMode::CPU:
DrawEmitterCPU(renderContext, buffer, drawCall, drawModes, staticFlags, emitterData, renderModulesIndices, sortOrder);
DrawEmitterCPU(renderContextBatch, buffer, drawCall, viewsDrawModes, staticFlags, bounds, renderModulesIndices, sortOrder);
break;
#if COMPILE_WITH_GPU_PARTICLES
case ParticlesSimulationMode::GPU:
DrawEmitterGPU(renderContext, buffer, drawCall, drawModes, staticFlags, renderModulesIndices, sortOrder);
DrawEmitterGPU(renderContextBatch, buffer, drawCall, viewsDrawModes, staticFlags, bounds, renderModulesIndices, sortOrder);
break;
#endif
}
@@ -1090,6 +1116,7 @@ void UpdateGPU(RenderTask* task, GPUContext* context)
ScopeLock lock(GpuUpdateListLocker);
if (GpuUpdateList.IsEmpty())
return;
PROFILE_CPU_NAMED("GPUParticles");
PROFILE_GPU("GPU Particles");
PROFILE_MEM(Particles);

View File

@@ -6,7 +6,7 @@
#include "Engine/Threading/ConcurrentSystemLocker.h"
class TaskGraphSystem;
struct RenderContext;
struct RenderContextBatch;
struct RenderView;
class ParticleEmitter;
class ParticleSystemInstance;
@@ -48,9 +48,9 @@ public:
/// <summary>
/// Draws the particles.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="renderContextBatch">The rendering context.</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
/// <summary>