Particles WIP

This commit is contained in:
Brandon Mak 2023-02-27 15:14:32 +08:00
parent fe27f8752b
commit f6db65ddf4
10 changed files with 652 additions and 18 deletions

View File

@ -0,0 +1,117 @@
#version 450
layout(local_size_x = 128) in;
struct EmitterParameters
{
vec4 angularMin;
vec4 angularMax;
vec4 lifeAndSizeRange; // min life, max life, min size, max size
}
struct ParticleData
{
vec4 position;
vec4 rotation;
vec4 velocity;
vec4 acceleration;
vec4 scaleAndDecay;
float life;
uint textureIndex;
}
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;
};
layout (set = 0, binding = 0) uniform GenericDataBuffer
{
GenericData data;
} genericDataBuffer;
layout (std430, set = 2, binding = 0) readonly buffer EmitterBuffer
{
EmitterParameters data;
} emitterParams;
layout (std430, set = 2, binding = 1) coherent restrict buffer ParticlesInputBuffer
{
ParticleData data[];
} inputParticles;
// output buffer not needed
// layout (std430, set = 2, binding = 2) coherent restrict buffer ParticlesOutputBuffer
// {
// ParticleData data[];
// } outputParticles;
layout (std430, set = 2, binding = 3) coherent restrict buffer ParticlesFreelistBuffer
{
int freeCount;
int freeIndices[];
} freelist;
// push constants
layout(std140, push_constant) uniform EmitterPushConstant
{
vec4 emitterPosition;
uint emissionCount;
} emitterPushConstant;
uint pcg_hash(uint seed)
{
uint state = seed * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
// Used to advance the PCG state.
uint rand_pcg(inout uint rng_state)
{
uint state = rng_state;
rng_state = rng_state * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
// Advances the prng state and returns the corresponding random float.
float rand(inout uint state)
{
uint x = rand_pcg(state);
state = x;
return float(x)*uintBitsToFloat(0x2f800004u);
}
void main()
{
uint emitterInvocationIndex = gl_GlobalInvocationID.x;
if (emitterInvocationIndex >= emitterPushConstant.emissionCount)
return;
int freelistIndex = atomicAdd (freelist.freeCount, -1) - 1;
if (freelistIndex < 0)
atomicAdd (freelist.freeCount, 1);
ParticleData particle;
int index = freelist.freeIndices[freelistIndex];
particle.position = emitterPosition;
particle.life = emitterParams.10.0f;
particles[index] = particle;
}

View File

@ -0,0 +1,141 @@
#version 450
layout(local_size_x = 128) in;
struct DrawArraysIndirectArgs
{
uint count;
uint instanceCount;
uint first;
uint baseInstance;
};
struct ParticleData
{
vec4 position;
vec4 rotation;
vec4 velocity;
vec4 acceleration;
vec4 scaleAndDecay;
float life;
uint textureIndex;
}
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;
};
layout(set = 1, binding = 0) uniform CameraData
{
vec4 position;
mat4 vpMat;
mat4 viewMat;
mat4 projMat;
} cameraData;
layout (set = 0, binding = 0) uniform GenericDataBuffer
{
GenericData data;
} genericDataBuffer;
layout (std430, set = 2, binding = 1) coherent restrict readonly buffer ParticlesInputBuffer
{
ParticleData data[];
} inputParticles;
// output buffer not needed
layout (std430, set = 2, binding = 2) coherent restrict buffer ParticlesOutputBuffer
{
ParticleData data[];
} outputParticles;
layout (std430, set = 2, binding = 3) coherent restrict buffer ParticlesFreelistBuffer
{
int freeCount;
int freeIndices[];
} freelist;
layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData
{
uint indices[];
};
layout (std140, set = 2, binding = 5) coherent restrict uniform IndirectDrawArgs
{
DrawArraysIndirectArgs indirectArgs;
};
// push constants
layout(std140, push_constant) uniform EmitterPushConstant
{
vec4 emitterPosition;
uint emissionCount;
} emitterPushConstant;
uint pcg_hash(uint seed)
{
uint state = seed * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
// Used to advance the PCG state.
uint rand_pcg(inout uint rng_state)
{
uint state = rng_state;
rng_state = rng_state * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
// Advances the prng state and returns the corresponding random float.
float rand(inout uint state)
{
uint x = rand_pcg(state);
state = x;
return float(x)*uintBitsToFloat(0x2f800004u);
}
void main()
{
uint index = gl_GlobalInvocationID.x;
ParticleData particle = inputParticles.data[index];
if (particle.lifetime > 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)
{
particle.lifetime = 0.0f;
particle.position.x = 99999.0f;
outputParticles.data[index] = particle;
freelist.freeIndices[atomicAdd(freelist.freeCount, 1)] = int (index);
return;
}
outputParticles.data[index] = particle;
uint drawIndex = atomicAdd (indirectArgs.instanceCount, 1);
indices[drawIndex] = index;
}
}

View File

@ -64,6 +64,7 @@ namespace SHADE
({ ({
{SHPredefinedDescriptorTypes::STATIC_DATA, 0}, {SHPredefinedDescriptorTypes::STATIC_DATA, 0},
{SHPredefinedDescriptorTypes::CAMERA, 1}, {SHPredefinedDescriptorTypes::CAMERA, 1},
{SHPredefinedDescriptorTypes::PARTICLES, 2},
}); });
} }
@ -200,6 +201,73 @@ namespace SHADE
Handle<SHVkDescriptorSetLayout> materialBoneDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout({ materialDataBinding, boneDataBinding }); Handle<SHVkDescriptorSetLayout> materialBoneDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout({ materialDataBinding, boneDataBinding });
SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, materialBoneDataPerInstanceLayout->GetVkHandle(), "[Descriptor Set Layout] Material and Bone Globals"); SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, materialBoneDataPerInstanceLayout->GetVkHandle(), "[Descriptor Set Layout] Material and Bone Globals");
// particle emitter binding
SHVkDescriptorSetLayout::Binding emitterDataBinding
{
.Type = vk::DescriptorType::eStorageBufferDynamic,
.Stage = vk::ShaderStageFlagBits::eCompute,
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_EMITTER_DATA,
.DescriptorCount = 1,
};
// particle input binding
SHVkDescriptorSetLayout::Binding particleInputDataBinding
{
.Type = vk::DescriptorType::eStorageBufferDynamic,
.Stage = vk::ShaderStageFlagBits::eCompute,
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INPUT_DATA,
.DescriptorCount = 1,
};
// particle output binding
SHVkDescriptorSetLayout::Binding particleOutputDataBinding
{
.Type = vk::DescriptorType::eStorageBufferDynamic,
.Stage = vk::ShaderStageFlagBits::eCompute | vk::ShaderStageFlagBits::eVertex,
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_OUTPUT_DATA,
.DescriptorCount = 1,
};
// particle freelist binding
SHVkDescriptorSetLayout::Binding particleFreelistBinding
{
.Type = vk::DescriptorType::eStorageBuffer, // non dynamic because we only need 1 copy for all frames
.Stage = vk::ShaderStageFlagBits::eCompute,
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_FREELIST_DATA,
.DescriptorCount = 1,
};
// particle indices binding
SHVkDescriptorSetLayout::Binding particleIndicesBinding
{
.Type = vk::DescriptorType::eStorageBufferDynamic, // dynamic because we have multiple copies.
.Stage = vk::ShaderStageFlagBits::eCompute | vk::ShaderStageFlagBits::eVertex,
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INDICES_DATA,
.DescriptorCount = 1,
};
// particle draw call binding
SHVkDescriptorSetLayout::Binding particleDrawDataBinding
{
.Type = vk::DescriptorType::eUniformBufferDynamic, // UBO (Because lesser data), dynamic (1 set for each frame)
.Stage = vk::ShaderStageFlagBits::eCompute,
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::PARTICLE_DRAW_DATA,
.DescriptorCount = 1,
};
Handle<SHVkDescriptorSetLayout> particleDescSetLayout = logicalDevice->CreateDescriptorSetLayout(
{
emitterDataBinding,
particleInputDataBinding,
particleOutputDataBinding,
particleFreelistBinding,
particleIndicesBinding,
particleDrawDataBinding
});
SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, particleDescSetLayout->GetVkHandle(), "[Descriptor Set Layout] Particle System Data");
predefinedLayouts.push_back(staticGlobalLayout); predefinedLayouts.push_back(staticGlobalLayout);
predefinedLayouts.push_back(lightDataDescSetLayout); predefinedLayouts.push_back(lightDataDescSetLayout);
predefinedLayouts.push_back(cameraDataGlobalLayout); predefinedLayouts.push_back(cameraDataGlobalLayout);
@ -207,6 +275,7 @@ namespace SHADE
predefinedLayouts.push_back(fontDataDescSetLayout); predefinedLayouts.push_back(fontDataDescSetLayout);
predefinedLayouts.push_back(shadowMapDescLayout); predefinedLayouts.push_back(shadowMapDescLayout);
predefinedLayouts.push_back(materialBoneDataPerInstanceLayout); predefinedLayouts.push_back(materialBoneDataPerInstanceLayout);
predefinedLayouts.push_back(particleDescSetLayout);
perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].descSetLayouts = GetPredefinedDescSetLayouts perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].descSetLayouts = GetPredefinedDescSetLayouts
( (
@ -245,7 +314,8 @@ namespace SHADE
perSystemData[SHUtilities::ConvertEnum(SystemType::PARTICLE_RENEDERING)].descSetLayouts = GetPredefinedDescSetLayouts perSystemData[SHUtilities::ConvertEnum(SystemType::PARTICLE_RENEDERING)].descSetLayouts = GetPredefinedDescSetLayouts
( (
SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::STATIC_DATA | SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::STATIC_DATA |
SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::CAMERA SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::CAMERA |
SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::PARTICLES
); );
} }

