From d2d8a83461995307d5e9e73ce2d2facedb830fb7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Jun 2021 23:48:00 +0200 Subject: [PATCH] Add support for multi-threaded CPU particles simulation --- .../Animations/Graph/AnimGroup.Animation.cpp | 8 +- ...rticleEmitterGraph.CPU.ParticleModules.cpp | 170 +++++++++--------- .../ParticleEmitterGraph.CPU.Particles.cpp | 77 ++++---- .../Graph/CPU/ParticleEmitterGraph.CPU.cpp | 135 ++++++-------- .../Graph/CPU/ParticleEmitterGraph.CPU.h | 107 ++++------- .../Particles/Graph/ParticleEmitterGraph.h | 32 +--- Source/Engine/Particles/ParticleEmitter.cpp | 2 +- .../Particles/ParticleEmitterFunction.cpp | 8 + Source/Engine/Particles/Particles.cpp | 7 +- .../Engine/Particles/ParticlesSimulation.cpp | 7 +- Source/Engine/Particles/ParticlesSimulation.h | 5 +- 11 files changed, 245 insertions(+), 313 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 732390112..89ee914c8 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1580,11 +1580,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu #if 0 // Prevent recursive calls - for (int32 i = _callStack.Count() - 1; i >= 0; i--) + for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24)) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24)) { - const auto callFunc = _callStack[i]->Assets[0].Get(); + const auto callFunc = context.CallStack[i]->Assets[0].Get(); if (callFunc == function) { value = Value::Zero; @@ -1879,7 +1879,7 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va Graph* graph; for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24) && context.Functions.TryGet(context.CallStack[i], graph) && context.GraphStack.Last() == (Graph*)graph) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24) && context.Functions.TryGet(context.CallStack[i], graph) && context.GraphStack.Last() == graph) { functionCallNode = (AnimGraphNode*)context.CallStack[i]; break; diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp index 4d8e02a97..3f2c58df8 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp @@ -103,7 +103,8 @@ namespace int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) { const auto node = _graph.SpawnModules[index]; - auto& data = _data->SpawnModulesData[index]; + auto& context = Context.Get(); + auto& data = context.Data->SpawnModulesData[index]; // Accumulate the previous frame fraction float spawnCount = data.SpawnCounter; @@ -115,13 +116,13 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) case 100: { const float rate = Math::Max((float)TryGetValue(node->GetBox(0), node->Values[2]), 0.0f); - spawnCount += rate * _deltaTime; + spawnCount += rate * context.DeltaTime; break; } // Single Burst case 101: { - const bool isFirstUpdate = (_data->Time - _deltaTime) <= 0.0f; + const bool isFirstUpdate = (context.Data->Time - context.DeltaTime) <= 0.0f; if (isFirstUpdate) { const float count = Math::Max((float)TryGetValue(node->GetBox(0), node->Values[2]), 0.0f); @@ -133,11 +134,11 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) case 102: { float& nextSpawnTime = data.NextSpawnTime; - if (nextSpawnTime - _data->Time <= 0.0f) + if (nextSpawnTime - context.Data->Time <= 0.0f) { const float count = Math::Max((float)TryGetValue(node->GetBox(0), node->Values[2]), 0.0f); const float delay = Math::Max((float)TryGetValue(node->GetBox(1), node->Values[3]), 0.0f); - nextSpawnTime = _data->Time + delay; + nextSpawnTime = context.Data->Time + delay; spawnCount += count; } break; @@ -146,13 +147,13 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) case 103: { float& nextSpawnTime = data.NextSpawnTime; - if (nextSpawnTime - _data->Time <= 0.0f) + if (nextSpawnTime - context.Data->Time <= 0.0f) { const Vector2 countMinMax = (Vector2)TryGetValue(node->GetBox(0), node->Values[2]); const Vector2 delayMinMax = (Vector2)TryGetValue(node->GetBox(1), node->Values[3]); const float count = Math::Max(countMinMax.X + RAND * (countMinMax.Y - countMinMax.X), 0.0f); const float delay = Math::Max(delayMinMax.X + RAND * (delayMinMax.Y - delayMinMax.X), 0.0f); - nextSpawnTime = _data->Time + delay; + nextSpawnTime = context.Data->Time + delay; spawnCount += count; } break; @@ -170,8 +171,9 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* node, int32 particlesStart, int32 particlesEnd) { - auto stride = _data->Buffer->Stride; - auto start = _data->Buffer->GetParticleCPU(particlesStart); + auto& context = Context.Get(); + auto stride = context.Data->Buffer->Stride; + auto start = context.Data->Buffer->GetParticleCPU(particlesStart); switch (node->TypeID) { @@ -181,7 +183,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { auto spriteFacingMode = node->Values[2].AsInt; { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* spriteFacingModePtr = start + attribute.Offset; for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { @@ -192,14 +194,14 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if ((ParticleSpriteFacingMode)spriteFacingMode == ParticleSpriteFacingMode::CustomFacingVector || (ParticleSpriteFacingMode)spriteFacingMode == ParticleSpriteFacingMode::FixedAxis) { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[1]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; byte* customFacingVectorPtr = start + attribute.Offset; auto box = node->GetBox(0); if (node->UsePerParticleDataResolve()) { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Vector3 vector = (Vector3)GetValue(box, 3); *((Vector3*)customFacingVectorPtr) = vector; customFacingVectorPtr += stride; @@ -223,7 +225,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { auto modelFacingMode = node->Values[2].AsInt; { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* modelFacingModePtr = start + attribute.Offset; for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { @@ -236,11 +238,11 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Update Age case 300: { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* agePtr = start + attribute.Offset; for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - *((float*)agePtr) += _deltaTime; + *((float*)agePtr) += context.DeltaTime; agePtr += stride; } break; @@ -249,16 +251,16 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* case 301: case 304: { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* velocityPtr = start + attribute.Offset; auto box = node->GetBox(0); if (node->UsePerParticleDataResolve()) { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Vector3 force = (Vector3)GetValue(box, 2); - *((Vector3*)velocityPtr) += force * _deltaTime; + *((Vector3*)velocityPtr) += force * context.DeltaTime; velocityPtr += stride; } } @@ -267,7 +269,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* const Vector3 force = (Vector3)GetValue(box, 2); for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - *((Vector3*)velocityPtr) += force * _deltaTime; + *((Vector3*)velocityPtr) += force * context.DeltaTime; velocityPtr += stride; } } @@ -276,9 +278,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Conform to Sphere case 305: { - auto& position = _data->Buffer->Layout->Attributes[node->Attributes[0]]; - auto& velocity = _data->Buffer->Layout->Attributes[node->Attributes[1]]; - auto& mass = _data->Buffer->Layout->Attributes[node->Attributes[2]]; + auto& position = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& velocity = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; + auto& mass = context.Data->Buffer->Layout->Attributes[node->Attributes[2]]; byte* positionPtr = start + position.Offset; byte* velocityPtr = start + velocity.Offset; @@ -308,7 +310,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* float ratio = Math::SmoothStep(0.0f, stickDistance * 2.0f, Math::Abs(distToSurface)); \ float tgtSpeed = Math::Sign(distToSurface) * attractionSpeed * ratio; \ float deltaSpeed = tgtSpeed - spdNormal; \ - Vector3 deltaVelocity = dir * (Math::Sign(deltaSpeed) * Math::Min(Math::Abs(deltaSpeed), _deltaTime * Math::Lerp(stickForce, attractionForce, ratio)) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ + Vector3 deltaVelocity = dir * (Math::Sign(deltaSpeed) * Math::Min(Math::Abs(deltaSpeed), context.DeltaTime * Math::Lerp(stickForce, attractionForce, ratio)) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ *(Vector3*)velocityPtr = velocity + deltaVelocity; \ positionPtr += stride; \ velocityPtr += stride; \ @@ -318,7 +320,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -338,7 +340,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Kill (sphere) case 306: { - auto& position = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& position = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + position.Offset; @@ -356,8 +358,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if (sign * lengthSqr <= sign * sphereRadiusSqr) \ { \ particlesEnd--; \ - _data->Buffer->CPU.Count--; \ - Platform::MemoryCopy(_data->Buffer->GetParticleCPU(particleIndex), _data->Buffer->GetParticleCPU(_data->Buffer->CPU.Count), _data->Buffer->Stride); \ + context.Data->Buffer->CPU.Count--; \ + Platform::MemoryCopy(context.Data->Buffer->GetParticleCPU(particleIndex), context.Data->Buffer->GetParticleCPU(context.Data->Buffer->CPU.Count), context.Data->Buffer->Stride); \ particleIndex--; \ } \ positionPtr += stride @@ -366,7 +368,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -386,7 +388,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Kill (box) case 307: { - auto& position = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& position = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + position.Offset; @@ -409,8 +411,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if (collision) \ { \ particlesEnd--; \ - _data->Buffer->CPU.Count--; \ - Platform::MemoryCopy(_data->Buffer->GetParticleCPU(particleIndex), _data->Buffer->GetParticleCPU(_data->Buffer->CPU.Count), _data->Buffer->Stride); \ + context.Data->Buffer->CPU.Count--; \ + Platform::MemoryCopy(context.Data->Buffer->GetParticleCPU(particleIndex), context.Data->Buffer->GetParticleCPU(context.Data->Buffer->CPU.Count), context.Data->Buffer->Stride); \ particleIndex--; \ } \ positionPtr += stride @@ -419,7 +421,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -447,8 +449,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if (kill) \ { \ particlesEnd--; \ - _data->Buffer->CPU.Count--; \ - Platform::MemoryCopy(_data->Buffer->GetParticleCPU(particleIndex), _data->Buffer->GetParticleCPU(_data->Buffer->CPU.Count), _data->Buffer->Stride); \ + context.Data->Buffer->CPU.Count--; \ + Platform::MemoryCopy(context.Data->Buffer->GetParticleCPU(particleIndex), context.Data->Buffer->GetParticleCPU(context.Data->Buffer->CPU.Count), context.Data->Buffer->Stride); \ particleIndex--; \ } @@ -456,7 +458,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -479,9 +481,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* auto box = node->GetBox(0); const bool useSpriteSize = node->Values[3].AsBool; - auto& velocity = _data->Buffer->Layout->Attributes[node->Attributes[0]]; - auto& mass = _data->Buffer->Layout->Attributes[node->Attributes[1]]; - byte* spriteSizePtr = useSpriteSize ? start + _data->Buffer->Layout->Attributes[node->Attributes[2]].Offset : nullptr; + auto& velocity = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& mass = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; + byte* spriteSizePtr = useSpriteSize ? start + context.Data->Buffer->Layout->Attributes[node->Attributes[2]].Offset : nullptr; byte* velocityPtr = start + velocity.Offset; byte* massPtr = start + mass.Offset; @@ -492,7 +494,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* float particleDrag = drag; \ if (useSpriteSize) \ particleDrag *= ((Vector2*)spriteSizePtr)->MulValues(); \ - *((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * _deltaTime) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ + *((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * context.DeltaTime) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ velocityPtr += stride; \ massPtr += stride; \ spriteSizePtr += stride @@ -501,7 +503,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -521,9 +523,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Turbulence case 311: { - auto& position = _data->Buffer->Layout->Attributes[node->Attributes[0]]; - auto& velocity = _data->Buffer->Layout->Attributes[node->Attributes[1]]; - auto& mass = _data->Buffer->Layout->Attributes[node->Attributes[2]]; + auto& position = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& velocity = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; + auto& mass = context.Data->Buffer->Layout->Attributes[node->Attributes[2]]; byte* positionPtr = start + position.Offset; byte* velocityPtr = start + velocity.Offset; @@ -551,7 +553,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* Vector3 vectorFieldUVW = Vector3::Transform(*((Vector3*)positionPtr), invFieldTransformMatrix); \ Vector3 force = Noise3D(vectorFieldUVW + 0.5f, octavesCount, roughness); \ force = Vector3::Transform(force, fieldTransformMatrix) * intensity; \ - *((Vector3*)velocityPtr) += force * (_deltaTime / Math::Max(*(float*)massPtr, ZeroTolerance)); \ + *((Vector3*)velocityPtr) += force * (context.DeltaTime / Math::Max(*(float*)massPtr, ZeroTolerance)); \ positionPtr += stride; \ velocityPtr += stride; \ massPtr += stride @@ -560,7 +562,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -581,7 +583,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* case 200: case 302: { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* dataPtr = start + attribute.Offset; int32 dataSize = attribute.GetSize(); auto box = node->GetBox(0); @@ -590,7 +592,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Value value = GetValue(box, 4).Cast(type); Platform::MemoryCopy(dataPtr, &value.AsPointer, dataSize); dataPtr += stride; @@ -637,7 +639,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* case 362: case 363: { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* dataPtr = start + attribute.Offset; int32 dataSize = attribute.GetSize(); auto box = node->GetBox(0); @@ -646,7 +648,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Value value = GetValue(box, 2).Cast(type); Platform::MemoryCopy(dataPtr, &value.AsPointer, dataSize); dataPtr += stride; @@ -666,7 +668,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (sphere surface) case 202: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -691,7 +693,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -711,7 +713,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (plane) case 203: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -729,7 +731,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -749,7 +751,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (circle) case 204: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -772,7 +774,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -792,7 +794,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (disc) case 205: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -815,7 +817,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -835,7 +837,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (box surface) case 206: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -865,7 +867,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -885,7 +887,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (box volume) case 207: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -903,7 +905,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -923,7 +925,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (cylinder) case 208: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -948,7 +950,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -968,7 +970,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (line) case 209: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -986,7 +988,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1006,7 +1008,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (torus) case 210: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -1050,7 +1052,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1070,7 +1072,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (sphere volume) case 211: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -1095,7 +1097,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1121,8 +1123,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (spiral) case 214: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; - auto& velocityAttr = _data->Buffer->Layout->Attributes[node->Attributes[1]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& velocityAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; byte* positionPtr = start + positionAttr.Offset; byte* velocityPtr = start + velocityAttr.Offset; @@ -1131,7 +1133,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* auto rotationSpeedBox = node->GetBox(1); auto velocityScaleBox = node->GetBox(2); - auto& arc = node->SpiralModuleProgress; + auto& arc = *(float*)&context.Data->CustomData[node->CustomDataOffset]; #define INPUTS_FETCH() \ const Vector3 center = (Vector3)GetValue(centerBox, 2); \ @@ -1151,7 +1153,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1171,9 +1173,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Helper macros for collision modules to share the code #define COLLISION_BEGIN() \ - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; \ - auto& velocityAttr = _data->Buffer->Layout->Attributes[node->Attributes[1]]; \ - auto& ageAttr = _data->Buffer->Layout->Attributes[node->Attributes[2]]; \ + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; \ + auto& velocityAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; \ + auto& ageAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[2]]; \ byte* positionPtr = start + positionAttr.Offset; \ byte* velocityPtr = start + velocityAttr.Offset; \ byte* agePtr = start + ageAttr.Offset; \ @@ -1221,7 +1223,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define LOGIC() \ Vector3 position = *(Vector3*)positionPtr; \ Vector3 velocity = *(Vector3*)velocityPtr; \ - Vector3 nextPos = position + velocity * _deltaTime; \ + Vector3 nextPos = position + velocity * context.DeltaTime; \ Vector3 n = planeNormal; \ float distToPlane = Vector3::Dot(nextPos, n) - Vector3::Dot(planePosition, n) - radius; \ if (distToPlane < 0.0f) \ @@ -1233,7 +1235,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1263,7 +1265,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define LOGIC() \ Vector3 position = *(Vector3*)positionPtr; \ Vector3 velocity = *(Vector3*)velocityPtr; \ - Vector3 nextPos = position + velocity * _deltaTime; \ + Vector3 nextPos = position + velocity * context.DeltaTime; \ Vector3 dir = nextPos - spherePosition; \ float sqrLength = Vector3::Dot(dir, dir); \ float totalRadius = sphereRadius + sign * radius; \ @@ -1278,7 +1280,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1308,7 +1310,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define LOGIC() \ Vector3 position = *(Vector3*)positionPtr; \ Vector3 velocity = *(Vector3*)velocityPtr; \ - Vector3 nextPos = position + velocity * _deltaTime; \ + Vector3 nextPos = position + velocity * context.DeltaTime; \ Vector3 dir = nextPos - boxPosition; \ Vector3 absDir = Vector3::Abs(dir); \ Vector3 halfBoxSize = boxSize * 0.5f + radius * sign; \ @@ -1338,7 +1340,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1370,7 +1372,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define LOGIC() \ Vector3 position = *(Vector3*)positionPtr; \ Vector3 velocity = *(Vector3*)velocityPtr; \ - Vector3 nextPos = position + velocity * _deltaTime; \ + Vector3 nextPos = position + velocity * context.DeltaTime; \ Vector3 dir = nextPos - cylinderPosition; \ float halfHeight = cylinderHeight * 0.5f + radius * sign; \ float cylinderRadiusT = cylinderRadius + radius * sign; \ @@ -1403,7 +1405,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index 019dc41f3..6d9ba3722 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -7,11 +7,12 @@ #include "Engine/Graphics/RenderTask.h" #define GET_VIEW() auto mainViewTask = MainRenderTask::Instance && MainRenderTask::Instance->LastUsedFrame != 0 ? MainRenderTask::Instance : nullptr -#define ACCESS_PARTICLE_ATTRIBUTE(index) (_data->Buffer->GetParticleCPU(_particleIndex) + _data->Buffer->Layout->Attributes[node->Attributes[index]].Offset) +#define ACCESS_PARTICLE_ATTRIBUTE(index) (context.Data->Buffer->GetParticleCPU(context.ParticleIndex) + context.Data->Buffer->Layout->Attributes[node->Attributes[index]].Offset) #define GET_PARTICLE_ATTRIBUTE(index, type) *(type*)ACCESS_PARTICLE_ATTRIBUTE(index) void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value) { + auto& context = Context.Get(); switch (node->TypeID) { // Get @@ -21,7 +22,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* nod const auto param = _graph.GetParameter((Guid)node->Values[0], paramIndex); if (param) { - value = _data->Parameters[paramIndex]; + value = context.Data->Parameters[paramIndex]; switch (param->Type.Type) { case VariantType::Vector2: @@ -130,6 +131,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTextures(Box* box, Node* node, void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Value& value) { + auto& context = Context.Get(); switch (node->TypeID) { // Linearize Depth @@ -141,15 +143,13 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va } // Time case 8: - { - value = box->ID == 0 ? _data->Time : _deltaTime; + value = box->ID == 0 ? context.Data->Time : context.DeltaTime; break; - } // Transform Position To Screen UV case 9: { GET_VIEW(); - const Matrix viewProjection = _viewTask ? _viewTask->View.PrevViewProjection : Matrix::Identity; + const Matrix viewProjection = context.ViewTask ? context.ViewTask->View.PrevViewProjection : Matrix::Identity; const Vector3 position = (Vector3)TryGetValue(node->GetBox(0), Value::Zero); Vector4 projPos; Vector3::Transform(position, viewProjection); @@ -165,6 +165,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* nodeBase, Value& value) { + auto& context = Context.Get(); auto node = (ParticleEmitterGraphCPUNode*)nodeBase; switch (node->TypeID) { @@ -199,8 +200,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node // Particle Attribute (by index) case 303: { - const auto particleIndex = tryGetValue(node->GetBox(1), _particleIndex); - byte* ptr = (_data->Buffer->GetParticleCPU((uint32)particleIndex) + _data->Buffer->Layout->Attributes[node->Attributes[0]].Offset); + const auto particleIndex = tryGetValue(node->GetBox(1), context.ParticleIndex); + byte* ptr = (context.Data->Buffer->GetParticleCPU((uint32)particleIndex) + context.Data->Buffer->Layout->Attributes[node->Attributes[0]].Offset); switch ((ParticleAttribute::ValueTypes)node->Attributes[1]) { case ParticleAttribute::ValueTypes::Float: @@ -296,19 +297,19 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node // Effect Position case 200: { - value = _effect->GetPosition(); + value = context.Effect->GetPosition(); break; } // Effect Rotation case 201: { - value = _effect->GetOrientation(); + value = context.Effect->GetOrientation(); break; } // Effect Scale case 202: { - value = _effect->GetScale(); + value = context.Effect->GetScale(); break; } // Simulation Mode @@ -320,25 +321,25 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node // View Position case 204: { - value = _viewTask ? _viewTask->View.Position : Vector3::Zero; + value = context.ViewTask ? context.ViewTask->View.Position : Vector3::Zero; break; } // View Direction case 205: { - value = _viewTask ? _viewTask->View.Direction : Vector3::Forward; + value = context.ViewTask ? context.ViewTask->View.Direction : Vector3::Forward; break; } // View Far Plane case 206: { - value = _viewTask ? _viewTask->View.Far : 0.0f; + value = context.ViewTask ? context.ViewTask->View.Far : 0.0f; break; } // Screen Size case 207: { - const Vector4 size = _viewTask ? _viewTask->View.ScreenSize : Vector4::Zero; + const Vector4 size = context.ViewTask ? context.ViewTask->View.ScreenSize : Vector4::Zero; if (box->ID == 0) value = Vector2(size.X, size.Y); else @@ -358,11 +359,11 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node #if 0 // Prevent recursive calls - for (int32 i = _callStack.Count() - 1; i >= 0; i--) + for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300)) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300)) { - const auto callFunc = ((ParticleEmitterGraphCPUNode*)_callStack[i])->Assets[0].Get(); + const auto callFunc = context.CallStack[i]->Assets[0].Get(); if (callFunc == function) { value = Value::Zero; @@ -372,14 +373,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node } #endif - // Create a instanced version of the function graph - ParticleEmitterGraphCPU* graph; - if (!_functions.TryGet(nodeBase, graph)) - { - graph = New(); - function->LoadSurface((ParticleEmitterGraphCPU&)*graph); - _functions.Add(nodeBase, graph); - } + // Get function graph + Graph* graph = (Graph*)&function->Graph; + context.Functions[nodeBase] = graph; // Peek the function output (function->Outputs maps the functions outputs to output nodes indices) const int32 outputIndex = box->ID - 16; @@ -388,22 +384,22 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node value = Value::Zero; break; } - ParticleEmitterGraphCPU::Node* functionOutputNode = &graph->Nodes[function->Outputs[outputIndex]]; + Node* functionOutputNode = &graph->Nodes[function->Outputs[outputIndex]]; Box* functionOutputBox = functionOutputNode->TryGetBox(0); // Evaluate the function output - _graphStack.Push((Graph*)graph); + context.GraphStack.Push(graph); value = functionOutputBox && functionOutputBox->HasConnection() ? eatBox(nodeBase, functionOutputBox->FirstConnection()) : Value::Zero; - _graphStack.Pop(); + context.GraphStack.Pop(); break; } // Particle Index case 301: - value = _particleIndex; + value = context.ParticleIndex; break; // Particles Count case 302: - value = (uint32)_data->Buffer->CPU.Count; + value = (uint32)context.Data->Buffer->CPU.Count; break; default: VisjectExecutor::ProcessGroupParticles(box, nodeBase, value); @@ -413,20 +409,21 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, Value& value) { + auto& context = Context.Get(); switch (node->TypeID) { // Function Input case 1: { // Find the function call - ParticleEmitterGraphCPUNode* functionCallNode = nullptr; - ASSERT(_graphStack.Count() >= 2); - ParticleEmitterGraphCPU* graph; - for (int32 i = _callStack.Count() - 1; i >= 0; i--) + Node* functionCallNode = nullptr; + ASSERT(context.GraphStack.Count() >= 2); + Graph* graph; + for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300) && _functions.TryGet(_callStack[i], graph) && _graphStack[_graphStack.Count() - 1] == (Graph*)graph) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300) && context.Functions.TryGet(context.CallStack[i], graph) && context.GraphStack[context.GraphStack.Count() - 1] == graph) { - functionCallNode = (ParticleEmitterGraphCPUNode*)_callStack[i]; + functionCallNode = context.CallStack[i]; break; } } @@ -436,7 +433,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, break; } const auto function = functionCallNode->Assets[0].As(); - if (!_functions.TryGet((Node*)functionCallNode, graph) || !function) + if (!context.Functions.TryGet(functionCallNode, graph) || !function) { value = Value::Zero; break; @@ -461,9 +458,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, if (functionCallBox && functionCallBox->HasConnection()) { // Use provided input value from the function call - _graphStack.Pop(); + context.GraphStack.Pop(); value = eatBox(node, functionCallBox->FirstConnection()); - _graphStack.Push((Graph*)graph); + context.GraphStack.Push(graph); } else { diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp index 52b159947..396cbaee0 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp @@ -7,6 +7,8 @@ #include "Engine/Particles/ParticleEffect.h" #include "Engine/Engine/Time.h" +ThreadLocal ParticleEmitterGraphCPUExecutor::Context; + namespace { bool SortRibbonParticles(const int32& a, const int32& b, ParticleBufferCPUDataAccessor* data) @@ -35,8 +37,7 @@ void ParticleEmitterGraphCPU::CreateDefault() bool ParticleEmitterGraphCPU::Load(ReadStream* stream, bool loadMeta) { - // Base - if (ParticleEmitterGraph::Load(stream, loadMeta)) + if (Base::Load(stream, loadMeta)) return true; // Assign the offset in the sorted indices buffer to the rendering modules @@ -61,7 +62,7 @@ bool ParticleEmitterGraphCPU::Load(ReadStream* stream, bool loadMeta) for (int32 i = 0; i < RibbonRenderingModules.Count(); i++) { const auto module = RibbonRenderingModules[i]; - module->Ribbon.RibbonOrderOffset = ribbonOrderOffset; + module->RibbonOrderOffset = ribbonOrderOffset; ribbonOrderOffset += Capacity; } @@ -96,18 +97,16 @@ void ParticleEmitterGraphCPU::InitializeNode(Node* node) if (node->Used) return; - // Base - ParticleEmitterGraph::InitializeNode(node); + Base::InitializeNode(node); switch (node->Type) { // Position (spiral) case GRAPH_NODE_MAKE_TYPE(15, 214): - { - node->SpiralModuleProgress = 0.0f; + node->CustomDataOffset = CustomDataSize; + CustomDataSize += sizeof(float); break; } - } } ParticleEmitterGraphCPUExecutor::ParticleEmitterGraphCPUExecutor(ParticleEmitterGraphCPU& graph) @@ -120,9 +119,19 @@ ParticleEmitterGraphCPUExecutor::ParticleEmitterGraphCPUExecutor(ParticleEmitter _perGroupProcessCall[16] = (ProcessBoxHandler)&ParticleEmitterGraphCPUExecutor::ProcessGroupFunction; } -ParticleEmitterGraphCPUExecutor::~ParticleEmitterGraphCPUExecutor() +void ParticleEmitterGraphCPUExecutor::Init(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt) { - _functions.ClearDelete(); + auto& context = Context.Get(); + context.GraphStack.Clear(); + context.GraphStack.Push((Graph*)&_graph); + context.Data = &data; + context.Emitter = emitter; + context.Effect = effect; + context.DeltaTime = dt; + context.ParticleIndex = 0; + context.ViewTask = effect->GetRenderTask(); + context.CallStack.Clear(); + context.Functions.Clear(); } bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, BoundingBox& result) @@ -240,20 +249,14 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa case 401: { // Prepare graph data - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _emitter = emitter; - _effect = effect; - _deltaTime = 0.0f; - _viewTask = effect->GetRenderTask(); - _callStack.Clear(); + auto& context = Context.Get(); + Init(emitter, effect, data); // Find the maximum radius of the particle light float maxRadius = 0.0f; for (int32 particleIndex = 0; particleIndex < count; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const float radius = (float)GetValue(module->GetBox(1), 3); if (radius > maxRadius) maxRadius = radius; @@ -370,14 +373,8 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff const int32 stride = buffer->Stride; // Prepare graph data - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _emitter = emitter; - _effect = effect; - _deltaTime = 0.0f; - _viewTask = effect->GetRenderTask(); - _callStack.Clear(); + Init(emitter, effect, data); + auto& context = Context.Get(); // Draw lights for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.LightModules.Count(); moduleIndex++) @@ -405,7 +402,7 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff for (int32 particleIndex = 0; particleIndex < count; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Vector4 color = (Vector4)GetValue(module->GetBox(0), 2); const float radius = (float)GetValue(module->GetBox(1), 3); @@ -429,15 +426,8 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt, bool canSpawn) { // Prepare data - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _emitter = emitter; - _effect = effect; - _particleIndex = 0; - _deltaTime = dt; - _viewTask = effect->GetRenderTask(); - _callStack.Clear(); + Init(emitter, effect, data, dt); + auto& context = Context.Get(); auto& cpu = data.Buffer->CPU; // Update particles @@ -452,20 +442,20 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE // Dead particles removal if (_graph._attrAge != -1 && _graph._attrLifetime != -1) { - byte* agePtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrAge].Offset; - byte* lifetimePtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrLifetime].Offset; + byte* agePtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrAge].Offset; + byte* lifetimePtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrLifetime].Offset; for (int32 particleIndex = 0; particleIndex < cpu.Count; particleIndex++) { if (*(float*)agePtr >= *(float*)lifetimePtr) { cpu.Count--; - Platform::MemoryCopy(_data->Buffer->GetParticleCPU(particleIndex), _data->Buffer->GetParticleCPU(cpu.Count), _data->Buffer->Stride); + Platform::MemoryCopy(data.Buffer->GetParticleCPU(particleIndex), data.Buffer->GetParticleCPU(cpu.Count), data.Buffer->Stride); particleIndex--; } else { - agePtr += _data->Buffer->Stride; - lifetimePtr += _data->Buffer->Stride; + agePtr += data.Buffer->Stride; + lifetimePtr += data.Buffer->Stride; } } } @@ -474,12 +464,12 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE // Debug validation for NANs in data if (_graph._attrPosition != -1) { - byte* positionPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrPosition].Offset; + byte* positionPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrPosition].Offset; for (int32 particleIndex = 0; particleIndex < cpu.Count; particleIndex++) { Vector3 pos = *((Vector3*)positionPtr); ASSERT(!pos.IsNanOrInfinity()); - positionPtr += _data->Buffer->Stride; + positionPtr += data.Buffer->Stride; } } #endif @@ -487,26 +477,26 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE // Euler integration if (_graph._attrPosition != -1 && _graph._attrVelocity != -1) { - byte* positionPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrPosition].Offset; - byte* velocityPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrVelocity].Offset; + byte* positionPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrPosition].Offset; + byte* velocityPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrVelocity].Offset; for (int32 particleIndex = 0; particleIndex < cpu.Count; particleIndex++) { - *((Vector3*)positionPtr) += *((Vector3*)velocityPtr) * _deltaTime; - positionPtr += _data->Buffer->Stride; - velocityPtr += _data->Buffer->Stride; + *((Vector3*)positionPtr) += *((Vector3*)velocityPtr) * dt; + positionPtr += data.Buffer->Stride; + velocityPtr += data.Buffer->Stride; } } // Angular Euler Integration if (_graph._attrRotation != -1 && _graph._attrAngularVelocity != -1) { - byte* rotationPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrRotation].Offset; - byte* angularVelocityPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrAngularVelocity].Offset; + byte* rotationPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrRotation].Offset; + byte* angularVelocityPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrAngularVelocity].Offset; for (int32 particleIndex = 0; particleIndex < cpu.Count; particleIndex++) { - *((Vector3*)rotationPtr) += *((Vector3*)angularVelocityPtr) * _deltaTime; - rotationPtr += _data->Buffer->Stride; - angularVelocityPtr += _data->Buffer->Stride; + *((Vector3*)rotationPtr) += *((Vector3*)angularVelocityPtr) * dt; + rotationPtr += data.Buffer->Stride; + angularVelocityPtr += data.Buffer->Stride; } } @@ -545,16 +535,14 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE // Sort ribbon particles if (cpu.RibbonOrder.IsEmpty()) { - cpu.RibbonOrder.Resize(_graph.RibbonRenderingModules.Count() * _data->Buffer->Capacity); + cpu.RibbonOrder.Resize(_graph.RibbonRenderingModules.Count() * data.Buffer->Capacity); } - ASSERT(cpu.RibbonOrder.Count() == _graph.RibbonRenderingModules.Count() * _data->Buffer->Capacity); + ASSERT(cpu.RibbonOrder.Count() == _graph.RibbonRenderingModules.Count() * data.Buffer->Capacity); for (int32 i = 0; i < _graph.RibbonRenderingModules.Count(); i++) { const auto module = _graph.RibbonRenderingModules[i]; - - ParticleBufferCPUDataAccessor sortKeyData(_data->Buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[1])); - - int32* ribbonOrderData = cpu.RibbonOrder.Get() + module->Ribbon.RibbonOrderOffset; + ParticleBufferCPUDataAccessor sortKeyData(data.Buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[1])); + int32* ribbonOrderData = cpu.RibbonOrder.Get() + module->RibbonOrderOffset; for (int32 j = 0; j < cpu.Count; j++) { @@ -567,23 +555,13 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE } } } - - // Cleanup - _data = nullptr; } int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt) { // Prepare data - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _emitter = emitter; - _effect = effect; - _particleIndex = 0; - _deltaTime = dt; - _viewTask = effect->GetRenderTask(); - _callStack.Clear(); + auto& context = Context.Get(); + Init(emitter, effect, data, dt); // Spawn particles int32 spawnCount = 0; @@ -592,16 +570,14 @@ int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, Par spawnCount += ProcessSpawnModule(i); } - // Cleanup - _data = nullptr; - return spawnCount; } VisjectExecutor::Value ParticleEmitterGraphCPUExecutor::eatBox(Node* caller, Box* box) { // Check if graph is looped or is too deep - if (_callStack.Count() >= PARTICLE_EMITTER_MAX_CALL_STACK) + auto& context = Context.Get(); + if (context.CallStack.Count() >= PARTICLE_EMITTER_MAX_CALL_STACK) { OnError(caller, box, TEXT("Graph is looped or too deep!")); return Value::Zero; @@ -615,7 +591,7 @@ VisjectExecutor::Value ParticleEmitterGraphCPUExecutor::eatBox(Node* caller, Box #endif // Add to the calling stack - _callStack.Add(caller); + context.CallStack.Add(caller); // Call per group custom processing event Value value; @@ -624,12 +600,13 @@ VisjectExecutor::Value ParticleEmitterGraphCPUExecutor::eatBox(Node* caller, Box (this->*func)(box, parentNode, value); // Remove from the calling stack - _callStack.RemoveLast(); + context.CallStack.RemoveLast(); return value; } VisjectExecutor::Graph* ParticleEmitterGraphCPUExecutor::GetCurrentGraph() const { - return _graphStack.Peek(); + auto& context = Context.Get(); + return context.GraphStack.Peek(); } diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h index 34d7cec2e..587999aae 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h @@ -7,6 +7,7 @@ #include "Engine/Particles/ParticlesData.h" #include "Engine/Visject/VisjectGraph.h" #include "Engine/Core/Collections/Dictionary.h" +#include "Engine/Threading/ThreadLocal.h" struct RenderContext; class ParticleEffect; @@ -31,16 +32,6 @@ class ParticleEmitterGraphCPUExecutor; class ParticleEmitterGraphCPUBox : public VisjectGraphBox { -public: - - ParticleEmitterGraphCPUBox() - { - } - - ParticleEmitterGraphCPUBox(void* parent, byte id, VariantType type) - : VisjectGraphBox(parent, id, type) - { - } }; class ParticleEmitterGraphCPUNode : public ParticleEmitterGraphNode> @@ -54,23 +45,13 @@ public: union { - /// - /// The spiral position module progress value. - /// - float SpiralModuleProgress; - - struct - { - int32 RibbonOrderOffset; - } Ribbon; + int32 CustomDataOffset; + int32 RibbonOrderOffset; }; -public: - /// /// True if this node uses the per-particle data resolve instead of optimized whole-collection fetch. /// - /// True if use per particle data resolve, otherwise can optimize resolve pass. FORCE_INLINE bool UsePerParticleDataResolve() const { return UsesParticleData || !IsConstant; @@ -83,32 +64,31 @@ public: class ParticleEmitterGraphCPU : public ParticleEmitterGraph, ParticleEmitterGraphCPUNode, Variant> { friend ParticleEmitterGraphCPUExecutor; + typedef ParticleEmitterGraph, ParticleEmitterGraphCPUNode, Variant> Base; private: + struct NodeState + { + union + { + int32 SpiralProgress; + }; + }; Array _defaultParticleData; public: + // Size of the custom pre-node data buffer used for state tracking (eg. position on spiral arc progression). + int32 CustomDataSize = 0; + /// /// Creates the default surface graph (the main root node) for the particle emitter. Ensure to dispose the previous graph data before. /// void CreateDefault(); -public: - - /// - /// Determines whenever this emitter uses lights rendering. - /// - /// True if emitter uses lights rendering, otherwise false. - FORCE_INLINE bool UsesLightRendering() const - { - return LightModules.HasItems(); - } - /// /// Gets the position attribute offset from the particle data layout start (in bytes). /// - /// The offset in bytes. FORCE_INLINE int32 GetPositionAttributeOffset() const { return _attrPosition != -1 ? Layout.Attributes[_attrPosition].Offset : -1; @@ -117,7 +97,6 @@ public: /// /// Gets the age attribute offset from the particle data layout start (in bytes). /// - /// The offset in bytes. FORCE_INLINE int32 GetAgeAttributeOffset() const { return _attrAge != -1 ? Layout.Attributes[_attrAge].Offset : -1; @@ -127,37 +106,35 @@ public: // [ParticleEmitterGraph] bool Load(ReadStream* stream, bool loadMeta) override; - - bool onNodeLoaded(Node* n) override - { - ParticleEmitterGraph::onNodeLoaded(n); - return VisjectGraph::onNodeLoaded(n); - } - -protected: - - // [ParticleEmitterGraph] void InitializeNode(Node* node) override; }; +/// +/// The CPU particles emitter graph evaluation context. +/// +struct ParticleEmitterGraphCPUContext +{ + float DeltaTime; + uint32 ParticleIndex; + ParticleEmitterInstance* Data; + ParticleEmitter* Emitter; + ParticleEffect* Effect; + class SceneRenderTask* ViewTask; + Array> CallStack; + Array> GraphStack; + Dictionary Functions; +}; + /// /// The Particle Emitter Graph simulation on a CPU. /// class ParticleEmitterGraphCPUExecutor : public VisjectExecutor { private: - - // Runtime ParticleEmitterGraphCPU& _graph; - float _deltaTime; - uint32 _particleIndex; - ParticleEmitterInstance* _data; - ParticleEmitter* _emitter; - ParticleEffect* _effect; - class SceneRenderTask* _viewTask; - Array> _callStack; - Array> _graphStack; - Dictionary _functions; + + // Per-thread context to allow async execution + static ThreadLocal Context; public: @@ -167,11 +144,6 @@ public: /// The graph to execute. explicit ParticleEmitterGraphCPUExecutor(ParticleEmitterGraphCPU& graph); - /// - /// Finalizes an instance of the class. - /// - ~ParticleEmitterGraphCPUExecutor(); - /// /// Computes the local bounds of the particle emitter instance. /// @@ -214,6 +186,7 @@ public: private: + void Init(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt = 0.0f); Value eatBox(Node* caller, Box* box) override; Graph* GetCurrentGraph() const override; @@ -226,17 +199,7 @@ private: int32 ProcessSpawnModule(int32 index); void ProcessModule(ParticleEmitterGraphCPUNode* node, int32 particlesStart, int32 particlesEnd); - Value TryGetValue(Box* box, int32 defaultValueBoxIndex, const Value& defaultValue) - { - const auto parentNode = box->GetParent(); - if (box->HasConnection()) - return eatBox(parentNode, box->FirstConnection()); - if (parentNode->Values.Count() > defaultValueBoxIndex) - return parentNode->Values[defaultValueBoxIndex]; - return defaultValue; - } - - Value GetValue(Box* box, int32 defaultValueBoxIndex) + FORCE_INLINE Value GetValue(Box* box, int32 defaultValueBoxIndex) { const auto parentNode = box->GetParent(); if (box->HasConnection()) diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index 4d353b3ed..8ee2d20d0 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -52,36 +52,18 @@ public: /// /// The Particle Emitter Graph used to simulate particles. /// -template -class ParticleEmitterGraph : public Base +template +class ParticleEmitterGraph : public BaseType { public: typedef ValueType Value; - /// - /// The particle emitter module types. - /// enum class ModuleType { - /// - /// The spawn module. - /// Spawn, - - /// - /// The init module. - /// Initialize, - - /// - /// The update module. - /// Update, - - /// - /// The render module. - /// Render, }; @@ -172,8 +154,6 @@ public: bool UsesVolumetricFogRendering = false; -protected: - virtual void InitializeNode(NodeType* node) { // Skip if already initialized @@ -314,10 +294,8 @@ protected: } // Particle Emitter Function case GRAPH_NODE_MAKE_TYPE(14, 300): - { node->Assets[0] = Content::LoadAsync((Guid)node->Values[0]); break; - } // Particle Index case GRAPH_NODE_MAKE_TYPE(14, 301): node->UsesParticleData = true; @@ -566,7 +544,7 @@ public: UsesVolumetricFogRendering = false; // Base - Base::Clear(); + BaseType::Clear(); } bool Load(ReadStream* stream, bool loadMeta) override @@ -575,7 +553,7 @@ public: Version++; // Base - if (Base::Load(stream, loadMeta)) + if (BaseType::Load(stream, loadMeta)) return true; // Compute particle data layout and initialize used nodes (for only used nodes, start depth searching rom the modules) @@ -678,6 +656,6 @@ public: } } - return Base::onNodeLoaded(n); + return BaseType::onNodeLoaded(n); } }; diff --git a/Source/Engine/Particles/ParticleEmitter.cpp b/Source/Engine/Particles/ParticleEmitter.cpp index b5c3ecdae..75de7b697 100644 --- a/Source/Engine/Particles/ParticleEmitter.cpp +++ b/Source/Engine/Particles/ParticleEmitter.cpp @@ -105,7 +105,7 @@ Asset::LoadResult ParticleEmitter::load() EnablePooling = root->Values[3].AsBool; CustomBounds = (BoundingBox)root->Values[4]; UseAutoBounds = root->Values[5].AsBool; - IsUsingLights = Graph.UsesLightRendering(); + IsUsingLights = Graph.LightModules.HasItems(); } // Select simulation mode diff --git a/Source/Engine/Particles/ParticleEmitterFunction.cpp b/Source/Engine/Particles/ParticleEmitterFunction.cpp index 24282f083..b02c42cbd 100644 --- a/Source/Engine/Particles/ParticleEmitterFunction.cpp +++ b/Source/Engine/Particles/ParticleEmitterFunction.cpp @@ -24,6 +24,14 @@ Asset::LoadResult ParticleEmitterFunction::load() MemoryReadStream stream(surfaceChunk->Get(), surfaceChunk->Size()); if (Graph.Load(&stream, false)) return LoadResult::Failed; + for (int32 i = 0; i < Graph.Nodes.Count(); i++) + { + // Initialize all used nodes (starting from function output as roots) + if (Graph.Nodes[i].Type == GRAPH_NODE_MAKE_TYPE(16, 2)) + { + Graph.InitializeNode(&Graph.Nodes[i]); + } + } #if COMPILE_WITH_PARTICLE_GPU_GRAPH stream.SetPosition(0); if (GraphGPU.Load(&stream, false)) diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 468906a3b..17c1e3da4 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -335,7 +335,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa break; int32 count = buffer->CPU.Count; ASSERT(buffer->CPU.RibbonOrder.Count() == emitter->Graph.RibbonRenderingModules.Count() * buffer->Capacity); - int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->Ribbon.RibbonOrderOffset; + int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->RibbonOrderOffset; ParticleBufferCPUDataAccessor positionData(buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[0])); @@ -483,7 +483,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa Vector2 uvOffset = module->Values[5].AsVector2(); ParticleBufferCPUDataAccessor sortKeyData(buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[1])); - int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->Ribbon.RibbonOrderOffset; + int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->RibbonOrderOffset; int32 count = buffer->CPU.Count; // Setup ribbon data @@ -1073,6 +1073,7 @@ ParticleBuffer* Particles::AcquireParticleBuffer(ParticleEmitter* emitter) if (emitter->EnablePooling && EnableParticleBufferPooling) { + PoolLocker.Lock(); const auto entries = Pool.TryGet(emitter); if (entries) { @@ -1087,7 +1088,6 @@ ParticleBuffer* Particles::AcquireParticleBuffer(ParticleEmitter* emitter) { Delete(result); result = nullptr; - if (entries->IsEmpty()) { Pool.Remove(emitter); @@ -1096,6 +1096,7 @@ ParticleBuffer* Particles::AcquireParticleBuffer(ParticleEmitter* emitter) } } } + PoolLocker.Unlock(); } if (!result) diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index e85665afa..97c687685 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -24,6 +24,7 @@ void ParticleEmitterInstance::ClearState() Version = 0; Time = 0; SpawnModulesData.Clear(); + CustomData.Clear(); #if COMPILE_WITH_GPU_PARTICLES GPU.DeltaTime = 0.0f; GPU.SpawnCount = 0; @@ -61,12 +62,16 @@ void ParticleEmitterInstance::Sync(ParticleSystemInstance& systemInstance, Parti if (SpawnModulesData.Count() != emitter->Graph.SpawnModules.Count()) { SpawnModulesData.Resize(emitter->Graph.SpawnModules.Count(), false); - SpawnerData data; data.SpawnCounter = 0; data.NextSpawnTime = 0; SpawnModulesData.SetAll(data); } + if (CustomData.Count() != emitter->Graph.CustomDataSize) + { + CustomData.Resize(emitter->Graph.CustomDataSize, false); + Platform::MemoryClear(CustomData.Get(), CustomData.Count()); + } } // Sync buffer version diff --git a/Source/Engine/Particles/ParticlesSimulation.h b/Source/Engine/Particles/ParticlesSimulation.h index c9230c1c6..7008957f0 100644 --- a/Source/Engine/Particles/ParticlesSimulation.h +++ b/Source/Engine/Particles/ParticlesSimulation.h @@ -83,8 +83,10 @@ public: /// Array SpawnModulesData; -#if COMPILE_WITH_GPU_PARTICLES + // Custom per-node data (eg. position on spiral module for arc progress tracking) + Array CustomData; +#if COMPILE_WITH_GPU_PARTICLES struct { /// @@ -97,7 +99,6 @@ public: /// int32 SpawnCount; } GPU; - #endif ///