feature/softbody-runtime-control
Benjamin Kraft 4 months ago
parent d1c32ce089
commit a3da2f8f3d
  1. 3
      include/application.hpp
  2. 20
      include/simulation.hpp
  3. 2
      include/vulkan/buffer.hpp
  4. 7
      include/vulkan/descriptor_pool.hpp
  5. 35
      shaders/normal.comp
  6. 13
      shaders/pbd.comp
  7. 196
      src/simulation.cpp
  8. 9
      src/vulkan/application.cpp
  9. 55
      src/vulkan/descriptor_pool.cpp

@ -16,6 +16,9 @@
#include <map>
#include <glm/vec2.hpp>
using std::unique_ptr, std::make_unique;
using std::vector;
class Instance;
class Swapchain;
class GraphicsPipeline;

@ -10,16 +10,20 @@ public:
Simulation();
~Simulation();
private:
Buffer* vertexBuffer;
Buffer* edgeBuffer;
Buffer* triangleBuffer;
Buffer* faceBuffer;
Buffer* tetrahedronBuffer;
unique_ptr<Buffer> vertexBuffer;
unique_ptr<Buffer> faceBuffer;
unique_ptr<Buffer> edgeBuffer;
unique_ptr<Buffer> triangleBuffer;
unique_ptr<Buffer> tetrahedronBuffer;
void createMeshBuffers();
ComputePipeline* pbdPipeline = nullptr;
ComputePipeline* normalPipeline = nullptr;
unique_ptr<Buffer> sizeInformationBuffer;
std::vector<std::unique_ptr<SoftBody>> softBodies;
unique_ptr<ComputePipeline> pbdPipeline;
unique_ptr<ComputePipeline> normalPipeline;
void createComputePipelines();
vector<unique_ptr<SoftBody>> softBodies;
void recordDrawCommands() override;
void recordComputeCommands(VkCommandBuffer cmdBuffer) override;
};

@ -19,6 +19,8 @@ public:
VmaAllocation allocation = VK_NULL_HANDLE;
VmaAllocationInfo allocationInfo {};
VkDeviceSize size;
Buffer(const Buffer& other) = delete;
Buffer& operator =(const Buffer& other) = delete;
private:
void copyTo(Buffer* dst);
};

