diff --git a/Assets/Shaders/ParticleEmit_CS.glsl b/Assets/Shaders/ParticleEmit_CS.glsl index 421e2ce1..33beaccc 100644 --- a/Assets/Shaders/ParticleEmit_CS.glsl +++ b/Assets/Shaders/ParticleEmit_CS.glsl @@ -7,7 +7,7 @@ struct EmitterParameters vec4 angularMin; vec4 angularMax; vec4 lifeAndSizeRange; // min life, max life, min size, max size -} +}; struct ParticleData { @@ -18,7 +18,7 @@ struct ParticleData vec4 scaleAndDecay; float life; uint textureIndex; -} +}; struct GenericData { @@ -96,9 +96,15 @@ float rand(inout uint state) return float(x)*uintBitsToFloat(0x2f800004u); } +float map(float value, float inMin, float inMax, float outMin, float outMax) +{ + return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin); +} + void main() { uint emitterInvocationIndex = gl_GlobalInvocationID.x; + vec4 emitterPosition = emitterPushConstant.emitterPosition; if (emitterInvocationIndex >= emitterPushConstant.emissionCount) return; @@ -109,9 +115,18 @@ void main() ParticleData particle; - int index = freelist.freeIndices[freelistIndex]; - particle.position = emitterPosition; - particle.life = emitterParams.10.0f; + // Get seed for randomization + uint pixel_index = uint (emitterPosition.x + emitterPosition.y + floatBitsToUint(genericDataBuffer.data.elapsedTime) * (gl_GlobalInvocationID.x + 1)); + uint seed = pcg_hash (pixel_index); - particles[index] = particle; + int index = freelist.freeIndices[freelistIndex]; + + // emit particle from emitter position + particle.position = vec4 (emitterPosition.xyz, 1.0f); + + // randomize life value that ranges from minLife to maxLife + particle.life = map (rand(seed), -1.0f, 1.0f, emitterParams.data.lifeAndSizeRange.x, emitterParams.data.lifeAndSizeRange.y); + + + inputParticles.data[index] = particle; } \ No newline at end of file diff --git a/Assets/Shaders/ParticleEmit_CS.shshaderb b/Assets/Shaders/ParticleEmit_CS.shshaderb new file mode 100644 index 00000000..e0318886 Binary files /dev/null and b/Assets/Shaders/ParticleEmit_CS.shshaderb differ diff --git a/Assets/Shaders/ParticleEmit_CS.shshaderb.shmeta b/Assets/Shaders/ParticleEmit_CS.shshaderb.shmeta new file mode 100644 index 00000000..f7c25e30 --- /dev/null +++ b/Assets/Shaders/ParticleEmit_CS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: ParticleEmit_CS +ID: 49959611 +Type: 2 diff --git a/Assets/Shaders/ParticleUpdate_CS.glsl b/Assets/Shaders/ParticleUpdate_CS.glsl index 7649ee63..89c0ae6c 100644 --- a/Assets/Shaders/ParticleUpdate_CS.glsl +++ b/Assets/Shaders/ParticleUpdate_CS.glsl @@ -19,7 +19,7 @@ struct ParticleData vec4 scaleAndDecay; float life; uint textureIndex; -} +}; struct GenericData { @@ -73,10 +73,10 @@ layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData uint indices[]; }; -layout (std140, set = 2, binding = 5) coherent restrict uniform IndirectDrawArgs +layout (std140, set = 2, binding = 5) coherent restrict buffer IndirectDrawArgs { DrawArraysIndirectArgs indirectArgs; -}; +}; // push constants layout(std140, push_constant) uniform EmitterPushConstant @@ -116,17 +116,12 @@ void main() ParticleData particle = inputParticles.data[index]; - if (particle.lifetime > 0.0f) + if (particle.life > 0.0f) { - // particle.position += particle.velocity * dt; - - // particle.lifetime -= dt; - // particle.size -= 1.2f * dt; - // particle.color += 1.0f * dt; - if (particle.lifetime < 0.0f || particle.size < 0.0f) + if (particle.life < 0.0f || particle.scaleAndDecay.x < 0.0f || particle.scaleAndDecay.y < 0.0f) { - particle.lifetime = 0.0f; + particle.life = 0.0f; particle.position.x = 99999.0f; outputParticles.data[index] = particle; diff --git a/Assets/Shaders/ParticleUpdate_CS.shshaderb b/Assets/Shaders/ParticleUpdate_CS.shshaderb new file mode 100644 index 00000000..53c89d01 Binary files /dev/null and b/Assets/Shaders/ParticleUpdate_CS.shshaderb differ diff --git a/Assets/Shaders/ParticleUpdate_CS.shshaderb.shmeta b/Assets/Shaders/ParticleUpdate_CS.shshaderb.shmeta new file mode 100644 index 00000000..cf9a5051 --- /dev/null +++ b/Assets/Shaders/ParticleUpdate_CS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: ParticleUpdate_CS +ID: 36260925 +Type: 2 diff --git a/Assets/Shaders/Particle_FS.glsl b/Assets/Shaders/Particle_FS.glsl new file mode 100644 index 00000000..d365b110 --- /dev/null +++ b/Assets/Shaders/Particle_FS.glsl @@ -0,0 +1,15 @@ +#version 460 core + +layout (location = 0) out vec4 fragColor; + +// between shader stages +layout(location = 0) in struct +{ + vec2 uv; // location = 0 + +} In; + +void main () +{ + fragColor = vec4 (1.0f); +} diff --git a/Assets/Shaders/Particle_FS.shshaderb b/Assets/Shaders/Particle_FS.shshaderb new file mode 100644 index 00000000..19e503b2 Binary files /dev/null and b/Assets/Shaders/Particle_FS.shshaderb differ diff --git a/Assets/Shaders/Particle_FS.shshaderb.shmeta b/Assets/Shaders/Particle_FS.shshaderb.shmeta new file mode 100644 index 00000000..afe2e1e6 --- /dev/null +++ b/Assets/Shaders/Particle_FS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: Particle_FS +ID: 42509714 +Type: 2 diff --git a/Assets/Shaders/Particle_VS.glsl b/Assets/Shaders/Particle_VS.glsl new file mode 100644 index 00000000..07594fbe --- /dev/null +++ b/Assets/Shaders/Particle_VS.glsl @@ -0,0 +1,93 @@ +#version 460 core + +struct GenericData +{ + //! Delta time + float dt; + + //! Elapsed time of the application + float elapsedTime; + + //! Viewport width of the scene (excluding imgui, that means smaller than window) + uint viewportWidth; + + //! Ditto but for height + uint viewportHeight; +}; + + +struct ParticleData +{ + vec4 position; + vec4 rotation; + vec4 velocity; + vec4 acceleration; + vec4 scaleAndDecay; + float life; + uint textureIndex; +}; + +layout (set = 0, binding = 0) uniform GenericDataBuffer +{ + GenericData data; +} genericDataBuffer; + + +layout(set = 1, binding = 0) uniform CameraData +{ + vec4 position; + mat4 vpMat; + mat4 viewMat; + mat4 projMat; +} cameraData; + +// output buffer not needed +layout (std430, set = 2, binding = 2) coherent restrict buffer ParticlesOutputBuffer +{ + ParticleData data[]; +} outputParticles; + +layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData +{ + uint indices[]; +}; + +// between shader stages +layout(location = 0) out struct +{ + vec2 uv; // location = 0 + +} Out; + +vec2 CreateQuad (in uint vertexID) +{ + uint b = 1 << vertexID; + return vec2 ((0x3 & b) != 0, (0x9 & b) != 0); +} + +void main() +{ + // Create a quad and its texture coordinates + Out.uv = CreateQuad (gl_VertexIndex); + vec3 vertexPos = vec3 (Out.uv - vec2(0.5f), 0.0f); + + ParticleData particle = outputParticles.data[indices[gl_InstanceIndex]]; + + vec3 normalized = normalize (particle.velocity.xyz); + float angle = atan (normalized.y, normalized.x); + vec2 particleScaleData = particle.scaleAndDecay.xz; // x and y + + mat4 localModel = mat4 (1.0f); + localModel[0][0] = particleScaleData.x; + localModel[1][1] = particleScaleData.y; + + mat4 rotate = mat4(1.0f); + rotate[0][0] = cos(angle); + rotate[0][1] = sin(angle); + rotate[1][0] = -sin(angle); + rotate[1][1] = cos(angle); + localModel = rotate * localModel; + + localModel[3] = vec4 (particle.position.xyz, 1.0f); + gl_Position = cameraData.vpMat * localModel * vec4(vertexPos, 1.0f); +} \ No newline at end of file diff --git a/Assets/Shaders/Particle_VS.shshaderb b/Assets/Shaders/Particle_VS.shshaderb new file mode 100644 index 00000000..00b333cb Binary files /dev/null and b/Assets/Shaders/Particle_VS.shshaderb differ diff --git a/Assets/Shaders/Particle_VS.shshaderb.shmeta b/Assets/Shaders/Particle_VS.shshaderb.shmeta new file mode 100644 index 00000000..8b44df47 --- /dev/null +++ b/Assets/Shaders/Particle_VS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: Particle_VS +ID: 35035037 +Type: 2 diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 03a37a69..388667a3 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -30,6 +30,7 @@ #include "../SHEditorWindowManager.h" #include "../AssetBrowser/SHAssetBrowser.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" +#include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h" #include "Animation/SHAnimationClip.h" namespace SHADE @@ -804,4 +805,32 @@ namespace SHADE ImGui::PopID(); } + template<> + static void DrawComponent(SHParticleEmitterComponent* component) + { + if (!component) + return; + + ImGui::PushID(SHFamilyID::GetID()); + + const auto componentType = rttr::type::get(*component); + + SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); + + ImGui::SameLine(); + if (ImGui::CollapsingHeader(componentType.get_name().data(), ImGuiTreeNodeFlags_DefaultOpen)) + { + DrawContextMenu(component); + + SHEditorWidgets::DragFloat("Emission Count", [comp = component]() {return comp->GetEmissionCount(); }, [comp = component](float count) {comp->SetEmissionCount(count);}); + SHEditorWidgets::CheckBox("Is Passive", [comp = component]() {return comp->GetPassive(); }, [comp = component](bool flag) {comp->SetPassive(flag); }); + + } + else + { + DrawContextMenu(component); + } + ImGui::PopID(); + } + } diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp index 37b89883..3aea863c 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp @@ -450,7 +450,7 @@ namespace SHADE */ /***************************************************************************/ - void SHVkCommandBuffer::DrawMultiIndirect(Handle indirectDrawData, uint32_t drawCount) + void SHVkCommandBuffer::DrawMultiIndirectIndexed(Handle indirectDrawData, uint32_t drawCount) { if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING) { @@ -462,6 +462,19 @@ namespace SHADE vkCommandBuffer.drawIndexedIndirect(indirectDrawData->GetVkBuffer(), 0, drawCount, sizeof(vk::DrawIndexedIndirectCommand)); } + void SHVkCommandBuffer::DrawMultiIndirect(Handle indirectDrawData, uint32_t drawCount) + { + if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING) + { + SHLOG_ERROR("Command buffer must have started recording before a pipeline can be bound."); + return; + } + + if (indirectDrawData) + vkCommandBuffer.drawIndirect(indirectDrawData->GetVkBuffer(), 0, drawCount, sizeof(vk::DrawIndexedIndirectCommand)); + + } + void SHVkCommandBuffer::ComputeDispatch(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) noexcept { vkCommandBuffer.dispatch (groupCountX, groupCountY, groupCountZ); diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h index c6a17d2a..c42ff33c 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h @@ -128,9 +128,10 @@ namespace SHADE void BindDescriptorSet (Handle descSetGroup, SH_PIPELINE_TYPE bindPoint, uint32_t firstSet, std::span const dynamicOffsets); // Draw Commands - void DrawArrays (uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) const noexcept; - void DrawIndexed (uint32_t indexCount, uint32_t firstIndex, uint32_t vertexOffset) const noexcept; - void DrawMultiIndirect (Handle indirectDrawData, uint32_t drawCount); + void DrawArrays (uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) const noexcept; + void DrawIndexed (uint32_t indexCount, uint32_t firstIndex, uint32_t vertexOffset) const noexcept; + void DrawMultiIndirectIndexed (Handle indirectDrawData, uint32_t drawCount); + void DrawMultiIndirect (Handle indirectDrawData, uint32_t drawCount); // Compute Commands void ComputeDispatch (uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) noexcept; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 4aa33de5..e2a6ec66 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -755,7 +755,7 @@ namespace SHADE } } - cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); + cmdBuffer->DrawMultiIndirectIndexed(drawDataBuffer[frameIndex], static_cast(drawData.size())); cmdBuffer->EndLabeledSegment(); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp index 76d34ff7..d07ad2f4 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp @@ -251,7 +251,7 @@ namespace SHADE // particle draw call binding SHVkDescriptorSetLayout::Binding particleDrawDataBinding { - .Type = vk::DescriptorType::eUniformBufferDynamic, // UBO (Because lesser data), dynamic (1 set for each frame) + .Type = vk::DescriptorType::eStorageBufferDynamic, // UBO (Because lesser data), dynamic (1 set for each frame) .Stage = vk::ShaderStageFlagBits::eCompute, .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_DRAW_DATA, .DescriptorCount = 1, diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index d9a740ac..1fb9babe 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -730,7 +730,7 @@ namespace SHADE cmdBuffer->BindVertexBuffer(COLOR_BIND_PT, batch.InstanceColorBuffer[frameIndex], 0); // Execute draw - cmdBuffer->DrawMultiIndirect(batch.MDIBuffer[frameIndex], static_cast(batch.MDIData.size())); + cmdBuffer->DrawMultiIndirectIndexed(batch.MDIBuffer[frameIndex], static_cast(batch.MDIData.size())); } void SHDebugDrawSystem::destroyBatch(MeshBatch& batch) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 69aa4188..59813cd3 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -144,6 +144,10 @@ namespace SHADE //SHAssetManager::CompileAsset("../../Assets/Shaders/Trajectory_FS.glsl", false); //SHAssetManager::CompileAsset("../../Assets/Shaders/ShadowMapBlur_CS.glsl", false); //SHAssetManager::CompileAsset("../../Assets/Shaders/Anim_VS.glsl", false); + SHAssetManager::CompileAsset("../../Assets/Shaders/Particle_VS.glsl", false); + SHAssetManager::CompileAsset("../../Assets/Shaders/Particle_FS.glsl", false); + SHAssetManager::CompileAsset("../../Assets/Shaders/ParticleEmit_CS.glsl", false); + SHAssetManager::CompileAsset("../../Assets/Shaders/ParticleUpdate_CS.glsl", false); // Load Built In Shaders static constexpr AssetID VS_DEFAULT = 39210065; defaultVertShader = SHResourceManager::LoadOrGet(VS_DEFAULT); @@ -165,6 +169,9 @@ namespace SHADE static constexpr AssetID TRAJECTORY_VS = 41042628; trajectoryVS = SHResourceManager::LoadOrGet(TRAJECTORY_VS); static constexpr AssetID TRAJECTORY_FS = 45635685; trajectoryFS = SHResourceManager::LoadOrGet(TRAJECTORY_FS); static constexpr AssetID SHADOW_BLUR_CS = 38004013; shadowMapBlurCS = SHResourceManager::LoadOrGet(SHADOW_BLUR_CS); + static constexpr AssetID PARTICLE_VS = 35035037; particleVS = SHResourceManager::LoadOrGet(PARTICLE_VS); + static constexpr AssetID PARTICLE_EMIT_CS = 42509714; particleEmitCS = SHResourceManager::LoadOrGet(PARTICLE_EMIT_CS); + static constexpr AssetID PARTICLE_UPDATE_CS = 36260925; particleUpdateCS = SHResourceManager::LoadOrGet(PARTICLE_UPDATE_CS); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index e674efb6..a199464c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -484,6 +484,10 @@ namespace SHADE Handle trajectoryVS; Handle trajectoryFS; Handle shadowMapBlurCS; + Handle particleVS; + Handle particleFS; + Handle particleEmitCS; + Handle particleUpdateCS; // Fonts Handle testFont; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp index 12690ece..c7dca2a0 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.cpp @@ -20,4 +20,24 @@ namespace SHADE toEmit = true; } + void SHParticleEmitterComponent::SetEmissionCount(float count) noexcept + { + emissionCount = count; + } + + bool SHParticleEmitterComponent::SetPassive(bool flag) noexcept + { + isPassive = flag; + } + + float SHParticleEmitterComponent::GetEmissionCount(void) noexcept + { + return emissionCount; + } + + bool SHParticleEmitterComponent::GetPassive(void) noexcept + { + return isPassive; + } + } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h index 30f7f107..7b519e85 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h @@ -105,6 +105,12 @@ namespace SHADE void Emit (void) noexcept; + void SetEmissionCount (float count) noexcept; + bool SetPassive (bool flag) noexcept; + + float GetEmissionCount (void) noexcept; + bool GetPassive (void) noexcept; + friend class SHParticleSubSystem; }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp index 7c3d9c74..95e9818f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.cpp @@ -60,7 +60,7 @@ namespace SHADE } // buffer to store draw call data. Non-indexed, host-visible mapped, triple buffered. - comp.drawCallData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmd, indirectCommands.data(), SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmd, vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eIndirectBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT); + comp.drawCallData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmd, indirectCommands.data(), SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmd, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT); } @@ -150,6 +150,59 @@ namespace SHADE void SHParticleSubSystem::RenderComponent(Handle cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept { + cmdBuffer->DrawMultiIndirect(comp.drawCallData, 1); + } + + void SHParticleSubSystem::PreparePrePostUpdateBarriers(std::vector& preUpdateBarriers, std::vector& postUpdateBarriers, SHParticleEmitterComponent const& emitter, uint32_t const EMITTER_INDEX, uint32_t const FRAME_INDEX) noexcept + { + // pre-update particles data barrier. Note that this is for input because we want the input to be available before we use it. + vk::BufferMemoryBarrier particleDataBarrier + { + .srcAccessMask = vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .buffer = emitter.particleData->GetVkBuffer(), + .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_PARTICLE_INPUT], + .size = emitter.chunkSize + }; + + // pre-update free list data barrier + vk::BufferMemoryBarrier freelistDataBarrier + { + .srcAccessMask = vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .buffer = emitter.freelistData->GetVkBuffer(), + .offset = 0, // Only 1 copy of freelist data, so offset is at 0 + .size = static_cast(sizeof (uint32_t)) * (emitter.maxParticles + 1) + }; + + // ...copy assign barriers on heap + preUpdateBarriers[EMITTER_INDEX * 2] = particleDataBarrier; + preUpdateBarriers[(EMITTER_INDEX * 2) + 1] = freelistDataBarrier; + + // make new barrier on stack... + vk::BufferMemoryBarrier particleDataBarrierPost + { + .srcAccessMask = vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .buffer = emitter.particleData->GetVkBuffer(), + .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_PARTICLE_OUTPUT], + .size = emitter.chunkSize + }; + + // make new barrier on stack... + vk::BufferMemoryBarrier indicesDataBarrier + { + .srcAccessMask = vk::AccessFlagBits::eShaderWrite, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .buffer = emitter.indicesData->GetVkBuffer(), + .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_INDICES_DATA], + .size = static_cast(sizeof(uint32_t)) * emitter.maxParticles + }; + + + // ...copy assign barriers on heap + postUpdateBarriers[EMITTER_INDEX * 2] = particleDataBarrierPost; + postUpdateBarriers[(EMITTER_INDEX * 2) + 1] = indicesDataBarrier; } @@ -203,10 +256,19 @@ namespace SHADE // Get offset into GPU emitter data (for updating) uint32_t emitterDataOffset = frameIndex * sizeof (SHParticleEmitterComponent::GPUEmitterStruct); + // TODO: OPTIONAL but eventually these barriers can be moved to the system as member variables. This would require additional bookkeeping + // but it will be more efficient than populating a vector every frame. + // Barriers to make sure emitting shader is done completely before update is run. - // Every emitter will have its own barrier. + // Every emitter will have its own barrier for its particle data and freelist data. Indices data is not needed since + // it's mainly used in update and rendering so a barrier for it is NOT needed here. std::vector preUpdateBarriers{}; - preUpdateBarriers.resize(emitters.size()); + preUpdateBarriers.resize(emitters.size() * 2); + + // After we invoke the updates for the emitters, we need to make sure all particles and indices data are done updating + // before we issue them for rendering. + std::vector postUpdateBarriers{}; + postUpdateBarriers.resize(emitters.size() * 2); // If we wanted to be VERY safe, a barrier would be good here to make sure output particles have finish reading input particles in // the update compute. HOWEVER since a NUM_FRAME_BUFFERS frames would have passed by then, we will not insert 1 here. @@ -255,19 +317,10 @@ namespace SHADE EmitComponent(cmdBuffer, emitter, frameIndex); } - // make new barrier on stack... - vk::BufferMemoryBarrier barrier - { - .srcAccessMask = vk::AccessFlagBits::eShaderWrite, - .dstAccessMask = vk::AccessFlagBits::eShaderRead, - .buffer = emitter.particleData->GetVkBuffer(), - .offset = emitter.dynamicOffsets[frameIndex][DYOFF_INDEX_PARTICLE_INPUT], - .size = emitter.chunkSize - }; - - // ...copy assign barrier on heap - preUpdateBarriers[i] = barrier; + // prepare barriers + PreparePrePostUpdateBarriers(preUpdateBarriers, postUpdateBarriers, emitter, i, frameIndex); + // Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so). emitter.toEmit = false; ++i; } @@ -275,6 +328,8 @@ namespace SHADE // issue the barrier to wait cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {}); + + /*-----------------------------------------------------------------------*/ /* EMITTING PARTICLES DONE, BEGIN UPDATES.... */ /*-----------------------------------------------------------------------*/ @@ -287,7 +342,12 @@ namespace SHADE UpdateCompoennt(cmdBuffer, emitter, frameIndex); } - + /*-----------------------------------------------------------------------*/ + /* AFTER UPDATING, RENDER PARTICLES */ + /*-----------------------------------------------------------------------*/ + // issue the barrier to wait for output particles to be done updating and indices data to finish being modified. + cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eComputeShader, {}, {}, postUpdateBarriers, {}); + } void SHParticleSubSystem::Render(Handle cmdBuffer, Handle renderer, uint32_t frameIndex) noexcept @@ -297,7 +357,7 @@ namespace SHADE // TODO: Issue barrier for output particle data. Semaphore should also be issued outside in SHGraphicsSystem for (auto& emitter : emitters) { - + RenderComponent(cmdBuffer, emitter, frameIndex); } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.h index ed697a59..9003f169 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Particles/SHParticleSubSystem.h @@ -81,6 +81,8 @@ namespace SHADE void UpdateCompoennt(Handle cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept; void RenderComponent(Handle cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept; + void PreparePrePostUpdateBarriers (std::vector& preUpdateBarriers, std::vector& postUpdateBarriers, SHParticleEmitterComponent const& emitter, uint32_t const EMITTER_INDEX, uint32_t const FRAME_INDEX) noexcept; + public: void Init(Handle device, Handle inDescPool, Handle compatibleRenderpass, Handle subpass, Handle VS, Handle FS, Handle emitCS, Handle defaultUpdateCS) noexcept; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp index f923add4..cf2046ba 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderingSubSystem.cpp @@ -198,7 +198,7 @@ namespace SHADE cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRAJECTORY_TRANSFORM, transformBuffer, 0); // call draw call - cmdBuffer->DrawMultiIndirect(drawDataBuffer, drawData.size()); + cmdBuffer->DrawMultiIndirectIndexed(drawDataBuffer, drawData.size()); // clear CPU transform and draw data transformData.clear();