View File

@ -29,7 +29,8 @@ namespace SHADE
MATERIALS = 0b00001000, MATERIALS = 0b00001000,
FONT = 0b00010000, FONT = 0b00010000,
SHADOW = 0b00100000, SHADOW = 0b00100000,
MATERIAL_AND_BONES = 0b01000000 MATERIAL_AND_BONES = 0b01000000,
PARTICLES = 0b10000000
}; };
enum class SystemType enum class SystemType

View File

@ -16,5 +16,6 @@ namespace SHADE
FONT, FONT,
RENDER_GRAPH_NODE_COMPUTE_RESOURCE, RENDER_GRAPH_NODE_COMPUTE_RESOURCE,
RENDER_GRAPH_RESOURCE, RENDER_GRAPH_RESOURCE,
PARTICLES,
}; };
} }

View File

@ -229,6 +229,61 @@ namespace SHADE
*/ */
/***************************************************************************/ /***************************************************************************/
static constexpr uint32_t BONE_MATRIX_DATA = 1; static constexpr uint32_t BONE_MATRIX_DATA = 1;
/***************************************************************************/
/*!
\brief
Descriptor set binding for particle emitter data.
*/
/***************************************************************************/
static constexpr uint32_t PARTICLE_EMITTER_DATA = 0;
/***************************************************************************/
/*!
\brief
Descriptor set binding for input particle data.
*/
/***************************************************************************/
static constexpr uint32_t PARTICLE_INPUT_DATA = 1;
/***************************************************************************/
/*!
\brief
Descriptor set binding for output particle data.
*/
/***************************************************************************/
static constexpr uint32_t PARTICLE_OUTPUT_DATA = 2;
/***************************************************************************/
/*!
\brief
Descriptor set binding for particle freelist data.
*/
/***************************************************************************/
static constexpr uint32_t PARTICLE_FREELIST_DATA = 3;
/***************************************************************************/
/*!
\brief
Descriptor set binding for particle indices data.
*/
/***************************************************************************/
static constexpr uint32_t PARTICLE_INDICES_DATA = 4;
/***************************************************************************/
/*!
\brief
Descriptor set binding for bone matrix data.
*/
/***************************************************************************/
static constexpr uint32_t PARTICLE_DRAW_DATA = 5;
}; };
struct VertexBufferBindings struct VertexBufferBindings

