Code cleanup, code comments

This commit is contained in:
Sascha Willems 2024-01-14 12:17:41 +01:00
parent 9023421b0e
commit 0888d1c9b0
2 changed files with 92 additions and 131 deletions

View file

@ -1,6 +1,8 @@
/* /*
* Vulkan Example - CPU based particle system * Vulkan Example - CPU based particle system
* *
* This sample renders a particle system that is updated on the host (by the CPU) and rendered by the GPU using a vertex buffer
*
* Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de * Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
@ -10,10 +12,11 @@
#include "VulkanglTFModel.h" #include "VulkanglTFModel.h"
#define PARTICLE_COUNT 512 #define PARTICLE_COUNT 512
#define PARTICLE_SIZE 10.0f
#define FLAME_RADIUS 8.0f #define FLAME_RADIUS 8.0f
// The particle system is made from two different particle types
// That type defines how a particle is rendered
#define PARTICLE_TYPE_FLAME 0 #define PARTICLE_TYPE_FLAME 0
#define PARTICLE_TYPE_SMOKE 1 #define PARTICLE_TYPE_SMOKE 1
@ -24,7 +27,6 @@ struct Particle {
float size; float size;
float rotation; float rotation;
uint32_t type; uint32_t type;
// Attributes not used in shader
glm::vec4 vel; glm::vec4 vel;
float rotationSpeed; float rotationSpeed;
}; };
@ -43,56 +45,60 @@ public:
vks::Texture2D colorMap; vks::Texture2D colorMap;
vks::Texture2D normalMap; vks::Texture2D normalMap;
} floor; } floor;
} textures; } textures{};
vkglTF::Model environment; vkglTF::Model environment;
// These parameters define the particle system behaviour
glm::vec3 emitterPos = glm::vec3(0.0f, -FLAME_RADIUS + 2.0f, 0.0f); glm::vec3 emitterPos = glm::vec3(0.0f, -FLAME_RADIUS + 2.0f, 0.0f);
glm::vec3 minVel = glm::vec3(-3.0f, 0.5f, -3.0f); glm::vec3 minVel = glm::vec3(-3.0f, 0.5f, -3.0f);
glm::vec3 maxVel = glm::vec3(3.0f, 7.0f, 3.0f); glm::vec3 maxVel = glm::vec3(3.0f, 7.0f, 3.0f);
struct { struct Particles {
VkBuffer buffer; VkBuffer buffer{ VK_NULL_HANDLE };
VkDeviceMemory memory; VkDeviceMemory memory{ VK_NULL_HANDLE };
// Store the mapped address of the particle data for reuse // Store the mapped address of the particle data for reuse
void *mappedMemory; void *mappedMemory;
// Size of the particle buffer in bytes // Size of the particle buffer in bytes
size_t size; size_t size{ 0 };
} particles; } particles;
struct { struct {
vks::Buffer fire; vks::Buffer particles;
vks::Buffer environment; vks::Buffer environment;
} uniformBuffers; } uniformBuffers;
struct UBOVS { struct UniformDataParticles {
glm::mat4 projection; glm::mat4 projection;
glm::mat4 modelView; glm::mat4 modelView;
// The viewport dimension is used by the particle system vertex shader
// to calculate the absolute point size based on the current viewport size
glm::vec2 viewportDim; glm::vec2 viewportDim;
float pointSize = PARTICLE_SIZE; // This is the base point size for all particles
} uboVS; float pointSize{ 10.0f };
} uniformDataParticles;
struct UBOEnv { struct UniformDataEnvironment {
glm::mat4 projection; glm::mat4 projection;
glm::mat4 modelView; glm::mat4 modelView;
glm::mat4 normal; glm::mat4 normal;
glm::vec4 lightPos = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f); glm::vec4 lightPos = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
} uboEnv; } uniformDataEnvironment;
struct { struct {
VkPipeline particles; VkPipeline particles{ VK_NULL_HANDLE };
VkPipeline environment; VkPipeline environment{ VK_NULL_HANDLE };
} pipelines; } pipelines;
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
VkDescriptorSetLayout descriptorSetLayout; VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
struct { struct {
VkDescriptorSet particles; VkDescriptorSet particles{ VK_NULL_HANDLE };
VkDescriptorSet environment; VkDescriptorSet environment{ VK_NULL_HANDLE };
} descriptorSets; } descriptorSets;
std::vector<Particle> particleBuffer; std::vector<Particle> particleBuffer{};
std::default_random_engine rndEngine; std::default_random_engine rndEngine;
@ -109,9 +115,7 @@ public:
~VulkanExample() ~VulkanExample()
{ {
// Clean up used Vulkan resources if (device) {
// Note : Inherited destructor cleans up resources stored in base class
textures.particles.smoke.destroy(); textures.particles.smoke.destroy();
textures.particles.fire.destroy(); textures.particles.fire.destroy();
textures.floor.colorMap.destroy(); textures.floor.colorMap.destroy();
@ -128,10 +132,11 @@ public:
vkFreeMemory(device, particles.memory, nullptr); vkFreeMemory(device, particles.memory, nullptr);
uniformBuffers.environment.destroy(); uniformBuffers.environment.destroy();
uniformBuffers.fire.destroy(); uniformBuffers.particles.destroy();
vkDestroySampler(device, textures.particles.sampler, nullptr); vkDestroySampler(device, textures.particles.sampler, nullptr);
} }
}
virtual void getEnabledFeatures() virtual void getEnabledFeatures()
{ {
@ -184,7 +189,7 @@ public:
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.particles, 0, nullptr); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.particles, 0, nullptr);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.particles); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.particles);
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &particles.buffer, offsets); vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &particles.buffer, offsets);
vkCmdDraw(drawCmdBuffers[i], PARTICLE_COUNT, 1, 0, 0); vkCmdDraw(drawCmdBuffers[i], static_cast<uint32_t>(particles.size), 1, 0, 0);
drawUI(drawCmdBuffers[i]); drawUI(drawCmdBuffers[i]);
@ -222,6 +227,7 @@ public:
particle->pos += glm::vec4(emitterPos, 0.0f); particle->pos += glm::vec4(emitterPos, 0.0f);
} }
// Change the type of a particle, e.g. from flame to smoke
void transitionParticle(Particle *particle) void transitionParticle(Particle *particle)
{ {
switch (particle->type) switch (particle->type)
@ -251,6 +257,7 @@ public:
} }
} }
// Initialize the particle system and create a vertex buffer for rendering the particles
void prepareParticles() void prepareParticles()
{ {
particleBuffer.resize(PARTICLE_COUNT); particleBuffer.resize(PARTICLE_COUNT);
@ -274,6 +281,7 @@ public:
VK_CHECK_RESULT(vkMapMemory(device, particles.memory, 0, particles.size, 0, &particles.mappedMemory)); VK_CHECK_RESULT(vkMapMemory(device, particles.memory, 0, particles.size, 0, &particles.mappedMemory));
} }
// Update the state of all particles
void updateParticles() void updateParticles()
{ {
float particleTimer = frameTimer * 0.45f; float particleTimer = frameTimer * 0.45f;
@ -294,12 +302,14 @@ public:
break; break;
} }
particle.rotation += particleTimer * particle.rotationSpeed; particle.rotation += particleTimer * particle.rotationSpeed;
// Transition particle state // If a particle has faded out, turn it into the other type (e.g. flame to smoke and vice versa)
if (particle.alpha > 2.0f) if (particle.alpha > 2.0f)
{ {
transitionParticle(&particle); transitionParticle(&particle);
} }
} }
// Copy the updated particles to the vertex buffer
size_t size = particleBuffer.size() * sizeof(Particle); size_t size = particleBuffer.size() * sizeof(Particle);
memcpy(particles.mappedMemory, particleBuffer.data(), size); memcpy(particles.mappedMemory, particleBuffer.data(), size);
} }
@ -345,18 +355,17 @@ public:
environment.loadFromFile(getAssetPath() + "models/fireplace.gltf", vulkanDevice, queue, glTFLoadingFlags); environment.loadFromFile(getAssetPath() + "models/fireplace.gltf", vulkanDevice, queue, glTFLoadingFlags);
} }
void setupDescriptorPool() void setupDescriptors()
{ {
// Pool
std::vector<VkDescriptorPoolSize> poolSizes = { std::vector<VkDescriptorPoolSize> poolSizes = {
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2),
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4) vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4)
}; };
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
}
void setupDescriptorSetLayout() // Layout
{
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = { std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
// Binding 0 : Vertex shader uniform buffer // Binding 0 : Vertex shader uniform buffer
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
@ -365,16 +374,10 @@ public:
// Binding 1 : Fragment shader image sampler // Binding 1 : Fragment shader image sampler
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT,2) vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT,2)
}; };
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); // Sets
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
}
void setupDescriptorSets()
{
std::vector<VkWriteDescriptorSet> writeDescriptorSets; std::vector<VkWriteDescriptorSet> writeDescriptorSets;
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
@ -382,20 +385,12 @@ public:
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.particles)); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.particles));
// Image descriptor for the color map texture // Image descriptor for the color map texture
VkDescriptorImageInfo texDescriptorSmoke = VkDescriptorImageInfo texDescriptorSmoke = vks::initializers::descriptorImageInfo(textures.particles.sampler, textures.particles.smoke.view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vks::initializers::descriptorImageInfo( VkDescriptorImageInfo texDescriptorFire = vks::initializers::descriptorImageInfo(textures.particles.sampler, textures.particles.fire.view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
textures.particles.sampler,
textures.particles.smoke.view,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
VkDescriptorImageInfo texDescriptorFire =
vks::initializers::descriptorImageInfo(
textures.particles.sampler,
textures.particles.fire.view,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
writeDescriptorSets = { writeDescriptorSets = {
// Binding 0: Vertex shader uniform buffer // Binding 0: Vertex shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSets.particles, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.fire.descriptor), vks::initializers::writeDescriptorSet(descriptorSets.particles, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.particles.descriptor),
// Binding 1: Smoke texture // Binding 1: Smoke texture
vks::initializers::writeDescriptorSet(descriptorSets.particles, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &texDescriptorSmoke), vks::initializers::writeDescriptorSet(descriptorSets.particles, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &texDescriptorSmoke),
// Binding 1: Fire texture array // Binding 1: Fire texture array
@ -405,7 +400,6 @@ public:
// Environment // Environment
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.environment)); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.environment));
writeDescriptorSets = { writeDescriptorSets = {
// Binding 0: Vertex shader uniform buffer // Binding 0: Vertex shader uniform buffer
vks::initializers::writeDescriptorSet(descriptorSets.environment, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.environment.descriptor), vks::initializers::writeDescriptorSet(descriptorSets.environment, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.environment.descriptor),
@ -419,6 +413,11 @@ public:
void preparePipelines() void preparePipelines()
{ {
// Layout
VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout));
// Pipelines
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, VK_FALSE); VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
@ -501,62 +500,33 @@ public:
void prepareUniformBuffers() void prepareUniformBuffers()
{ {
// Vertex shader uniform buffer block // Vertex shader uniform buffer block
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffers.particles, sizeof(UniformDataParticles)));
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.fire,
sizeof(uboVS)));
// Vertex shader uniform buffer block // Vertex shader uniform buffer block
VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffers.environment, sizeof(UniformDataEnvironment)));
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.environment,
sizeof(uboEnv)));
// Map persistent // Map persistent
VK_CHECK_RESULT(uniformBuffers.fire.map()); VK_CHECK_RESULT(uniformBuffers.particles.map());
VK_CHECK_RESULT(uniformBuffers.environment.map()); VK_CHECK_RESULT(uniformBuffers.environment.map());
updateUniformBuffers();
}
void updateUniformBufferLight()
{
// Environment
uboEnv.lightPos.x = sin(timer * 2.0f * float(M_PI)) * 1.5f;
uboEnv.lightPos.y = 0.0f;
uboEnv.lightPos.z = cos(timer * 2.0f * float(M_PI)) * 1.5f;
memcpy(uniformBuffers.environment.mapped, &uboEnv, sizeof(uboEnv));
} }
void updateUniformBuffers() void updateUniformBuffers()
{ {
// Particle system fire // Particle system fire
uboVS.projection = camera.matrices.perspective; uniformDataParticles.projection = camera.matrices.perspective;
uboVS.modelView = camera.matrices.view; uniformDataParticles.modelView = camera.matrices.view;
uboVS.viewportDim = glm::vec2((float)width, (float)height); uniformDataParticles.viewportDim = glm::vec2((float)width, (float)height);
memcpy(uniformBuffers.fire.mapped, &uboVS, sizeof(uboVS)); memcpy(uniformBuffers.particles.mapped, &uniformDataParticles, sizeof(UniformDataParticles));
// Environment // Environment
uboEnv.projection = camera.matrices.perspective; uniformDataEnvironment.projection = camera.matrices.perspective;
uboEnv.modelView = camera.matrices.view; uniformDataEnvironment.modelView = camera.matrices.view;
uboEnv.normal = glm::inverseTranspose(uboEnv.modelView); uniformDataEnvironment.normal = glm::inverseTranspose(uniformDataEnvironment.modelView);
memcpy(uniformBuffers.environment.mapped, &uboEnv, sizeof(uboEnv)); // Update light position
if (!paused) {
uniformDataEnvironment.lightPos.x = sin(timer * 2.0f * float(M_PI)) * 1.5f;
uniformDataEnvironment.lightPos.y = 0.0f;
uniformDataEnvironment.lightPos.z = cos(timer * 2.0f * float(M_PI)) * 1.5f;
} }
memcpy(uniformBuffers.environment.mapped, &uniformDataEnvironment, sizeof(UniformDataEnvironment));
void draw()
{
VulkanExampleBase::prepareFrame();
// Command buffer to be submitted to the queue
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
// Submit to queue
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
} }
void prepare() void prepare()
@ -565,33 +535,30 @@ public:
loadAssets(); loadAssets();
prepareParticles(); prepareParticles();
prepareUniformBuffers(); prepareUniformBuffers();
setupDescriptorSetLayout(); setupDescriptors();
preparePipelines(); preparePipelines();
setupDescriptorPool();
setupDescriptorSets();
buildCommandBuffers(); buildCommandBuffers();
prepared = true; prepared = true;
} }
void draw()
{
VulkanExampleBase::prepareFrame();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame();
}
virtual void render() virtual void render()
{ {
if (!prepared) if (!prepared)
return; return;
draw(); updateUniformBuffers();
if (!paused) if (!paused) {
{
updateUniformBufferLight();
updateParticles(); updateParticles();
} }
if (camera.updated) draw();
{
updateUniformBuffers();
}
}
virtual void viewChanged()
{
updateUniformBuffers();
} }
}; };

View file

@ -20,12 +20,6 @@ layout (binding = 0) uniform UBO
float pointSize; float pointSize;
} ubo; } ubo;
out gl_PerVertex
{
vec4 gl_Position;
float gl_PointSize;
};
void main () void main ()
{ {
outColor = inColor; outColor = inColor;