@ -8,7 +8,8 @@ class Buffer;
enum class DescriptorSet {
WORLD = 0,
MESH = 1
MESH = 1,
SIMULATION = 2
};
class DescriptorPool {
@ -16,9 +17,9 @@ public:
DescriptorPool();
~DescriptorPool();
void bindBuffer(Buffer* buffer, VkDescriptorType type, DescriptorSet set, uint32_t binding);
void bindBuffer(const Buffer& buffer, VkDescriptorType type, DescriptorSet set, uint32_t binding);
std::map<DescriptorSet, VkDescriptorSet> sets;
std::map<DescriptorSet, VkDescriptorSetLayout> setLayouts;
std::map<DescriptorSet, VkDescriptorSetLayout> layouts;
private:
VkDescriptorPool handle = VK_NULL_HANDLE;

@ -20,10 +20,15 @@ struct Face {
layout (std430, set = 0, binding = 0) buffer VertexBuffer {
Vertex vertices[];
};
layout (std430, set = 0, binding = 3) buffer FaceBuffer {
layout (std430, set = 0, binding = 1) buffer FaceBuffer {
Face faces[];
};
layout (std140, set = 0, binding = 5) uniform Sizes {
uint vertexCount;
uint faceCount;
};
layout (push_constant) uniform PushConstants {
uint state;
@ -42,13 +47,11 @@ void atomicAddVec3(uint vID, vec3 add){
}
}
void reset(){
uint vID = gl_GlobalInvocationID.x;
void reset(uint vID){
vertices[vID].normal = floatBitsToUint(vec3(0, 0, 0));
}
void accumulate(){
uint fID = gl_GlobalInvocationID.x;
void accumulate(uint fID){
Face f = faces[fID];
Vertex v1 = vertices[f.a];
@ -61,15 +64,27 @@ void accumulate(){
atomicAddVec3(f.c, weightedNormal);
}
void norm(){
uint vID = gl_GlobalInvocationID.x;
void norm(uint vID){
vertices[vID].normal = floatBitsToUint(normalize(uintBitsToFloat(vertices[vID].normal)));
}
void main() {
uint id = gl_GlobalInvocationID.x;
switch (state){
case 0: reset(); break;
case 1: accumulate(); break;
case 2: norm(); break;
case 0:
if (id < vertexCount){
reset(id);
}
break;
case 1:
if (id < faceCount){
accumulate(id);
}
break;
case 2:
if (id < vertexCount){
norm(id);
}
break;
}
}

@ -1,6 +1,6 @@
#version 450
layout (local_size_x = 32) in;
layout (local_size_x = 256) in;
struct Vertex {
vec3 position;
@ -8,10 +8,19 @@ struct Vertex {
vec3 normal;
};
layout (std140, set = 0, binding = 0) buffer VertexBuffer {
layout (std430, set = 0, binding = 0) buffer VertexBuffer {
Vertex vertices[];
};
layout (std140, set = 1, binding = 0) uniform UBO {
float dt;
vec3 gravity;
};
layout (push_constant) uniform PushConstants {
uint state;
};
void main() {
}

@ -9,70 +9,38 @@
#include "vulkan/descriptor_pool.hpp"
Simulation::Simulation() {
Mesh sphere("models/sphere_high.ply");
Mesh bunny("models/bunny_high.ply");
auto body = std::make_unique<SoftBody>(&sphere, 0.3f);
for (size_t i = 0; i < 500; i++){
auto copy = std::make_unique<SoftBody>(*body.get());
copy->applyOffset({i / 2.f, 0, 0});
softBodies.push_back(std::move(copy));
}
vector<Vertex> vertices;
vector<Edge> edges;
vector<Triangle> triangles;
vector<Face> faces;
vector<Tetrahedron> tetrahedra;
for (const std::unique_ptr<SoftBody> &softBody : softBodies){
softBody->firstIndex = faces.size() * 3;
softBody->vertexOffset = static_cast<int32_t>(vertices.size());
vertices.insert(vertices.end(), softBody->vertices.begin(), softBody->vertices.end());
edges.insert(edges.end(), softBody->edges.begin(), softBody->edges.end());
triangles.insert(triangles.end(), softBody->triangles.begin(), softBody->triangles.end());
faces.insert(faces.end(), softBody->faces.begin(), softBody->faces.end());
tetrahedra.insert(tetrahedra.end(), softBody->tetrahedra.begin(), softBody->tetrahedra.end());
createMeshBuffers();
for (auto iter = faces.begin() + softBody->firstIndex / 3; iter != faces.end(); iter++){
iter->a += softBody->vertexOffset;
iter->b += softBody->vertexOffset;
iter->c += softBody->vertexOffset;
}
}
struct SizeInformation {
uint32_t vertexCount;
uint32_t faceCount;
class SimulationBuffer : public Buffer {
public:
SimulationBuffer(void* data, VkDeviceSize size, VkBufferUsageFlags additionalUsageFlags=0)
: Buffer(size, data, size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | additionalUsageFlags, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0) {}
uint32_t edgeCount;
uint32_t triangleCount;
uint32_t tetrahedronCount;
};
vertexBuffer = new SimulationBuffer(vertices.data(), vertices.size() * sizeof(Vertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
edgeBuffer = new SimulationBuffer(edges.data(), edges.size() * sizeof(Edge));
triangleBuffer = new SimulationBuffer(triangles.data(), triangles.size() * sizeof(Triangle));
faceBuffer = new SimulationBuffer(faces.data(), faces.size() * sizeof(Face), VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
tetrahedronBuffer = new SimulationBuffer(tetrahedra.data(), tetrahedra.size() * sizeof(Tetrahedron));
descriptorPool->bindBuffer(vertexBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 0);
descriptorPool->bindBuffer(edgeBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 1);
descriptorPool->bindBuffer(triangleBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 2);
descriptorPool->bindBuffer(faceBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 3);
descriptorPool->bindBuffer(tetrahedronBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 4);
pbdPipeline = new ComputePipeline("shaders/pbd.spv", {descriptorPool->setLayouts[DescriptorSet::MESH]});
normalPipeline = new ComputePipeline("shaders/normal.spv", {
descriptorPool->setLayouts[DescriptorSet::MESH]
},{
{
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.offset = 0,
.size = sizeof(uint32_t)
}
});
SizeInformation sizeInformation {};
sizeInformation.vertexCount = vertexBuffer->size / sizeof(Vertex);
sizeInformation.faceCount = faceBuffer->size / sizeof(Face);
sizeInformationBuffer = make_unique<Buffer>(
sizeof(SizeInformation), &sizeInformation, sizeof(sizeInformation),
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0);
descriptorPool->bindBuffer(*vertexBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 0);
descriptorPool->bindBuffer(*faceBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 1);
descriptorPool->bindBuffer(*edgeBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 2);
descriptorPool->bindBuffer(*triangleBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 3);
descriptorPool->bindBuffer(*tetrahedronBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 4);
descriptorPool->bindBuffer(*sizeInformationBuffer, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, DescriptorSet::MESH, 5);
createComputePipelines();
char* stats;
vmaBuildStatsString(Instance::instance->allocator, &stats, VK_TRUE);
// printf("%s", stats);
vmaFreeStatsString(Instance::instance->allocator, stats);
}
void Simulation::recordDrawCommands() {
@ -88,6 +56,15 @@ void Simulation::recordDrawCommands() {
}
void Simulation::recordComputeCommands(VkCommandBuffer cmdBuffer) {
#define BlOCK_SIZE 256
auto getGroupCount = [](uint32_t threads, uint32_t blockSize){
return (threads - 1) / blockSize + 1;
};
uint32_t vertexGroupCount = getGroupCount(vertexBuffer->size / sizeof(Vertex), BlOCK_SIZE);
uint32_t faceGroupCount = getGroupCount(faceBuffer->size / sizeof(Face), BlOCK_SIZE);
vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pbdPipeline->handle);
VkMemoryBarrier barrier {};
@ -100,8 +77,7 @@ void Simulation::recordComputeCommands(VkCommandBuffer cmdBuffer) {
size_t subSteps = 1;
for (size_t i = 0; i < subSteps; i++){
uint32_t preSolveInvocations = 1;
vkCmdDispatch(cmdBuffer, preSolveInvocations, 1, 1);
vkCmdDispatch(cmdBuffer, vertexGroupCount, 1, 1);
vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr);
uint32_t partitionCount = 1;
@ -110,22 +86,14 @@ void Simulation::recordComputeCommands(VkCommandBuffer cmdBuffer) {
vkCmdDispatch(cmdBuffer, partitionSize, 1, 1);
vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr);
}
uint32_t postSolveInvocations = 1;
vkCmdDispatch(cmdBuffer, postSolveInvocations, 1, 1);
vkCmdDispatch(cmdBuffer, vertexGroupCount, 1, 1);
vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr);
}
vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, normalPipeline->handle);
vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, normalPipeline->layout, 0, 1, &descriptorPool->sets[DescriptorSet::MESH], 0, nullptr);
#define BlOCK_SIZE 256
auto getGroupCount = [](uint32_t threads, uint32_t blockSize){
return (threads - 1) / blockSize + 1;
};
uint32_t vertexGroupCount = getGroupCount(vertexBuffer->size / sizeof(Vertex), BlOCK_SIZE);
uint32_t faceGroupCount = getGroupCount(faceBuffer->size / sizeof(Face), BlOCK_SIZE);
uint32_t state = 0;
vkCmdPushConstants(cmdBuffer, normalPipeline->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(uint32_t), &state);
vkCmdDispatch(cmdBuffer, vertexGroupCount, 1, 1);
@ -143,11 +111,77 @@ void Simulation::recordComputeCommands(VkCommandBuffer cmdBuffer) {
}
Simulation::~Simulation() {
delete vertexBuffer;
delete edgeBuffer;
delete triangleBuffer;
delete faceBuffer;
delete tetrahedronBuffer;
delete pbdPipeline;
delete normalPipeline;
}
void Simulation::createMeshBuffers() {
Mesh sphere("models/sphere_high.ply");
Mesh bunny("models/bunny_high.ply");
auto body = std::make_unique<SoftBody>(&sphere, 0.3f);
for (size_t i = 0; i < 500; i++){
auto copy = std::make_unique<SoftBody>(*body.get());
copy->applyOffset({i / 2.f, 0, 0});
softBodies.push_back(std::move(copy));
}
vector<Vertex> vertices;
vector<Edge> edges;
vector<Triangle> triangles;
vector<Face> faces;
vector<Tetrahedron> tetrahedra;
for (const std::unique_ptr<SoftBody> &softBody : softBodies){
softBody->firstIndex = faces.size() * 3;
softBody->vertexOffset = static_cast<int32_t>(vertices.size());
vertices.insert(vertices.end(), softBody->vertices.begin(), softBody->vertices.end());
edges.insert(edges.end(), softBody->edges.begin(), softBody->edges.end());
triangles.insert(triangles.end(), softBody->triangles.begin(), softBody->triangles.end());
faces.insert(faces.end(), softBody->faces.begin(), softBody->faces.end());
tetrahedra.insert(tetrahedra.end(), softBody->tetrahedra.begin(), softBody->tetrahedra.end());
for (auto face = faces.begin() + softBody->firstIndex / 3; face != faces.end(); face++){
face->a += softBody->vertexOffset;
face->b += softBody->vertexOffset;
face->c += softBody->vertexOffset;
}
}
class SimulationBuffer : public Buffer {
public:
SimulationBuffer(void* data, VkDeviceSize size, VkBufferUsageFlags additionalUsageFlags=0)
: Buffer(size, data, size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | additionalUsageFlags, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0) {}
};
vertexBuffer = make_unique<SimulationBuffer>(vertices.data(), vertices.size() * sizeof(Vertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
faceBuffer = make_unique<SimulationBuffer>(faces.data(), faces.size() * sizeof(Face), VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
edgeBuffer = make_unique<SimulationBuffer>(edges.data(), edges.size() * sizeof(Edge));
triangleBuffer = make_unique<SimulationBuffer>(triangles.data(), triangles.size() * sizeof(Triangle));
tetrahedronBuffer = make_unique<SimulationBuffer>(tetrahedra.data(), tetrahedra.size() * sizeof(Tetrahedron));
}
void Simulation::createComputePipelines() {
vector<VkDescriptorSetLayout> layouts;
vector<VkPushConstantRange> pushRanges;
{
layouts.push_back(descriptorPool->layouts[DescriptorSet::MESH]);
pbdPipeline = unique_ptr<ComputePipeline>(new ComputePipeline("shaders/pbd.spv", layouts));
}
layouts.clear();
pushRanges.clear();
{
layouts.push_back(descriptorPool->layouts[DescriptorSet::MESH]);
pushRanges.push_back({
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.offset = 0,
.size = sizeof(uint32_t)
});
normalPipeline = unique_ptr<ComputePipeline>(new ComputePipeline("shaders/normal.spv", layouts, pushRanges));
}
}

@ -19,7 +19,7 @@ Application::Application() {
descriptorPool = new DescriptorPool();
graphicsPipeline = new GraphicsPipeline("shaders/vert.spv", "shaders/frag.spv",
swapchain->renderPass,
{descriptorPool->setLayouts[DescriptorSet::WORLD]});
{descriptorPool->layouts[DescriptorSet::WORLD]});
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
@ -28,13 +28,8 @@ Application::Application() {
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT);
descriptorPool->bindBuffer(uniformBuffer, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, DescriptorSet::WORLD, 0);
descriptorPool->bindBuffer(*uniformBuffer, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, DescriptorSet::WORLD, 0);
camera = new Camera(swapchain->extent);
char* stats;
vmaBuildStatsString(Instance::instance->allocator, &stats, VK_TRUE);
// printf("%s", stats);
vmaFreeStatsString(Instance::instance->allocator, stats);
}
void Application::updateUniformBuffer() {

@ -18,25 +18,44 @@ DescriptorPool::DescriptorPool() {
vkCreateDescriptorPool(Instance::instance->device, &poolInfo, nullptr, &handle);
std::map<DescriptorSet, std::vector<VkDescriptorSetLayoutBinding>> setBindings;
setBindings[DescriptorSet::WORLD].push_back({
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT
});
for (uint32_t i = 0; i < 5; i++){
setBindings[DescriptorSet::MESH].push_back({
.binding = i,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
auto addBinding = [&setBindings](DescriptorSet set, VkDescriptorType type, VkShaderStageFlags stageFlags){
uint32_t binding = setBindings[set].size();
setBindings[set].push_back({
.binding = binding,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
.stageFlags = stageFlags
});
}
};
// camera
addBinding(DescriptorSet::WORLD, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT);
// vertices
addBinding(DescriptorSet::MESH, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
// faces
addBinding(DescriptorSet::MESH, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
// edges
addBinding(DescriptorSet::MESH, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
// triangles
addBinding(DescriptorSet::MESH, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
// tetrahedra
addBinding(DescriptorSet::MESH, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
// sizes
addBinding(DescriptorSet::MESH, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
// dt
addBinding(DescriptorSet::SIMULATION, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
for (const auto &[set, bindings] : setBindings)
createLayout(set, bindings);
for (const auto &[set, layout] : setLayouts){
for (const auto &[set, layout] : layouts){
VkDescriptorSetAllocateInfo allocateInfo {};
allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocateInfo.descriptorPool = handle;
@ -49,15 +68,15 @@ DescriptorPool::DescriptorPool() {
DescriptorPool::~DescriptorPool() {
vkDestroyDescriptorPool(Instance::instance->device, handle, nullptr);
for (const auto &[type, layout] : setLayouts)
for (const auto &[type, layout] : layouts)
vkDestroyDescriptorSetLayout(Instance::instance->device, layout, nullptr);
}
void DescriptorPool::bindBuffer(Buffer *buffer, VkDescriptorType type, DescriptorSet set, uint32_t binding) {
void DescriptorPool::bindBuffer(const Buffer& buffer, VkDescriptorType type, DescriptorSet set, uint32_t binding) {
VkDescriptorBufferInfo bufferInfo {};
bufferInfo.buffer = buffer->handle;
bufferInfo.buffer = buffer.handle;
bufferInfo.offset = 0;
bufferInfo.range = buffer->size;
bufferInfo.range = buffer.size;
VkWriteDescriptorSet descriptorWrite {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
@ -77,5 +96,5 @@ void DescriptorPool::createLayout(DescriptorSet set, const std::vector<VkDescrip
layoutCreateInfo.bindingCount = bindings.size();
layoutCreateInfo.pBindings = bindings.data();
vkCreateDescriptorSetLayout(Instance::instance->device, &layoutCreateInfo, nullptr, &setLayouts[set]);
vkCreateDescriptorSetLayout(Instance::instance->device, &layoutCreateInfo, nullptr, &layouts[set]);
}

Loading…
Cancel
Save