Solved particles flickering (I hope)

- Good reminder how strict Vulkan is with memory accesses
This commit is contained in:
Brandon Mak 2023-03-15 10:28:51 +08:00
parent 0bfe350477
commit ab2710da4b
8 changed files with 84 additions and 25 deletions

View File

@ -118,6 +118,9 @@ void main()
if (particle.life > 0.0f) if (particle.life > 0.0f)
{ {
// update position from velocity
// particle.position += particle.velocity * genericDataBuffer.data.dt;
// particle.life -= genericDataBuffer.data.dt;
if (particle.life < 0.0f || particle.scaleAndDecay.x < 0.0f || particle.scaleAndDecay.y < 0.0f) if (particle.life < 0.0f || particle.scaleAndDecay.x < 0.0f || particle.scaleAndDecay.y < 0.0f)
{ {

View File

@ -78,8 +78,11 @@ void main()
vec2 particleScaleData = particle.scaleAndDecay.xz; // x and y vec2 particleScaleData = particle.scaleAndDecay.xz; // x and y
mat4 localModel = mat4 (1.0f); mat4 localModel = mat4 (1.0f);
localModel[0][0] = particleScaleData.x; localModel[0][0] = 1.0f;
localModel[1][1] = particleScaleData.y; localModel[1][1] = 1.0f;
// localModel[0][0] = particleScaleData.x;
// localModel[1][1] = particleScaleData.y;
mat4 rotate = mat4(1.0f); mat4 rotate = mat4(1.0f);
rotate[0][0] = cos(angle); rotate[0][0] = cos(angle);
@ -89,6 +92,6 @@ void main()
// localModel = rotate * localModel; // localModel = rotate * localModel;
localModel[3] = vec4 (particle.position.xyz, 1.0f); localModel[3] = vec4 (particle.position.xyz, 1.0f);
gl_Position = cameraData.vpMat * localModel * vec4(vertexPos, 1.0f); // gl_Position = cameraData.vpMat * localModel * vec4(vertexPos, 1.0f);
// gl_Position = vec4(vertexPos, 1.0f); gl_Position = vec4(vertexPos, 1.0f);
} }

Binary file not shown.

View File

@ -251,7 +251,7 @@ namespace SHADE
// particle draw call binding // particle draw call binding
SHVkDescriptorSetLayout::Binding particleDrawDataBinding SHVkDescriptorSetLayout::Binding particleDrawDataBinding
{ {
.Type = vk::DescriptorType::eStorageBufferDynamic, // UBO (Because lesser data), dynamic (1 set for each frame) .Type = vk::DescriptorType::eStorageBufferDynamic, // SSBO, dynamic (1 set for each frame)
.Stage = vk::ShaderStageFlagBits::eCompute, .Stage = vk::ShaderStageFlagBits::eCompute,
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_DRAW_DATA, .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_DRAW_DATA,
.DescriptorCount = 1, .DescriptorCount = 1,

View File

@ -707,9 +707,11 @@ namespace SHADE
#endif #endif
} }
renderGraph->Begin(frameIndex); renderGraph->Begin(frameIndex);
auto cmdBuffer = renderGraph->GetCommandBuffer(frameIndex); auto cmdBuffer = renderGraph->GetCommandBuffer(frameIndex);
particleSubSystem->ResetInstanceCounts(cmdBuffer, frameIndex);
particleSubSystem->Run(cmdBuffer, frameIndex); particleSubSystem->Run(cmdBuffer, frameIndex);
renderGraph->Execute(frameIndex, descPool, MESH_DATA); renderGraph->Execute(frameIndex, descPool, MESH_DATA);

View File

@ -14,6 +14,8 @@
#include "Graphics/Pipeline/SHVkPipeline.h" #include "Graphics/Pipeline/SHVkPipeline.h"
#include "Graphics/RenderGraph/SHSubpass.h" #include "Graphics/RenderGraph/SHSubpass.h"
#include "Graphics/SHVkUtil.h" #include "Graphics/SHVkUtil.h"
#include "Graphics/Synchronization/SHVkFence.h"
namespace SHADE namespace SHADE
{ {
@ -68,7 +70,7 @@ namespace SHADE
} }
// buffer to store draw call data. Non-indexed, host-visible mapped, triple buffered. // buffer to store draw call data. Non-indexed, host-visible mapped, triple buffered.
comp.drawCallData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmdAligned, indirectCommands.data(), SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmdAligned, 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); comp.drawCallData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmdAligned, indirectCommands.data(), SHGraphicsConstants::NUM_FRAME_BUFFERS * sizeofIndirectCmdAligned, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT);
} }
@ -151,12 +153,6 @@ namespace SHADE
{ {
auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING); auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING);
uint32_t instanceCountOffset = sizeof (vk::DrawIndirectCommand) * frameIndex + offsetof(vk::DrawIndirectCommand, instanceCount);
uint32_t ZERO = 0;
// reset instance count to 0
comp.drawCallData->WriteToMemory (&ZERO, sizeof(uint32_t), 0, instanceCountOffset);
// bind the descriptor sets required for emitting particles // bind the descriptor sets required for emitting particles
cmdBuffer->BindDescriptorSet(comp.particleDescriptorSet, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), comp.dynamicOffsets[frameIndex]); cmdBuffer->BindDescriptorSet(comp.particleDescriptorSet, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), comp.dynamicOffsets[frameIndex]);
@ -170,7 +166,7 @@ namespace SHADE
//cmdBuffer->DrawArrays(4, 1, 0, 0); //cmdBuffer->DrawArrays(4, 1, 0, 0);
} }
void SHParticleSubSystem::PreparePrePostUpdateBarriers(std::vector<vk::BufferMemoryBarrier>& preUpdateBarriers, std::vector<vk::BufferMemoryBarrier>& postUpdateBarriers, SHParticleEmitterComponent const& emitter, uint32_t const EMITTER_INDEX, uint32_t const FRAME_INDEX) noexcept void SHParticleSubSystem::PreparePrePostUpdateBarriers(std::vector<vk::BufferMemoryBarrier>& preUpdateBarriers, std::vector<vk::BufferMemoryBarrier>& postUpdateBarriers, std::vector<vk::BufferMemoryBarrier>& preDrawBarriers, 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. // 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 vk::BufferMemoryBarrier particleDataBarrier
@ -219,18 +215,17 @@ namespace SHADE
// make new barrier on stack... // make new barrier on stack...
vk::BufferMemoryBarrier drawDataBarrier vk::BufferMemoryBarrier drawDataBarrier
{ {
.srcAccessMask = vk::AccessFlagBits::eShaderWrite, .srcAccessMask = vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eMemoryWrite,
.dstAccessMask = vk::AccessFlagBits::eShaderRead, .dstAccessMask = vk::AccessFlagBits::eIndirectCommandRead,
.buffer = emitter.drawCallData->GetVkBuffer(), .buffer = emitter.drawCallData->GetVkBuffer(),
.offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_DRAW_DATA], .offset = emitter.dynamicOffsets[FRAME_INDEX][DYOFF_INDEX_DRAW_DATA],
.size = static_cast<uint32_t>(sizeof(vk::DrawIndirectCommand)) .size = static_cast<uint32_t>(sizeof(vk::DrawIndirectCommand))
}; };
// ...copy assign barriers on heap // ...copy assign barriers on heap
postUpdateBarriers[EMITTER_INDEX * 3] = particleDataBarrierPost; postUpdateBarriers[EMITTER_INDEX * 2] = particleDataBarrierPost;
postUpdateBarriers[(EMITTER_INDEX * 3) + 1] = indicesDataBarrier; postUpdateBarriers[(EMITTER_INDEX * 2) + 1] = indicesDataBarrier;
postUpdateBarriers[(EMITTER_INDEX * 3) + 2] = drawDataBarrier; preDrawBarriers[EMITTER_INDEX] = drawDataBarrier;
} }
void SHParticleSubSystem::Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept void SHParticleSubSystem::Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept
@ -310,7 +305,7 @@ namespace SHADE
SHComponentManager::CreateComponentSparseSet<SHParticleEmitterComponent>(); SHComponentManager::CreateComponentSparseSet<SHParticleEmitterComponent>();
} }
void SHParticleSubSystem::Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex) noexcept void SHParticleSubSystem::Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, Handle<SHVkFence> waitFence) noexcept
{ {
float dt = static_cast<float>(SHFrameRateController::GetRawDeltaTime()); float dt = static_cast<float>(SHFrameRateController::GetRawDeltaTime());
@ -332,7 +327,11 @@ namespace SHADE
// After we invoke the updates for the emitters, we need to make sure all particles and indices data are done updating // 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. // before we issue them for rendering.
std::vector<vk::BufferMemoryBarrier> postUpdateBarriers{}; std::vector<vk::BufferMemoryBarrier> postUpdateBarriers{};
postUpdateBarriers.resize(emitters.size() * 3); postUpdateBarriers.resize(emitters.size() * 2);
std::vector<vk::BufferMemoryBarrier> preDrawBarriers{};
preDrawBarriers.resize(emitters.size());
// If we wanted to be VERY safe, a barrier would be good here to make sure output particles have finish reading input particles in // 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. // the update compute. HOWEVER since a NUM_FRAME_BUFFERS frames would have passed by then, we will not insert 1 here.
@ -350,6 +349,10 @@ namespace SHADE
SHGlobalDescriptorSets::BindGenericAndTextureData(logicalDevice, cmdBuffer, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::STATIC_DATA), frameIndex); SHGlobalDescriptorSets::BindGenericAndTextureData(logicalDevice, cmdBuffer, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::STATIC_DATA), frameIndex);
uint32_t i = 0; uint32_t i = 0;
if (waitFence)
waitFence->Wait(true);
for (auto& emitter : emitters) for (auto& emitter : emitters)
{ {
if (!emitter.initialized) if (!emitter.initialized)
@ -382,7 +385,7 @@ namespace SHADE
} }
// prepare barriers // prepare barriers
PreparePrePostUpdateBarriers(preUpdateBarriers, postUpdateBarriers, emitter, i, frameIndex); PreparePrePostUpdateBarriers(preUpdateBarriers, postUpdateBarriers, preDrawBarriers, emitter, i, frameIndex);
// Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so). // Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so).
emitter.toEmit = false; emitter.toEmit = false;
@ -398,6 +401,7 @@ namespace SHADE
/* EMITTING PARTICLES DONE, BEGIN UPDATES.... */ /* EMITTING PARTICLES DONE, BEGIN UPDATES.... */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
// bind the pipeline for updating // bind the pipeline for updating
cmdBuffer->BindPipeline(defaultUpdatePipelineData.pipeline); cmdBuffer->BindPipeline(defaultUpdatePipelineData.pipeline);
@ -411,9 +415,53 @@ namespace SHADE
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
// issue the barrier to wait for output particles to be done updating and indices data to finish being modified. // 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, {}); cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eComputeShader, {}, {}, postUpdateBarriers, {});
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eDrawIndirect, {}, {}, preDrawBarriers, {});
} }
void SHParticleSubSystem::ResetInstanceCounts(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex) noexcept
{
auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>();
std::vector<vk::BufferMemoryBarrier> preResetBarriers{};
preResetBarriers.resize(emitters.size());
uint32_t i = 0;
for (auto& emitter : emitters)
{
if (emitter.initialized)
{
// 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 drawDataHostWriteBarrier
{
.srcAccessMask = vk::AccessFlagBits::eIndirectCommandRead | vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
.dstAccessMask = vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eHostWrite,
.buffer = emitter.drawCallData->GetVkBuffer(),
.offset = emitter.dynamicOffsets[frameIndex][DYOFF_INDEX_DRAW_DATA],
.size = static_cast<uint32_t>(sizeof(vk::DrawIndirectCommand))
};
preResetBarriers[i] = drawDataHostWriteBarrier;
}
++i;
}
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eDrawIndirect, vk::PipelineStageFlagBits::eHost, {}, {}, preResetBarriers, {});
for (auto& emitter : emitters)
{
if (emitter.initialized)
{
//uint32_t instanceCountOffset = 20;
uint32_t instanceCountOffset = sizeof(vk::DrawIndirectCommand) * frameIndex + offsetof(vk::DrawIndirectCommand, instanceCount);
uint32_t ZERO = 0;
// reset instance count to 0
emitter.drawCallData->WriteToMemory(&ZERO, sizeof(uint32_t), 0, instanceCountOffset);
}
}
}
void SHParticleSubSystem::Render(Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex) noexcept void SHParticleSubSystem::Render(Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex) noexcept
{ {
auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>(); auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>();
@ -432,6 +480,7 @@ namespace SHADE
} }
} }
void SHParticleSubSystem::Exit(void) noexcept void SHParticleSubSystem::Exit(void) noexcept
{ {

View File

@ -20,6 +20,7 @@ namespace SHADE
class SHVkShaderModule; class SHVkShaderModule;
class SHRenderer; class SHRenderer;
class SHParticleEmitterComponent; class SHParticleEmitterComponent;
class SHVkFence;
@ -81,12 +82,13 @@ namespace SHADE
void UpdateCompoennt(Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept; void UpdateCompoennt(Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept;
void RenderComponent(Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept; void RenderComponent(Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept;
void PreparePrePostUpdateBarriers (std::vector<vk::BufferMemoryBarrier>& preUpdateBarriers, std::vector<vk::BufferMemoryBarrier>& postUpdateBarriers, SHParticleEmitterComponent const& emitter, uint32_t const EMITTER_INDEX, uint32_t const FRAME_INDEX) noexcept; void PreparePrePostUpdateBarriers (std::vector<vk::BufferMemoryBarrier>& preUpdateBarriers, std::vector<vk::BufferMemoryBarrier>& postUpdateBarriers, std::vector<vk::BufferMemoryBarrier>& preDrawBarriers, SHParticleEmitterComponent const& emitter, uint32_t const EMITTER_INDEX, uint32_t const FRAME_INDEX) noexcept;
public: public:
void Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept; void Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept;
void Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex) noexcept; void Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, Handle<SHVkFence> waitFence = {}) noexcept;
void ResetInstanceCounts (Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex) noexcept;
void Render(Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex) noexcept; void Render(Handle<SHVkCommandBuffer> cmdBuffer, Handle<SHRenderer> renderer, uint32_t frameIndex) noexcept;
void Exit(void) noexcept; void Exit(void) noexcept;

View File

@ -30,7 +30,7 @@ namespace SHADE
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */ /* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
bool Wait (bool waitAll, uint64_t timer) noexcept; bool Wait (bool waitAll, uint64_t timer = std::numeric_limits<uint32_t>::max()) noexcept;
void Reset (void) noexcept; void Reset (void) noexcept;
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/