ParticleEmitter2Component.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Scene/Components/ParticleEmitter2Component.h>
  6. #include <AnKi/Scene/Components/MeshComponent.h>
  7. #include <AnKi/Scene/Components/MoveComponent.h>
  8. #include <AnKi/Resource/ParticleEmitterResource2.h>
  9. #include <AnKi/Resource/ResourceManager.h>
  10. #include <AnKi/Resource/MeshResource.h>
  11. #include <AnKi/GpuMemory/RebarTransientMemoryPool.h>
  12. namespace anki {
  13. // Contains some shared geometry data and it's a singleton because we don't want to be wasting time and memory for every emitter
  14. class ParticleEmitter2Component::ParticleEmitterQuadGeometry : public MakeSingletonLazyInit<ParticleEmitterQuadGeometry>
  15. {
  16. public:
  17. UnifiedGeometryBufferAllocation m_quadPositions;
  18. UnifiedGeometryBufferAllocation m_quadUvs;
  19. UnifiedGeometryBufferAllocation m_quadIndices;
  20. GpuSceneArrays::MeshLod::Allocation m_gpuSceneMeshLods;
  21. U32 m_userCount = 0;
  22. Bool m_uploadedToGpuScene = false;
  23. SpinLock m_mtx;
  24. void init()
  25. {
  26. initGeom();
  27. initGpuScene();
  28. }
  29. void initGeom()
  30. {
  31. // Allocate and populate a quad
  32. const U32 vertCount = 4;
  33. const U32 indexCount = 6;
  34. m_quadPositions = UnifiedGeometryBuffer::getSingleton().allocateFormat(kMeshRelatedVertexStreamFormats[VertexStreamId::kPosition], vertCount);
  35. m_quadUvs = UnifiedGeometryBuffer::getSingleton().allocateFormat(kMeshRelatedVertexStreamFormats[VertexStreamId::kUv], vertCount);
  36. m_quadIndices = UnifiedGeometryBuffer::getSingleton().allocateFormat(Format::kR16_Uint, indexCount);
  37. static_assert(kMeshRelatedVertexStreamFormats[VertexStreamId::kPosition] == Format::kR16G16B16A16_Unorm);
  38. WeakArray<U16Vec4> transientPositions;
  39. const BufferView positionsAlloc = RebarTransientMemoryPool::getSingleton().allocateCopyBuffer(vertCount, transientPositions);
  40. transientPositions[0] = U16Vec4(0, 0, 0, 0);
  41. transientPositions[1] = U16Vec4(kMaxU16, 0, 0, 0);
  42. transientPositions[2] = U16Vec4(kMaxU16, kMaxU16, 0, 0);
  43. transientPositions[3] = U16Vec4(0, kMaxU16, 0, 0);
  44. static_assert(kMeshRelatedVertexStreamFormats[VertexStreamId::kUv] == Format::kR32G32_Sfloat);
  45. WeakArray<Vec2> transientUvs;
  46. const BufferView uvsAlloc = RebarTransientMemoryPool::getSingleton().allocateCopyBuffer(vertCount, transientUvs);
  47. transientUvs[0] = Vec2(0.0f);
  48. transientUvs[1] = Vec2(1.0f, 0.0f);
  49. transientUvs[2] = Vec2(1.0f, 1.0f);
  50. transientUvs[3] = Vec2(0.0f, 1.0f);
  51. WeakArray<U16> transientIndices;
  52. const BufferView indicesAlloc = RebarTransientMemoryPool::getSingleton().allocateCopyBuffer(indexCount, transientIndices);
  53. transientIndices[0] = 0;
  54. transientIndices[1] = 1;
  55. transientIndices[2] = 3;
  56. transientIndices[3] = 1;
  57. transientIndices[4] = 2;
  58. transientIndices[5] = 3;
  59. CommandBufferInitInfo cmdbInit("Particle quad upload");
  60. cmdbInit.m_flags |= CommandBufferFlag::kSmallBatch;
  61. CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(cmdbInit);
  62. Buffer* dstBuff = &UnifiedGeometryBuffer::getSingleton().getBuffer();
  63. cmdb->copyBufferToBuffer(positionsAlloc, m_quadPositions);
  64. cmdb->copyBufferToBuffer(uvsAlloc, m_quadUvs);
  65. cmdb->copyBufferToBuffer(indicesAlloc, m_quadIndices);
  66. BufferBarrierInfo barrier;
  67. barrier.m_bufferView = BufferView(dstBuff);
  68. barrier.m_previousUsage = BufferUsageBit::kCopyDestination;
  69. barrier.m_nextUsage = dstBuff->getBufferUsage();
  70. cmdb->setPipelineBarrier({}, {&barrier, 1}, {});
  71. cmdb->endRecording();
  72. GrManager::getSingleton().submit(cmdb.get());
  73. }
  74. void initGpuScene()
  75. {
  76. m_gpuSceneMeshLods.allocate();
  77. }
  78. void deinit()
  79. {
  80. UnifiedGeometryBuffer::getSingleton().deferredFree(m_quadPositions);
  81. UnifiedGeometryBuffer::getSingleton().deferredFree(m_quadUvs);
  82. UnifiedGeometryBuffer::getSingleton().deferredFree(m_quadIndices);
  83. m_gpuSceneMeshLods.free();
  84. }
  85. void addUser()
  86. {
  87. LockGuard lock(m_mtx);
  88. if(m_userCount == 0)
  89. {
  90. init();
  91. }
  92. ++m_userCount;
  93. }
  94. void removeUser()
  95. {
  96. LockGuard lock(m_mtx);
  97. ANKI_ASSERT(m_userCount > 0);
  98. if(m_userCount == 1)
  99. {
  100. deinit();
  101. }
  102. --m_userCount;
  103. }
  104. // Do that outside of init() because we can only upload to the GPU scene at specific places in the frame
  105. void tryUploadToGpuScene()
  106. {
  107. LockGuard lock(m_mtx);
  108. if(!m_uploadedToGpuScene)
  109. {
  110. m_uploadedToGpuScene = true;
  111. GpuSceneMeshLod meshLod = {};
  112. meshLod.m_vertexOffsets[U32(VertexStreamId::kPosition)] =
  113. m_quadPositions.getOffset() / getFormatInfo(kMeshRelatedVertexStreamFormats[VertexStreamId::kPosition]).m_texelSize;
  114. meshLod.m_vertexOffsets[U32(VertexStreamId::kUv)] =
  115. m_quadUvs.getOffset() / getFormatInfo(kMeshRelatedVertexStreamFormats[VertexStreamId::kUv]).m_texelSize;
  116. meshLod.m_indexCount = 6;
  117. meshLod.m_firstIndex = m_quadIndices.getOffset() / sizeof(U16);
  118. meshLod.m_positionScale = 1.0f;
  119. meshLod.m_positionTranslation = Vec3(-0.5f, -0.5f, 0.0f);
  120. Array<GpuSceneMeshLod, kMaxLodCount> meshLods;
  121. meshLods.fill(meshLod);
  122. m_gpuSceneMeshLods.uploadToGpuScene(meshLods);
  123. }
  124. }
  125. };
  126. ParticleEmitter2Component::ParticleEmitter2Component(SceneNode* node)
  127. : SceneComponent(node, kClassType)
  128. {
  129. ParticleEmitterQuadGeometry::getSingleton().addUser();
  130. }
  131. ParticleEmitter2Component::~ParticleEmitter2Component()
  132. {
  133. ParticleEmitterQuadGeometry::getSingleton().removeUser();
  134. }
  135. ParticleEmitter2Component& ParticleEmitter2Component::setParticleEmitterFilename(CString fname)
  136. {
  137. ParticleEmitterResource2Ptr newRsrc;
  138. const Error err = ResourceManager::getSingleton().loadResource(fname, newRsrc);
  139. if(err)
  140. {
  141. ANKI_SCENE_LOGE("Failed to load resource: %s", fname.cstr());
  142. }
  143. else
  144. {
  145. m_particleEmitterResource = std::move(newRsrc);
  146. m_anyDirty = true;
  147. }
  148. return *this;
  149. }
  150. CString ParticleEmitter2Component::getParticleEmitterFilename() const
  151. {
  152. if(m_particleEmitterResource)
  153. {
  154. return m_particleEmitterResource->getFilename();
  155. }
  156. else
  157. {
  158. return "*Error*";
  159. }
  160. }
  161. void ParticleEmitter2Component::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)
  162. {
  163. ANKI_ASSERT(other);
  164. if(other->getType() == SceneComponentType::kMesh)
  165. {
  166. bookkeepComponent(m_meshComponent, other, added);
  167. m_anyDirty = true;
  168. }
  169. }
  170. Bool ParticleEmitter2Component::isValid() const
  171. {
  172. Bool valid = !!m_particleEmitterResource;
  173. if(m_geomType == ParticleGeometryType::kMeshComponent)
  174. {
  175. valid = valid && m_meshComponent->isValid();
  176. }
  177. return valid;
  178. }
  179. void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& updated)
  180. {
  181. m_gpuSceneReallocationsThisFrame = false;
  182. m_dt = F32(info.m_dt);
  183. if(!isValid()) [[unlikely]]
  184. {
  185. for(auto& s : m_gpuScene.m_particleStreams)
  186. {
  187. GpuSceneBuffer::getSingleton().deferredFree(s);
  188. }
  189. GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_aliveParticleIndices);
  190. GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_anKiParticleEmitterProperties);
  191. m_gpuScene.m_gpuSceneParticleEmitter.free();
  192. m_gpuSceneReallocationsThisFrame = true;
  193. return;
  194. }
  195. ParticleEmitterQuadGeometry::getSingleton().tryUploadToGpuScene();
  196. if(!m_anyDirty) [[likely]]
  197. {
  198. return;
  199. }
  200. // From now on it's dirty, do all updates
  201. m_gpuSceneReallocationsThisFrame = true; // Difficult to properly track so set it to true and don't bother
  202. m_anyDirty = false;
  203. updated = true;
  204. const ParticleEmitterResourceCommonProperties& commonProps = m_particleEmitterResource->getCommonProperties();
  205. // Streams
  206. for(ParticleProperty prop : EnumIterable<ParticleProperty>())
  207. {
  208. GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_particleStreams[prop]);
  209. m_gpuScene.m_particleStreams[prop] =
  210. GpuSceneBuffer::getSingleton().allocate(commonProps.m_particleCount * kParticlePropertySize[prop], alignof(U32));
  211. }
  212. // Alive particles index buffer
  213. {
  214. GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_aliveParticleIndices);
  215. m_gpuScene.m_aliveParticleIndices = GpuSceneBuffer::getSingleton().allocate(commonProps.m_particleCount * sizeof(U32), alignof(U32));
  216. }
  217. // AnKiParticleEmitterProperties
  218. {
  219. GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_anKiParticleEmitterProperties);
  220. ConstWeakArray<U8> prefilled = m_particleEmitterResource->getPrefilledAnKiParticleEmitterProperties();
  221. m_gpuScene.m_anKiParticleEmitterProperties = GpuSceneBuffer::getSingleton().allocate(prefilled.getSizeInBytes(), alignof(U32));
  222. GpuSceneMicroPatcher::getSingleton().newCopy(m_gpuScene.m_anKiParticleEmitterProperties, prefilled.getSizeInBytes(), prefilled.getBegin());
  223. }
  224. // GpuSceneParticleEmitter2
  225. {
  226. if(!m_gpuScene.m_gpuSceneParticleEmitter)
  227. {
  228. m_gpuScene.m_gpuSceneParticleEmitter.allocate();
  229. }
  230. GpuSceneParticleEmitter2 emitter;
  231. zeroMemory(emitter);
  232. for(ParticleProperty prop : EnumIterable<ParticleProperty>())
  233. {
  234. emitter.m_particleStateSteamOffsets[U32(prop)] = m_gpuScene.m_particleStreams[prop].getOffset();
  235. }
  236. emitter.m_aliveParticleIndicesOffset = m_gpuScene.m_aliveParticleIndices.getOffset();
  237. emitter.m_particleCount = commonProps.m_particleCount;
  238. emitter.m_emissionPeriod = commonProps.m_emissionPeriod;
  239. emitter.m_particlesPerEmission = commonProps.m_particlesPerEmission;
  240. emitter.m_particleEmitterPropertiesOffset = m_gpuScene.m_anKiParticleEmitterProperties.getOffset();
  241. if(m_geomType == ParticleGeometryType::kMeshComponent)
  242. {
  243. const MeshResource& meshr = m_meshComponent->getMeshResource();
  244. emitter.m_particleAabbMin = meshr.getBoundingShape().getMin().xyz();
  245. emitter.m_particleAabbMax = meshr.getBoundingShape().getMax().xyz();
  246. }
  247. else
  248. {
  249. emitter.m_particleAabbMin = Vec3(-0.5f);
  250. emitter.m_particleAabbMax = Vec3(+0.5f);
  251. }
  252. emitter.m_reinitializeOnNextUpdate = 1;
  253. emitter.m_worldTransformsIndex = info.m_node->getFirstComponentOfType<MoveComponent>().getGpuSceneTransformsIndex();
  254. emitter.m_uuid = getUuid();
  255. m_gpuScene.m_gpuSceneParticleEmitter.uploadToGpuScene(emitter);
  256. }
  257. }
  258. U32 ParticleEmitter2Component::getGpuSceneMeshLodIndex(U32 submeshIdx) const
  259. {
  260. ANKI_ASSERT(isValid());
  261. if(m_geomType == ParticleGeometryType::kQuad)
  262. {
  263. return ParticleEmitterQuadGeometry::getSingleton().m_gpuSceneMeshLods.getIndex() * kMaxLodCount;
  264. }
  265. else
  266. {
  267. return m_meshComponent->getGpuSceneMeshLodsIndex(submeshIdx);
  268. }
  269. }
  270. } // end namespace anki