Add async particles drawing (GPU emitters are sync)

This commit is contained in:
Wojtek Figat
2025-08-05 22:53:09 +02:00
parent baf0cfce8e
commit b1710c4d01
5 changed files with 53 additions and 2 deletions

View File

@@ -20,6 +20,7 @@ ParticleEffect::ParticleEffect(const SpawnParams& params)
{
_box = BoundingBox(_transform.Translation);
BoundingSphere::FromBox(_box, _sphere);
_drawCategory = SceneRendering::SceneDrawAsync;
}
void ParticleEffectParameter::Init(ParticleEffect* effect, int32 emitterIndex, int32 paramIndex)

View File

@@ -649,8 +649,22 @@ void CleanupGPUParticlesSorting()
GPUParticlesSorting = nullptr;
}
void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int8 sortOrder)
void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, const RenderModulesIndices& renderModulesIndices, int8 sortOrder)
{
if (!IsInMainThread())
{
// Clone draw call data the hard way
byte drawCallCopy[sizeof(DrawCall)];
Platform::MemoryCopy(&drawCallCopy, &drawCall, sizeof(DrawCall));
// 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)
{
DrawEmitterGPU(renderContext, buffer, *(DrawCall*)drawCallCopy, drawModes, staticFlags, renderModulesIndices, sortOrder);
});
return;
}
const auto context = GPUDevice::Instance->GetMainContext();
auto emitter = buffer->Emitter;
@@ -1092,7 +1106,7 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
break;
#if COMPILE_WITH_GPU_PARTICLES
case ParticlesSimulationMode::GPU:
DrawEmitterGPU(renderContext, buffer, drawCall, drawModes, staticFlags, emitterData, renderModulesIndices, sortOrder);
DrawEmitterGPU(renderContext, buffer, drawCall, drawModes, staticFlags, renderModulesIndices, sortOrder);
break;
#endif
}

View File

@@ -255,6 +255,20 @@ void RenderList::AddSettingsBlend(IPostFxSettingsProvider* provider, float weigh
Blendable.Add(blend);
}
void RenderList::AddDelayedDraw(DelayedDraw&& func)
{
MemPoolLocker.Lock(); // TODO: convert _delayedDraws into RenderListBuffer with usage of arena Memory for fast alloc
_delayedDraws.Add(MoveTemp(func));
MemPoolLocker.Unlock();
}
void RenderList::DrainDelayedDraws(RenderContext& renderContext)
{
for (DelayedDraw& e : _delayedDraws)
e(renderContext);
_delayedDraws.SetCapacity(0);
}
void RenderList::BlendSettings()
{
PROFILE_CPU();
@@ -459,6 +473,7 @@ RenderList::RenderList(const SpawnParams& params)
, ObjectBuffer(0, PixelFormat::R32G32B32A32_Float, false, TEXT("Object Buffer"))
, TempObjectBuffer(0, PixelFormat::R32G32B32A32_Float, false, TEXT("Object Buffer"))
, _instanceBuffer(0, sizeof(ShaderObjectDrawInstanceData), TEXT("Instance Buffer"), GPUVertexLayout::Get({ { VertexElement::Types::Attribute0, 3, 0, 1, PixelFormat::R32_UInt } }))
, _delayedDraws(&Memory)
{
}
@@ -490,6 +505,7 @@ void RenderList::Clear()
PostFx.Clear();
Settings = PostProcessSettings();
Blendable.Clear();
_delayedDraws.Clear();
_instanceBuffer.Clear();
ObjectBuffer.Clear();
TempObjectBuffer.Clear();

View File

@@ -435,8 +435,24 @@ public:
/// </summary>
DynamicTypedBuffer TempObjectBuffer;
typedef Function<void(RenderContext& renderContext)> DelayedDraw;
void AddDelayedDraw(DelayedDraw&& func);
void DrainDelayedDraws(RenderContext& renderContext);
/// <summary>
/// Adds custom callback (eg. lambda) to invoke after scene draw calls are collected on a main thread (some async draw tasks might be active). Allows for safe usage of GPUContext for draw preparations or to perform GPU-driven drawing.
/// </summary>
template<typename T>
FORCE_INLINE void AddDelayedDraw(const T& lambda)
{
DelayedDraw func;
func.Bind<ConcurrentArenaAllocation>(&Memory, lambda);
AddDelayedDraw(MoveTemp(func));
}
private:
DynamicVertexBuffer _instanceBuffer;
Array<DelayedDraw, ConcurrentArenaAllocation> _delayedDraws;
public:
/// <summary>

View File

@@ -458,6 +458,10 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
JobSystem::Wait(label);
renderContextBatch.WaitLabels.Clear();
// Perform custom post-scene drawing (eg. GPU dispatches used by VFX)
for (RenderContext& e : renderContextBatch.Contexts)
e.List->DrainDelayedDraws(e);
#if USE_EDITOR
GBufferPass::Instance()->OverrideDrawCalls(renderContext);
#endif