Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
ParticleEmitter.cpp
Go to the documentation of this file.
1
2
4
5
9
24
25namespace Divide
26{
27namespace
28{
29 // 3 should always be enough for round-robin GPU updates to avoid stalls:
30 // 1 in ram, 1 in driver and 1 in VRAM
35
37}
38
39ParticleEmitter::ParticleEmitter( const ResourceDescriptor<ParticleEmitter>& descriptor )
40 : SceneNode(descriptor,
41 GetSceneNodeType<ParticleEmitter>(),
45{
46 _buffersDirty.fill(false);
47}
48
49ParticleEmitter::~ParticleEmitter()
50{
51 assert(_particles == nullptr);
52}
53
54bool ParticleEmitter::load( PlatformContext& context )
55{
56 for ( U8 i = 0u; i < s_MaxPlayerBuffers; ++i )
57 {
58 for ( U8 j = 0u; j < to_base( RenderStage::COUNT ); ++j )
59 {
60 _particleGPUBuffers[i][j] = context.gfx().newGVD( g_particleBufferSizeFactor, Util::StringFormat( "{}_buffer_{}_{}", resourceName(), i, j ).c_str() );
61 }
62 }
63
64 return SceneNode::load( context );
65}
66
67GenericVertexData& ParticleEmitter::getDataBuffer(const RenderStage stage, const PlayerIndex idx)
68{
69 return *_particleGPUBuffers[idx % s_MaxPlayerBuffers][to_U32(stage)];
70}
71
72bool ParticleEmitter::initData(const std::shared_ptr<ParticleData>& particleData)
73{
74 // assert if double init!
75 DIVIDE_ASSERT(particleData != nullptr, "ParticleEmitter::updateData error: Invalid particle data!");
76 _particles = particleData;
77 const vector<vec3<F32>>& geometry = particleData->particleGeometryVertices();
78 const vector<U32>& indices = particleData->particleGeometryIndices();
79
80 for (U8 i = 0u; i < s_MaxPlayerBuffers; ++i)
81 {
82 for (U8 j = 0u; j < to_base(RenderStage::COUNT); ++j)
83 {
84 GenericVertexData& buffer = getDataBuffer(static_cast<RenderStage>(j), i);
85
86 GenericVertexData::SetBufferParams params = {};
87 params._bindConfig = { g_particleGeometryBuffer, g_particleGeometryBuffer };
88 params._bufferParams._elementCount = to_U32(geometry.size());
89 params._bufferParams._elementSize = sizeof(vec3<F32>);
90 params._bufferParams._flags._updateFrequency = BufferUpdateFrequency::ONCE;
91 params._bufferParams._flags._updateUsage = BufferUpdateUsage::CPU_TO_GPU;
92 params._initialData = { (bufferPtr)geometry.data(), geometry.size() * params._bufferParams._elementSize};
93 params._useRingBuffer = false;
94
95 {
96 const BufferLock lock = buffer.setBuffer(params);
97 DIVIDE_UNUSED(lock);
98 }
99
100 if (!indices.empty())
101 {
102 GenericVertexData::IndexBuffer idxBuff{};
103 idxBuff.smallIndices = false;
104 idxBuff.count = to_U32(indices.size());
105 idxBuff.data = (bufferPtr)indices.data();
106 idxBuff.dynamic = false;
107
108 const BufferLock lock = buffer.setIndexBuffer(idxBuff);
109 DIVIDE_UNUSED(lock);
110 }
111 }
112 }
113
114 const U32 particleCount = _particles->totalCount();
115
116 for ( U8 i = 0; i < s_MaxPlayerBuffers; ++i )
117 {
118 for ( U8 j = 0; j < to_base( RenderStage::COUNT ); ++j )
119 {
120 GenericVertexData& buffer = getDataBuffer( static_cast<RenderStage>(j), i );
121
122 GenericVertexData::SetBufferParams params = {};
123 params._bindConfig = { g_particlePositionBuffer, g_particlePositionBuffer };
124 params._useRingBuffer = true;
125
126 params._bufferParams._elementCount = particleCount;
127 params._bufferParams._elementSize = sizeof( vec4<F32> );
128 params._bufferParams._flags._updateFrequency = BufferUpdateFrequency::OCASSIONAL;
129 params._bufferParams._flags._updateUsage = BufferUpdateUsage::CPU_TO_GPU;
130 BufferLock lock = buffer.setBuffer( params );
131 DIVIDE_UNUSED( lock );
132
133 params._bindConfig = { g_particleColourBuffer, g_particleColourBuffer };
134 params._bufferParams._elementCount = particleCount;
135 params._bufferParams._elementSize = sizeof( UColour4 );
136
137 lock = buffer.setBuffer( params );
138 DIVIDE_UNUSED( lock );
139 }
140 }
141
142 for ( U32 i = 0; i < particleCount; ++i )
143 {
144 // Distance to camera (squared)
145 _particles->_misc[i].w = -1.0f;
146 }
147
148 const PrimitiveTopology topology = _particles->particleGeometryType();
149 AttributeMap vertexFormat{};
150 {
151 AttributeDescriptor& desc = vertexFormat._attributes[to_base(AttribLocation::POSITION)];
152 desc._vertexBindingIndex = g_particleGeometryBuffer;
153 desc._componentsPerElement = 3u;
154 desc._dataType = GFXDataFormat::FLOAT_32;
155
156 auto& vertBinding = vertexFormat._vertexBindings.emplace_back();
157 vertBinding._bufferBindIndex = desc._vertexBindingIndex;
158 vertBinding._strideInBytes = 3 * sizeof(F32);
159 }
160 {
161 AttributeDescriptor& desc = vertexFormat._attributes[to_base(AttribLocation::NORMAL)];
162 desc._vertexBindingIndex = g_particlePositionBuffer;
163 desc._componentsPerElement = 4u;
164 desc._dataType = GFXDataFormat::FLOAT_32;
165 desc._normalized = false;
166 desc._strideInBytes = 0u;
167
168 auto& vertBinding = vertexFormat._vertexBindings.emplace_back();
169 vertBinding._bufferBindIndex = desc._vertexBindingIndex;
170 vertBinding._strideInBytes = 3 * sizeof( F32 );
171 vertBinding._perVertexInputRate = false;
172 }
173 {
174 AttributeDescriptor& desc = vertexFormat._attributes[to_base(AttribLocation::COLOR)];
175 desc._vertexBindingIndex = g_particleColourBuffer;
176 desc._componentsPerElement = 4u;
177 desc._dataType = GFXDataFormat::UNSIGNED_BYTE;
178 desc._normalized = true;
179 desc._strideInBytes = 0u;
180
181 auto& vertBinding = vertexFormat._vertexBindings.emplace_back();
182 vertBinding._bufferBindIndex = desc._vertexBindingIndex;
183 vertBinding._strideInBytes = 3 * sizeof( F32 );
184 }
185
186 const bool useTexture = !_particles->_textureFileName.empty();
187 Handle<Material> matHandle = CreateResource(ResourceDescriptor<Material>(useTexture ? "Material_particles_Texture" : "Material_particles"));
188 Material* mat = Get(matHandle);
189
190 mat->setPipelineLayout(topology, vertexFormat);
191 mat->computeRenderStateCBK([]([[maybe_unused]] Material* material, [[maybe_unused]] const RenderStagePass stagePass, RenderStateBlock& blockInOut)
192 {
193 blockInOut._cullMode = CullMode::NONE;
194 });
195
196 mat->computeShaderCBK([useTexture]([[maybe_unused]] Material* material, const RenderStagePass stagePass)
197 {
198 ShaderModuleDescriptor vertModule{ ShaderType::VERTEX, "particles.glsl", useTexture ? "WithTexture" : "NoTexture" };
199 ShaderModuleDescriptor fragModule{ ShaderType::FRAGMENT, "particles.glsl", useTexture ? "WithTexture" : "NoTexture" };
200
201 if (useTexture)
202 {
203 fragModule._defines.emplace_back("HAS_TEXTURE");
204 }
205
206 ShaderProgramDescriptor particleShaderDescriptor = {};
207 particleShaderDescriptor._name = useTexture ? "particles_WithTexture" : "particles_NoTexture";
208 particleShaderDescriptor._modules.push_back(vertModule);
209
210 if (stagePass._stage == RenderStage::DISPLAY)
211 {
212 if (IsDepthPass(stagePass))
213 {
214 fragModule._variant = "PrePass";
215 fragModule._defines.emplace_back("PRE_PASS");
216 particleShaderDescriptor._name = useTexture ? "particles_prePass_WithTexture" : "particles_prePass_NoTexture";
217 }
218 particleShaderDescriptor._modules.push_back(fragModule);
219 }
220 else if (IsDepthPass(stagePass))
221 {
222 if (stagePass._stage == RenderStage::SHADOW)
223 {
224 fragModule._variant = "Shadow.VSM";
225 particleShaderDescriptor._modules.push_back(fragModule);
226 particleShaderDescriptor._name = "particles_VSM";
227 }
228 else
229 {
230 particleShaderDescriptor._name = "particles_DepthPass";
231 }
232 }
233
234 return particleShaderDescriptor;
235 });
236
237 if ( useTexture )
238 {
239 ResourceDescriptor<Texture> texture( _particles->_textureFileName );
240 TextureDescriptor& textureDescriptor = texture._propertyDescriptor;
241 textureDescriptor._texType = TextureType::TEXTURE_2D_ARRAY;
242 textureDescriptor._packing = GFXImagePacking::NORMALIZED_SRGB;
243
244 mat->setTexture(TextureSlot::UNIT0, texture, {}, TextureOperation::NONE);
245 }
246
247 setMaterialTpl(matHandle);
248
249 return true;
250}
251
252bool ParticleEmitter::unload()
253{
254 WAIT_FOR_CONDITION(getState() == ResourceState::RES_LOADED);
255 _particles.reset();
256
257 return SceneNode::unload();
258}
259
260void ParticleEmitter::buildDrawCommands(SceneGraphNode* sgn, GenericDrawCommandContainer& cmdsOut)
261{
262 const U32 idxCount = to_U32( _particles->particleGeometryIndices().size() );
263 if (idxCount > 0)
264 {
265 GenericDrawCommand& cmd = cmdsOut.emplace_back();
266 toggleOption(cmd, CmdRenderOptions::RENDER_INDIRECT);
267
268 cmd._cmd.indexCount = idxCount;
269 }
270
271 SceneNode::buildDrawCommands(sgn, cmdsOut);
272}
273
274void ParticleEmitter::prepareRender(SceneGraphNode* sgn,
275 RenderingComponent& rComp,
276 RenderPackage& pkg,
277 GFX::MemoryBarrierCommand& postDrawMemCmd,
278 const RenderStagePass renderStagePass,
279 const CameraSnapshot& cameraSnapshot,
280 const bool refreshData)
281{
282 if ( _enabled && getAliveParticleCount() > 0)
283 {
284 Wait(*_bufferUpdate, sgn->context().taskPool(TaskPoolType::HIGH_PRIORITY));
285 if (refreshData && _buffersDirty[to_U32(renderStagePass._stage)])
286 {
287 GenericVertexData& buffer = getDataBuffer(renderStagePass._stage, 0);
288 postDrawMemCmd._bufferLocks.emplace_back(buffer.updateBuffer(g_particlePositionBuffer, 0u, to_U32(_particles->_renderingPositions.size()), _particles->_renderingPositions.data()));
289 postDrawMemCmd._bufferLocks.emplace_back(buffer.updateBuffer(g_particleColourBuffer, 0u, to_U32(_particles->_renderingColours.size()), _particles->_renderingColours.data()));
290
291 buffer.incQueue();
292 _buffersDirty[to_U32(renderStagePass._stage)] = false;
293 }
294
295 RenderingComponent::DrawCommands& cmds = rComp.drawCommands();
296 {
297 LockGuard<SharedMutex> w_lock(cmds._dataLock);
298 GenericDrawCommand& cmd = cmds._data.front();
299 cmd._cmd.instanceCount = to_U32(_particles->_renderingPositions.size());
300 cmd._sourceBuffer = getDataBuffer(renderStagePass._stage, 0).handle();
301 }
302
303 if (renderStagePass._passType == RenderPassType::PRE_PASS)
304 {
305 const vec3<F32>& eyePos = cameraSnapshot._eye;
306 const U32 aliveCount = getAliveParticleCount();
307
308 vector<vec4<F32>>& misc = _particles->_misc;
309 vector<vec4<F32>>& pos = _particles->_position;
310
311
312 ParallelForDescriptor descriptor = {};
313 descriptor._iterCount = aliveCount;
314 descriptor._partitionSize = 1000u;
315 Parallel_For( sgn->context().taskPool( TaskPoolType::HIGH_PRIORITY ), descriptor, [&eyePos, &misc, &pos](const Task*, const U32 start, const U32 end)
316 {
317 for (U32 i = start; i < end; ++i)
318 {
319 misc[i].w = pos[i].xyz.distanceSquared(eyePos);
320 }
321 });
322
323 _bufferUpdate = CreateTask(
324 [this, &renderStagePass](const Task&)
325 {
326 // invalidateCache means that the existing particle data is no longer partially sorted
327 _particles->sort();
328 _buffersDirty[to_U32(renderStagePass._stage)] = true;
329 });
330
331 Start(*_bufferUpdate, sgn->context().taskPool(TaskPoolType::HIGH_PRIORITY));
332 }
333 }
334
335 SceneNode::prepareRender(sgn, rComp, pkg, postDrawMemCmd, renderStagePass, cameraSnapshot, refreshData);
336}
337
338
340void ParticleEmitter::sceneUpdate(const U64 deltaTimeUS,
341 SceneGraphNode* sgn,
342 SceneState& sceneState)
343{
344 constexpr U32 s_particlesPerThread = 1024;
345
346 if (_enabled)
347 {
348 U32 aliveCount = getAliveParticleCount();
349 renderState().drawState(aliveCount > 0);
350
351 const TransformComponent* transform = sgn->get<TransformComponent>();
352
353 const vec3<F32>& pos = transform->getWorldPosition();
354 const Quaternion<F32>& rot = transform->getWorldOrientation();
355
356 F32 averageEmitRate = 0;
357 for (const std::shared_ptr<ParticleSource>& source : _sources)
358 {
359 source->updateTransform(pos, rot);
360 source->emit(g_updateInterval, _particles);
361 averageEmitRate += source->emitRate();
362 }
363 averageEmitRate /= _sources.size();
364
365 aliveCount = getAliveParticleCount();
366
367
368 ParallelForDescriptor descriptor = {};
369 descriptor._iterCount = aliveCount;
370 descriptor._partitionSize = s_particlesPerThread;
371 Parallel_For( sgn->context().taskPool( TaskPoolType::HIGH_PRIORITY ), descriptor, [this](const Task*, const U32 start, const U32 end)
372 {
373 for (U32 i = start; i < end; ++i)
374 {
375 _particles->_position[i].w = _particles->_misc[i].z;
376 _particles->_acceleration[i].set(0.0f);
377 }
378 });
379
380 ParticleData& data = *_particles;
381 for (const std::shared_ptr<ParticleUpdater>& up : _updaters)
382 {
383 up->update(g_updateInterval, data);
384 }
385
386 Wait(*_bbUpdate, sgn->context().taskPool(TaskPoolType::HIGH_PRIORITY));
387
388 _bbUpdate = CreateTask([this, aliveCount, averageEmitRate](const Task&)
389 {
390 BoundingBox aabb{};
391 for (U32 i = 0; i < aliveCount; i += to_U32(averageEmitRate) / 4)
392 {
393 aabb.add(_particles->_position[i]);
394 }
395 setBounds(aabb);
396 });
397 Start(*_bbUpdate, sgn->context().taskPool(TaskPoolType::HIGH_PRIORITY));
398 }
399
400 SceneNode::sceneUpdate(deltaTimeUS, sgn, sceneState);
401}
402
403U32 ParticleEmitter::getAliveParticleCount() const noexcept
404{
405 return _particles ? _particles->aliveCount() : 0u;
406}
407
408}
#define WAIT_FOR_CONDITION(...)
#define DIVIDE_UNUSED(X)
#define DIVIDE_ASSERT(...)
bool load(PlatformContext &context) override
Loading and unloading interface.
Definition: SceneNode.cpp:98
constexpr T MillisecondsToMicroseconds(U a) noexcept
Definition: MathHelper.inl:749
Str StringFormat(const char *fmt, Args &&...args)
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
constexpr SceneNodeType GetSceneNodeType()
Definition: SceneNodeFwd.h:111
static constexpr bool IsDepthPass(const RenderStagePass stagePass) noexcept
constexpr U32 to_U32(const T value)
void Wait(const Task &task, TaskPool &pool)
Definition: Task.cpp:20
uint8_t U8
Task * CreateTask(Predicate &&threadedFunction, bool allowedInIdle=true)
Definition: TaskPool.inl:45
void toggleOption(GenericDrawCommand &cmd, CmdRenderOptions option) noexcept
FORCE_INLINE Handle< T > CreateResource(const ResourceDescriptor< T > &descriptor, bool &wasInCache, std::atomic_uint &taskCounter)
void Start(Task &task, TaskPool &pool, TaskPriority priority=TaskPriority::DONT_CARE, const DELEGATE< void > &onCompletionFunction={})
Definition: Task.cpp:9
PropertyDescriptor< Texture > TextureDescriptor
void Parallel_For(TaskPool &pool, const ParallelForDescriptor &descriptor, const DELEGATE< void, const Task *, U32, U32 > &cbk)
Definition: TaskPool.cpp:428
PropertyDescriptor< ShaderProgram > ShaderProgramDescriptor
vec4< U8 > UColour4
Definition: MathHelper.h:72
FORCE_INLINE T * Get(const Handle< T > handle)
uint32_t U32
void * bufferPtr
uint64_t U64
constexpr auto to_base(const Type value) -> Type