View File

@ -7,7 +7,6 @@ namespace SHADE
{ {
timeBeforeEmission = emissionInterval; timeBeforeEmission = emissionInterval;
// initialize all buffers here
} }
void SHParticleEmitterComponent::OnDestroy(void) void SHParticleEmitterComponent::OnDestroy(void)
@ -17,7 +16,7 @@ namespace SHADE
void SHParticleEmitterComponent::Emit(void) noexcept void SHParticleEmitterComponent::Emit(void) noexcept
{ {
toEmit = true;
} }
} }

View File

@ -3,10 +3,13 @@
#include "Resource/SHHandle.h" #include "Resource/SHHandle.h"
#include "Math/Vector/SHVec4.h" #include "Math/Vector/SHVec4.h"
#include "ECS_Base/Components/SHComponent.h" #include "ECS_Base/Components/SHComponent.h"
#include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h"
namespace SHADE namespace SHADE
{ {
class SHVkBuffer; class SHVkBuffer;
class SHVkDescriptorSetGroup;
class SHVkDescriptorSetLayout;
class SHParticleEmitterComponent : public SHComponent class SHParticleEmitterComponent : public SHComponent
{ {
@ -37,15 +40,12 @@ namespace SHADE
//! Acceleration of the particle //! Acceleration of the particle
SHVec4 acceleration; SHVec4 acceleration;
//! Scale of the texture //! x scale, x scale decay, y scale and y scale decay
float scale; SHVec4 scaleAndDecay;
//! Life of the particle //! Life of the particle
float life; float life;
//! life decay rate
float lifeDecayRate;
//! Texture into the desc array that the particle is using //! Texture into the desc array that the particle is using
uint32_t textureIndex; uint32_t textureIndex;
}; };
@ -53,6 +53,9 @@ namespace SHADE
//! Max number of particles of this emitter //! Max number of particles of this emitter
uint32_t maxParticles; uint32_t maxParticles;
//! num bytes of all particles in 1 chunk (1 frame)
uint32_t chunkSize;
//! emission count per emit //! emission count per emit
uint32_t emissionCount; uint32_t emissionCount;
@ -71,18 +74,31 @@ namespace SHADE
//! Freelist data //! Freelist data
Handle<SHVkBuffer> freelistData; Handle<SHVkBuffer> freelistData;
//! Freelist data //! Indices data
Handle<SHVkBuffer> indicesData; Handle<SHVkBuffer> indicesData;
//! Freelist data //! draw call data
Handle<SHVkBuffer> drawCallData; Handle<SHVkBuffer> drawCallData;
//! We need more descriptor sets because the operations on a frame's particle data needs to rely on the previous frame's. Each set
//! will contain 2 bindings that point to 2 buffers (input and output).
Handle<SHVkDescriptorSetGroup> particleDescriptorSet;
//! Emitter's data on the CPU side. To be copied to GPU. //! Emitter's data on the CPU side. To be copied to GPU.
GPUEmitterStruct emitterDataCPU; GPUEmitterStruct cpuEmitterData;
//! If passive, emitter emits particles based on timer above. //! If passive, emitter emits particles based on timer above.
bool isPassive; bool isPassive;
//! Have the particle system initialize variables in this component when this is false
bool initialized;
//! If true, particle system will emit particles in Run function. Sets to false every frame.
bool toEmit;
//! For all the dynamic SSBOs in the descriptor set
std::array<std::array<uint32_t, 5>, SHGraphicsConstants::NUM_FRAME_BUFFERS> dynamicOffsets{};
public: public:
void OnCreate(void) override final; void OnCreate(void) override final;
void OnDestroy(void) override final; void OnDestroy(void) override final;

View File

@ -5,12 +5,147 @@
#include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h" #include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h"
#include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHComponentManager.h"
#include "Graphics/Descriptors/SHVkDescriptorSetGroup.h"
#include "Graphics/Descriptors/SHVkDescriptorSetLayout.h"
#include "Graphics/MiddleEnd/GlobalData/SHGlobalDescriptorSets.h"
#include "Math/Transform/SHTransformComponent.h"
#include "Graphics/Buffers/SHVkBuffer.h"
namespace SHADE namespace SHADE
{ {
void SHParticleSubSystem::Init(Handle<SHVkLogicalDevice> device, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept void SHParticleSubSystem::InitializeComponent(SHParticleEmitterComponent& comp) noexcept
{ {
uint32_t emitterStructAligned = logicalDevice->PadSSBOSize(sizeof (SHParticleEmitterComponent::GPUEmitterStruct));
uint32_t particleStructAligned = logicalDevice->PadUBOSize(sizeof (SHParticleEmitterComponent::GPUParticleStruct));
uint32_t sizeofIndirectCmd = static_cast<uint32_t>(sizeof(vk::DrawIndirectCommand));
uint32_t sizeofUint = static_cast<uint32_t>(sizeof(uint32_t));
// TODO: temporary only.
static constexpr uint32_t NUM_PARTICLES = 500;
comp.maxParticles = NUM_PARTICLES;
// offset into the buffer for input and output
uint32_t const PARTICLE_FRAME_CHUNK_SIZE = (particleStructAligned * comp.maxParticles);
// Buffer Initialization
{
// count, value
std::vector<uint32_t> freelistInit(comp.maxParticles + 1, 0);
freelistInit[0] = comp.maxParticles;
// Particle emitter buffer. Multiple copies, Host-visible mapped. We want multiple copies because we'll be writing to it from the CPU. We don't want to do that while the GPU
// is using it during compute operations so we write to another portion.
comp.emitterData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * emitterStructAligned, nullptr, 0, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT);
// buffer for particle data: pure GPU memory, no transfers, no flags. We want to triple buffer this so that we can submit work to the GPU
// without having to wait for rendering to finish reading the data
comp.particleData = logicalDevice->CreateBuffer(SHGraphicsConstants::NUM_FRAME_BUFFERS * particleStructAligned * NUM_PARTICLES, nullptr, 0, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, {});
// Buffer for freelist data. 1 copy only, host-visible mapped. We only need 1 copy because it is only required in compute. If it was used or read in another
// stage we would need more copies.
comp.freelistData = logicalDevice->CreateBuffer(sizeofUint * (comp.maxParticles + 1), freelistInit.data(), sizeofUint * (comp.maxParticles + 1), vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT);
// Buffer for indices. NUM_FRAME_BUFFERS copies since it's used in rendering. host-visible mapped.
comp.indicesData = logicalDevice->CreateBuffer(sizeofUint * comp.maxParticles, nullptr, 0, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT);
// Draw call data will not be tampered with after this initialization except for one variable: instanceCount, which will be modified from compute shader
std::array<vk::DrawIndirectCommand, SHGraphicsConstants::NUM_FRAME_BUFFERS> indirectCommands{};
for (auto& cmd : indirectCommands)
{
cmd.vertexCount = 4;
cmd.firstVertex = 0;
cmd.firstInstance = 0;
cmd.instanceCount = 0;
}
// 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);
}
// Descriptor set initialization
{
// Get particle desc set layout
auto descSetLayout = SHGraphicsPredefinedData::GetPredefinedDescSetLayouts(SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::PARTICLES);
// Since we are populating the set as is, the set index will be 0
static constexpr uint32_t PARTICLE_DATA_SET_INDEX = 0;
// Variable desc counts, all ignored anyway (This is required but its a dumb interface. You can only blame yourself, Brandon. )
std::vector const VARIABLE_COUNTS = {0u,0u,0u,0u,0u};
// allocate new desc set
comp.particleDescriptorSet = descPool->Allocate(descSetLayout, VARIABLE_COUNTS);
// convenience handle
auto set = comp.particleDescriptorSet;
// After buffers are created, we want to populate all bindings(6) with the buffers
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_EMITTER_DATA, { &comp.emitterData, 1 }, 0, emitterStructAligned);
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INPUT_DATA, { &comp.particleData, 1 }, 0, PARTICLE_FRAME_CHUNK_SIZE); // input and output will be th same until we bind using dynamic offsets
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_OUTPUT_DATA, { &comp.particleData, 1 }, 0, PARTICLE_FRAME_CHUNK_SIZE);
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_FREELIST_DATA, { &comp.freelistData, 1 }, 0, sizeofUint * (comp.maxParticles + 1));
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_INDICES_DATA, { &comp.indicesData, 1 }, 0, sizeofUint * (comp.maxParticles));
set->ModifyWriteDescBuffer(PARTICLE_DATA_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PARTICLE_DRAW_DATA, { &comp.drawCallData, 1 }, 0, sizeofIndirectCmd);
}
comp.initialized = true;
comp.timeBeforeEmission = comp.emissionInterval;
comp.toEmit = false;
comp.chunkSize = PARTICLE_FRAME_CHUNK_SIZE;
for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i)
{
auto& offsets = comp.dynamicOffsets[i];
uint32_t inputOffset = PARTICLE_FRAME_CHUNK_SIZE * ((i + SHGraphicsConstants::NUM_FRAME_BUFFERS - 1) % SHGraphicsConstants::NUM_FRAME_BUFFERS); // take previous frame's data
uint32_t outputOffset = PARTICLE_FRAME_CHUNK_SIZE * i;
// In the order of:
// 1. Emitter data
// 2. Particle input
// 3. Particle output
// 4. Particle draw data
offsets[DYOFF_INDEX_EMITTER] = i * emitterStructAligned;
offsets[DYOFF_INDEX_PARTICLE_INPUT] = inputOffset;
offsets[DYOFF_INDEX_PARTICLE_OUTPUT] = outputOffset;
offsets[DYOFF_INDEX_INDICES_DATA] = i * sizeofIndirectCmd;
offsets[DYOFF_INDEX_DRAW_DATA] = i * sizeofUint * comp.maxParticles;
}
}
void SHParticleSubSystem::EmitComponent(Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept
{
auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING);
auto* transform = SHComponentManager::GetComponent<SHTransformComponent>(comp.GetEID());
// bind the descriptor sets required for emitting particles
cmdBuffer->BindDescriptorSet(comp.particleDescriptorSet, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), comp.dynamicOffsets[frameIndex]);
cmdBuffer->SetPushConstantVariable("EmitterPushConstant.emitterPosition", transform->GetWorldPosition(), SH_PIPELINE_TYPE::COMPUTE);
cmdBuffer->SetPushConstantVariable("EmitterPushConstant.emissionCount", comp.emissionCount, SH_PIPELINE_TYPE::COMPUTE);
// emit particles
cmdBuffer->ComputeDispatch((comp.emissionCount / EMITTER_WORKGROUP_SIZE) + 1, 1, 1);
}
void SHParticleSubSystem::UpdateCompoennt(Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept
{
auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING);
// bind the descriptor sets required for emitting particles
cmdBuffer->BindDescriptorSet(comp.particleDescriptorSet, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), comp.dynamicOffsets[frameIndex]);
// dispatch the compute shaders to update particles
cmdBuffer->ComputeDispatch((comp.maxParticles / EMITTER_WORKGROUP_SIZE) + 1, 1, 1);
}
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
{
descPool = inDescPool;
logicalDevice = device;
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* INITIALIZE ALL PIPELINES */ /* INITIALIZE ALL PIPELINES */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
@ -41,6 +176,7 @@ namespace SHADE
defaultUpdatePipelineData.pipelineLayout = logicalDevice->CreatePipelineLayout(defaultUpdatePlParams); defaultUpdatePipelineData.pipelineLayout = logicalDevice->CreatePipelineLayout(defaultUpdatePlParams);
defaultUpdatePipelineData.pipeline = logicalDevice->CreateComputePipeline(defaultUpdatePipelineData.pipelineLayout); defaultUpdatePipelineData.pipeline = logicalDevice->CreateComputePipeline(defaultUpdatePipelineData.pipelineLayout);
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* OTHER INITIALIZATION */ /* OTHER INITIALIZATION */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
@ -50,10 +186,35 @@ namespace SHADE
void SHParticleSubSystem::Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, float dt) noexcept void SHParticleSubSystem::Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, float dt) noexcept
{ {
auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>(); auto& emitters = SHComponentManager::GetDense<SHParticleEmitterComponent>();
auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::PARTICLE_RENEDERING);
// Get offset into GPU emitter data (for updating)
uint32_t emitterDataOffset = frameIndex * sizeof (SHParticleEmitterComponent::GPUEmitterStruct);
//! Barriers to make sure emitting shader is done completely before update is run.
//! Every emitter will have its own barrier.
std::vector<vk::BufferMemoryBarrier> preUpdateBarriers{};
preUpdateBarriers.resize(emitters.size());
/*-----------------------------------------------------------------------*/
/* BEGIN EMITTING PARTICES */
/*-----------------------------------------------------------------------*/
// TODO: Might need to issue a barrier here for input particle data
// bind the pipeline for emitting particles
cmdBuffer->BindPipeline(emittingPipelineData.pipeline);
// Generic data
SHGlobalDescriptorSets::BindGenericAndTextureData(logicalDevice, cmdBuffer, SH_PIPELINE_TYPE::COMPUTE, mappings.at(SHPredefinedDescriptorTypes::STATIC_DATA), frameIndex);
uint32_t i = 0;
for (auto& emitter : emitters) for (auto& emitter : emitters)
{ {
// Emit emitters here if there are ready to be emitted if (!emitter.initialized)
InitializeComponent(emitter);
// Set emitter emit flag to true here if there are ready to be emitted
if (emitter.isPassive) if (emitter.isPassive)
{ {
// decrement emission timer // decrement emission timer
@ -65,15 +226,60 @@ namespace SHADE
// reset timer // reset timer
emitter.timeBeforeEmission = emitter.emissionInterval; emitter.timeBeforeEmission = emitter.emissionInterval;
// Call dispatch here to emit particles // Emit later
emitter.toEmit = true;
} }
} }
if (emitter.toEmit) // take note that if emitter is not passive, this can also be set to true outside of this function
{
// Copy data to host visible buffer of emitter
emitter.emitterData->WriteToMemory (&emitter.cpuEmitterData, sizeof (SHParticleEmitterComponent::GPUEmitterStruct), 0, emitterDataOffset);
// Call emit function here
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;
emitter.toEmit = false;
++i;
}
// issue the barrier to wait
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {});
/*-----------------------------------------------------------------------*/
/* EMITTING PARTICLES DONE, BEGIN UPDATES.... */
/*-----------------------------------------------------------------------*/
// bind the pipeline for updating
cmdBuffer->BindPipeline(defaultUpdatePipelineData.pipeline);
for (auto& emitter : emitters)
{
UpdateCompoennt(cmdBuffer, emitter, frameIndex);
}
} }
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>();
// TODO: Issue barrier for output particle data. Semaphore should also be issued outside in SHGraphicsSystem
} }
void SHParticleSubSystem::Exit(void) noexcept void SHParticleSubSystem::Exit(void) noexcept

View File

@ -19,12 +19,21 @@ namespace SHADE
class SHSubpass; class SHSubpass;
class SHVkShaderModule; class SHVkShaderModule;
class SHRenderer; class SHRenderer;
class SHParticleEmitterComponent;
class SHParticleSubSystem class SHParticleSubSystem
{ {
private: private:
static constexpr uint32_t EMITTER_WORKGROUP_SIZE = 128;
static constexpr uint32_t DYOFF_INDEX_EMITTER = 0;
static constexpr uint32_t DYOFF_INDEX_PARTICLE_INPUT = 1;
static constexpr uint32_t DYOFF_INDEX_PARTICLE_OUTPUT = 2;
static constexpr uint32_t DYOFF_INDEX_INDICES_DATA = 3;
static constexpr uint32_t DYOFF_INDEX_DRAW_DATA = 4;
// To hold data for a pipeline and pipeline layout. // To hold data for a pipeline and pipeline layout.
// We want this here because particles require 3 pipeline sets: // We want this here because particles require 3 pipeline sets:
// - Rendering // - Rendering
@ -39,6 +48,18 @@ namespace SHADE
Handle<SHVkPipelineLayout> pipelineLayout; Handle<SHVkPipelineLayout> pipelineLayout;
}; };
#if 0 // not used, mainly for convenience to show what shaders use
// Push constant data for emitters
struct EmitterShaderPC
{
//! Emitter position
SHVec4 emitterPosition;
//! emission count for 1 single emission
uint32_t emissionCount;
};
#endif
//! Logical device for creation and destruction //! Logical device for creation and destruction
Handle<SHVkLogicalDevice> logicalDevice; Handle<SHVkLogicalDevice> logicalDevice;
@ -51,9 +72,16 @@ namespace SHADE
//! Pipeline data for updating particles //! Pipeline data for updating particles
PipelineData defaultUpdatePipelineData; PipelineData defaultUpdatePipelineData;
//! Desc pool for particle component desc set allocation
Handle<SHVkDescriptorPool> descPool;
void InitializeComponent (SHParticleEmitterComponent& comp) noexcept;
void EmitComponent (Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept;
void UpdateCompoennt (Handle<SHVkCommandBuffer> cmdBuffer, SHParticleEmitterComponent& comp, uint32_t frameIndex) noexcept;
public: public:
void Init(Handle<SHVkLogicalDevice> device, 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, float dt) noexcept; void Run(Handle<SHVkCommandBuffer> cmdBuffer, uint32_t frameIndex, float dt) noexcept;