Compare commits

..

4 Commits

  1. 18
      imgui.ini
  2. 28
      include/application.hpp
  3. 1
      include/constraints.hpp
  4. 12
      include/fixed_list.hpp
  5. 2
      include/soft_body.hpp
  6. 30
      include/vulkan/buffer.hpp
  7. 2024
      models/icosphere.ply
  8. 2021
      models/icosphere_medium.ply
  9. BIN
      models/vulkanscene.blend
  10. 1
      shaders/compile.sh
  11. 260
      src/application.cpp
  12. 33
      src/constraints.cpp
  13. 17
      src/fixed_list.cpp
  14. 2
      src/soft_body.cpp
  15. 102
      src/vulkan/buffer.cpp

@ -29,17 +29,25 @@ Size=249,166
Collapsed=0 Collapsed=0
[Window][Performance] [Window][Performance]
Pos=1617,2 Pos=1716,2
Size=302,1002 Size=203,345
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000002,0
[Window][Performance 2] [Window][Performance 2]
Pos=1617,2 Pos=1617,2
Size=302,1002 Size=302,1002
Collapsed=0 Collapsed=0
DockId=0x00000001,1 DockId=0x00000002,1
[Window][Scene]
Pos=1716,349
Size=203,655
Collapsed=0
DockId=0x00000004,0
[Docking][Data] [Docking][Data]
DockNode ID=0x00000001 Pos=1617,2 Size=302,1002 Selected=0x60B79D0E DockNode ID=0x00000001 Pos=1716,2 Size=203,1002 Split=Y Selected=0x60B79D0E
DockNode ID=0x00000002 Parent=0x00000001 SizeRef=203,345 Selected=0x60B79D0E
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=203,655 Selected=0xE192E354

@ -16,11 +16,14 @@
#include <map> #include <map>
#include <glm/vec2.hpp> #include <glm/vec2.hpp>
#include "constraints.hpp" #include "constraints.hpp"
#include "fixed_list.hpp"
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <glm/vec3.hpp> #include <glm/vec3.hpp>
#include <list>
using std::unique_ptr, std::make_unique; using std::unique_ptr, std::make_unique;
using std::vector; using std::vector;
using std::optional;
class SoftBody; class SoftBody;
class Instance; class Instance;
@ -35,6 +38,7 @@ class Semaphore;
class Camera; class Camera;
class DescriptorPool; class DescriptorPool;
class Grabber; class Grabber;
struct SizesUniformData;
class Application { class Application {
public: public:
@ -44,10 +48,12 @@ public:
private: private:
void imGuiWindows(); void imGuiWindows();
struct PerformanceInformation { struct PerformanceInformation {
float totalUpdateDuration; static const size_t saves = 30;
FixedList recentTotalUpdateDurations = FixedList(saves);
float updateDuration; float updateDuration;
float frameDuration; FixedList recentFrameDurations = FixedList(saves);
} performanceInformation {};
} performanceInformation;
void createSyncObjects(); void createSyncObjects();
@ -71,17 +77,21 @@ private:
unique_ptr<Grabber> grabber; unique_ptr<Grabber> grabber;
unique_ptr<Buffer> grabBuffer; unique_ptr<Buffer> grabBuffer;
void createMeshBuffers(); void addSoftBody(const std::string& modelFile, size_t count=1);
void removeSoftBody(const unique_ptr<SoftBody>& softBody);
void updateConstraintBuffers(VkCommandBuffer commandBuffer);
size_t currentDrawVertexBuffer = 0; size_t currentDrawVertexBuffer = 0;
unique_ptr<Buffer> vertexBuffers[2]; optional<unique_ptr<Buffer>> vertexBuffers[2];
unique_ptr<Buffer> faceBuffer; optional<unique_ptr<Buffer>> faceBuffer;
unique_ptr<Buffer> edgeBuffer; optional<unique_ptr<Buffer>> edgeBuffer;
unique_ptr<Buffer> triangleBuffer; optional<unique_ptr<Buffer>> triangleBuffer;
unique_ptr<Buffer> tetrahedronBuffer; optional<unique_ptr<Buffer>> tetrahedronBuffer;
ConstraintData constraintData {}; ConstraintData constraintData {};
vector<unique_ptr<SoftBody>> softBodies; vector<unique_ptr<SoftBody>> softBodies;
unique_ptr<Buffer> sizeInformationBuffer; unique_ptr<Buffer> sizeInformationBuffer;
SizesUniformData *sizeInformation = nullptr;
unique_ptr<Buffer> simulationPropertiesBuffer; unique_ptr<Buffer> simulationPropertiesBuffer;

@ -51,6 +51,7 @@ struct ConstraintData {
void recordNewPartition(); void recordNewPartition();
void writePartitionInformation(); void writePartitionInformation();
void insert(const ConstraintData& other);
private: private:
uint32_t prePartitionEdgeCount; uint32_t prePartitionEdgeCount;
uint32_t prePartitionTetrahedronCount; uint32_t prePartitionTetrahedronCount;

@ -0,0 +1,12 @@
#pragma once
#include <list>
#include <cstddef>
class FixedList : private std::list<float> {
size_t maxSize;
public:
explicit FixedList(size_t maxSize);
void push(float value);
float average() const;
};

@ -28,7 +28,7 @@ public:
vector<Face> faces; vector<Face> faces;
ConstraintData constraintData; ConstraintData constraintData;
void applyVertexOffset(const glm::vec3& offset); void applyVertexWorldOffset(const glm::vec3& offset);
SoftBody& operator =(const SoftBody& other) = delete; SoftBody& operator =(const SoftBody& other) = delete;
private: private:

@ -4,27 +4,45 @@
#include <stdexcept> #include <stdexcept>
#include "vertex.hpp" #include "vertex.hpp"
#include "vk_mem_alloc.h" #include "vk_mem_alloc.h"
#include <memory>
#include <optional>
using std::unique_ptr;
using std::shared_ptr;
using std::make_shared;
using std::make_unique;
using std::optional;
class Image; class Image;
class Buffer { class Buffer {
public: public:
explicit Buffer(VkDeviceSize bufferSize, VkBufferUsageFlags bufferUsage, Buffer(VkDeviceSize bufferSize, VkBufferUsageFlags bufferUsage,
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags vmaAllocationFlags); VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags vmaAllocationFlags);
explicit Buffer(VkDeviceSize bufferSize, void* initialData, VkDeviceSize initialDataSize, VkBufferUsageFlags bufferUsage, Buffer(VkDeviceSize bufferSize, void* initialData, VkDeviceSize initialDataSize, VkBufferUsageFlags bufferUsage,
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags vmaAllocationFlags); VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags vmaAllocationFlags);
~Buffer(); static unique_ptr<Buffer> Append(const Buffer& old, void* data, VkDeviceSize size, VkCommandBuffer commandBuffer=VK_NULL_HANDLE);
static unique_ptr<Buffer> Replace(const Buffer& old, void* data, VkDeviceSize size, VkCommandBuffer commandBuffer=VK_NULL_HANDLE);
virtual ~Buffer();
VkBuffer handle = VK_NULL_HANDLE; VkBuffer handle = VK_NULL_HANDLE;
VmaAllocation allocation = VK_NULL_HANDLE; VmaAllocation allocation = VK_NULL_HANDLE;
VmaAllocationInfo allocationInfo {}; VmaAllocationInfo allocationInfo {};
VkDeviceSize size; VkDeviceSize size;
void setName(const std::string& name); void setName(const std::string& newName);
template <typename T> template <typename T>
T& access(){ T& access(){
return *reinterpret_cast<T*>(allocationInfo.pMappedData); return *reinterpret_cast<T*>(allocationInfo.pMappedData);
} }
void copyTo(Buffer* buffer) const;
void copyTo(Image* image) const;
void setData(void* data, VkDeviceSize offset, VkDeviceSize size, VkCommandBuffer commandBuffer=VK_NULL_HANDLE);
shared_ptr<Buffer> getStagingBuffer();
Buffer(const Buffer& other) = delete; Buffer(const Buffer& other) = delete;
Buffer& operator =(const Buffer& other) = delete; Buffer& operator =(const Buffer& other) = delete;
void copyTo(Buffer* buffer); private:
void copyTo(Image* image); optional<shared_ptr<Buffer>> stagingBufferOptional;
std::string name;
VkBufferUsageFlags bufferUsageFlags;
VmaMemoryUsage memoryUsage;
VmaAllocationCreateFlags allocationCreateFlags;
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -1,4 +1,3 @@
#!/usr/bin/env bash
glslc shader.vert -o vert.spv glslc shader.vert -o vert.spv
glslc shader.frag -o frag.spv glslc shader.frag -o frag.spv
glslc --target-env=vulkan1.1 pbd.comp -o pbd.spv glslc --target-env=vulkan1.1 pbd.comp -o pbd.spv

@ -20,6 +20,7 @@
#include "imgui.h" #include "imgui.h"
#include "imgui/backends/imgui_impl_vulkan.h" #include "imgui/backends/imgui_impl_vulkan.h"
#include "imgui/backends/imgui_impl_glfw.h" #include "imgui/backends/imgui_impl_glfw.h"
#include "fixed_list.hpp"
using namespace std::chrono; using namespace std::chrono;
@ -85,17 +86,15 @@ Application::Application() {
grabBuffer->setName("Grab"); grabBuffer->setName("Grab");
grabber = make_unique<Grabber>(); grabber = make_unique<Grabber>();
createMeshBuffers();
SizesUniformData sizeInformation {};
sizeInformation.vertexCount = vertexBuffers[0]->size / sizeof(Vertex);
sizeInformation.faceCount = faceBuffer->size / sizeof(Face);
sizeInformationBuffer = make_unique<Buffer>( sizeInformationBuffer = make_unique<Buffer>(
sizeof(SizesUniformData), &sizeInformation, sizeof(sizeInformation), sizeof(SizesUniformData),
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT);
sizeInformationBuffer->setName("Sizes"); sizeInformationBuffer->setName("Sizes");
sizeInformation = &sizeInformationBuffer->access<SizesUniformData>();
addSoftBody("models/bunny_medium.ply");
SimulationUniformData simulationUniformData { SimulationUniformData simulationUniformData {
.gravity = {0, -9.81, 0}, .gravity = {0, -9.81, 0},
@ -126,7 +125,7 @@ Application::Application() {
descriptorPool->bindBuffer(*vertexBuffers[1 - currentDrawVertexBuffer], VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 0); descriptorPool->bindBuffer(*vertexBuffers[1 - currentDrawVertexBuffer], VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 0);
descriptorPool->bindBuffer(*faceBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 1); descriptorPool->bindBuffer(*faceBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 1);
descriptorPool->bindBuffer(*edgeBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 2); descriptorPool->bindBuffer(*edgeBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 2);
descriptorPool->bindBuffer(*triangleBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 3); // descriptorPool->bindBuffer(*triangleBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 3);
descriptorPool->bindBuffer(*tetrahedronBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 4); descriptorPool->bindBuffer(*tetrahedronBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 4);
descriptorPool->bindBuffer(*sizeInformationBuffer, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, DescriptorSet::MESH, 5); descriptorPool->bindBuffer(*sizeInformationBuffer, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, DescriptorSet::MESH, 5);
@ -147,11 +146,12 @@ void Application::mainLoop() {
auto t2 = system_clock::now(); auto t2 = system_clock::now();
auto measuredUpdateDuration = duration<float>(t2 - t1); auto measuredUpdateDuration = duration<float>(t2 - t1);
auto requestedUpdateDuration = duration<float>(simulationPropertiesBuffer->access<SimulationUniformData>().dt); auto requestedUpdateDuration = duration<float>(
simulationPropertiesBuffer->access<SimulationUniformData>().dt);
std::this_thread::sleep_for(requestedUpdateDuration - measuredUpdateDuration); std::this_thread::sleep_for(requestedUpdateDuration - measuredUpdateDuration);
performanceInformation.updateDuration = measuredUpdateDuration.count(); performanceInformation.updateDuration = measuredUpdateDuration.count();
performanceInformation.totalUpdateDuration = std::max(requestedUpdateDuration, measuredUpdateDuration).count(); performanceInformation.recentTotalUpdateDurations.push(std::max(requestedUpdateDuration, measuredUpdateDuration).count());
} }
}); });
@ -162,7 +162,7 @@ void Application::mainLoop() {
auto t2 = system_clock::now(); auto t2 = system_clock::now();
float seconds = duration<float>(t2 - t1).count(); float seconds = duration<float>(t2 - t1).count();
performanceInformation.frameDuration = seconds; performanceInformation.recentFrameDurations.push(seconds);
t1 = system_clock::now(); t1 = system_clock::now();
drawFrame(seconds); drawFrame(seconds);
} }
@ -178,132 +178,105 @@ void Application::createSyncObjects() {
computeSemaphore = make_unique<Semaphore>(); computeSemaphore = make_unique<Semaphore>();
transferSemaphore = make_unique<Semaphore>(); transferSemaphore = make_unique<Semaphore>();
renderFence = make_unique<Fence>(true); renderFence = make_unique<Fence>(true);
computeFence = make_unique<Fence>(true); computeFence = make_unique<Fence>(false);
transferFence = make_unique<Fence>(true); transferFence = make_unique<Fence>(false);
} }
void Application::createMeshBuffers() { void Application::addSoftBody(const std::string &modelFile, size_t count) {
Mesh sphere("models/icosphere.ply"); Mesh mesh(modelFile);
Mesh bunny("models/bunny_medium.ply");
auto body = std::make_unique<SoftBody>(&sphere, 1.f / 60);
for (size_t i = 0; i < 2; i++){
auto copy = std::make_unique<SoftBody>(*body.get());
copy->applyVertexOffset({i * 2, 0, 0});
softBodies.push_back(std::move(copy));
}
body = std::make_unique<SoftBody>(&bunny, 1.f / 10);
for (size_t i = 0; i < 2; i++){
auto copy = std::make_unique<SoftBody>(*body.get());
copy->applyVertexOffset({i * 2, 0, 2});
softBodies.push_back(std::move(copy));
}
vector<Vertex> vertices;
vector<Face> faces;
for (std::unique_ptr<SoftBody> &currentSoftBody : softBodies){
currentSoftBody->firstIndex = faces.size() * 3;
currentSoftBody->vertexOffset = static_cast<int32_t>(vertices.size());
int32_t vertexOffset = currentSoftBody->vertexOffset;
for (auto &face : currentSoftBody->faces){
face.a += vertexOffset;
face.b += vertexOffset;
face.c += vertexOffset;
}
for (auto &edge : currentSoftBody->constraintData.edges){
edge.a += vertexOffset;
edge.b += vertexOffset;
}
for (auto &triangle : currentSoftBody->constraintData.triangles){
triangle.a += vertexOffset;
triangle.b += vertexOffset;
triangle.c += vertexOffset;
}
for (auto &tetrahedron : currentSoftBody->constraintData.tetrahedra){
tetrahedron.a += vertexOffset;
tetrahedron.b += vertexOffset;
tetrahedron.c += vertexOffset;
tetrahedron.d += vertexOffset;
}
vertices.insert(vertices.end(), currentSoftBody->vertices.begin(), currentSoftBody->vertices.end());
faces.insert(faces.end(), currentSoftBody->faces.begin(), currentSoftBody->faces.end());
constraintData.partitionCount = std::max(constraintData.partitionCount, currentSoftBody->constraintData.partitionCount);
auto combine = [&currentSoftBody, this] ( // Do SoftBody calculations once in constructor, will be copied from now on
auto &globalIndices, auto &bodyIndices, auto original = std::make_unique<SoftBody>(&mesh, 1.f / 60);
vector<ConstraintData::Partition> &globalPartitions, vector<ConstraintData::Partition> &bodyPartitions){
if (globalPartitions.size() < currentSoftBody->constraintData.partitionCount)
globalPartitions.resize(currentSoftBody->constraintData.partitionCount);
uint32_t offsetAdded = 0; vector<Vertex> newVertices;
vector<Face> newFaces;
for (uint32_t partition = 0; partition < constraintData.partitionCount; partition++){ for (size_t i = 0; i < count; i++){
ConstraintData::Partition &globalPartition = globalPartitions[partition]; // Local copy
globalPartition.offset += offsetAdded; auto softBody = std::make_unique<SoftBody>(*original.get());
if (partition < bodyPartitions.size()){ softBody->applyVertexWorldOffset({i * 2, 0, 0});
const ConstraintData::Partition &bodyPartition = bodyPartitions[partition];
auto dst = globalIndices.begin() + globalPartition.offset; // Position in face buffer
auto srcStart = bodyIndices.begin() + bodyPartition.offset; softBody->firstIndex = (sizeInformation->faceCount + newFaces.size()) * 3;
uint32_t count = bodyPartition.size;
globalIndices.insert(dst, srcStart, srcStart + count);
globalPartition.size += count; // Position in vertex buffer
softBody->vertexOffset = static_cast<int32_t>(sizeInformation->vertexCount + newVertices.size());
offsetAdded += count; // Vertex offset added manually for easy access in Compute Shaders
} {
auto vertexOffset = softBody->vertexOffset;
for (auto &face : softBody->faces){
face.a += vertexOffset;
face.b += vertexOffset;
face.c += vertexOffset;
} }
}; for (auto &edge : softBody->constraintData.edges){
edge.a += vertexOffset;
edge.b += vertexOffset;
}
for (auto &triangle : softBody->constraintData.triangles){
triangle.a += vertexOffset;
triangle.b += vertexOffset;
triangle.c += vertexOffset;
}
for (auto &tetrahedron : softBody->constraintData.tetrahedra){
tetrahedron.a += vertexOffset;
tetrahedron.b += vertexOffset;
tetrahedron.c += vertexOffset;
tetrahedron.d += vertexOffset;
}
}
combine(constraintData.edges, currentSoftBody->constraintData.edges, // Append data to vertices and faces
constraintData.edgePartitions, currentSoftBody->constraintData.edgePartitions); newVertices.insert(newVertices.end(), softBody->vertices.begin(), softBody->vertices.end());
newFaces.insert(newFaces.end(), softBody->faces.begin(), softBody->faces.end());
combine(constraintData.tetrahedra, currentSoftBody->constraintData.tetrahedra, // Insert data at the right places in this->constraintData
constraintData.tetrahedronPartitions, currentSoftBody->constraintData.tetrahedronPartitions); constraintData.insert(softBody->constraintData);
constraintData.triangles.insert(constraintData.triangles.end(), currentSoftBody->constraintData.triangles.begin(), currentSoftBody->constraintData.triangles.end()); softBodies.push_back(std::move(softBody));
constraintData.tetrahedra.insert(constraintData.tetrahedra.end(), currentSoftBody->constraintData.tetrahedra.begin(), currentSoftBody->constraintData.tetrahedra.end());
} }
printf("Vertices: %zu\nFaces: %zu\nEdges: %zu\nTriangles: %zu\nTetrahedra: %zu\nTotal Constraints: %zu\n", sizeInformation->vertexCount += newVertices.size();
vertices.size(), faces.size(), constraintData.edges.size(), constraintData.triangles.size(), constraintData.tetrahedra.size(), sizeInformation->faceCount += newFaces.size();
constraintData.edges.size() + constraintData.tetrahedra.size());
printf("Partitions: %u\n", constraintData.partitionCount); auto commandBuffer = Instance::instance->renderingCommandPool->beginSingleTimeCommandBuffer();
vertexBuffers[0] = Buffer::Append(*vertexBuffers[0], newVertices.data(), newVertices.size() * sizeof(Vertex), commandBuffer);
vertexBuffers[1] = Buffer::Append(*vertexBuffers[1], newVertices.data(), newVertices.size() * sizeof(Vertex), commandBuffer);
faceBuffer = Buffer::Append(*faceBuffer, newFaces.data(), newFaces.size() * sizeof(Face), commandBuffer);
edgeBuffer = Buffer::Replace(*edgeBuffer, constraintData.edges.data(), constraintData.edges.size() * sizeof(Edge), commandBuffer);
// triangleBuffer = Buffer::Replace(*triangleBuffer, constraintData.triangles.data(), constraintData.triangles.size() * sizeof(Triangle), commandBuffer);
tetrahedronBuffer = Buffer::Replace(*tetrahedronBuffer, constraintData.tetrahedra.data(), constraintData.tetrahedra.size() * sizeof(Tetrahedron), commandBuffer);
class SimulationBuffer : public Buffer { vertexBuffers[0] = make_unique<SimulationBuffer>(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
public: vertexBuffers[1] = make_unique<SimulationBuffer>(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
SimulationBuffer(void* data, VkDeviceSize size, VkBufferUsageFlags additionalUsageFlags=0) faceBuffer = make_unique<SimulationBuffer>(VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
: Buffer(size, data, size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | additionalUsageFlags, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0) {} edgeBuffer = make_unique<SimulationBuffer>();
}; // triangleBuffer = make_unique<SimulationBuffer>();
tetrahedronBuffer = make_unique<SimulationBuffer>();
vertexBuffers[0] = make_unique<SimulationBuffer>(vertices.data(), vertices.size() * sizeof(Vertex),
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
vertexBuffers[1] = make_unique<SimulationBuffer>(vertices.data(), vertices.size() * sizeof(Vertex),
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
faceBuffer = make_unique<SimulationBuffer>(faces.data(), faces.size() * sizeof(Face), VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
edgeBuffer = make_unique<SimulationBuffer>(constraintData.edges.data(), constraintData.edges.size() * sizeof(Edge));
triangleBuffer = make_unique<SimulationBuffer>(constraintData.triangles.data(), constraintData.triangles.size() * sizeof(Triangle));
tetrahedronBuffer = make_unique<SimulationBuffer>(constraintData.tetrahedra.data(), constraintData.tetrahedra.size() * sizeof(Tetrahedron));
vertexBuffers[0]->setName("Vertices 0"); vertexBuffers[0]->setName("Vertices 0");
vertexBuffers[1]->setName("Vertices 1"); vertexBuffers[1]->setName("Vertices 1");
faceBuffer->setName("Faces"); faceBuffer->setName("Faces");
edgeBuffer->setName("Edges"); edgeBuffer->setName("Edges");
triangleBuffer->setName("Triangles"); // triangleBuffer->setName("Triangles");
tetrahedronBuffer->setName("Tetrahedra"); tetrahedronBuffer->setName("Tetrahedra");
VkQueue queue = Instance::instance->graphicsAndPresentQueue;
Instance::instance->renderingCommandPool->endSingleTimeCommandBuffer(commandBuffer, queue);
}
void Application::removeSoftBody(const unique_ptr<SoftBody> &softBody) {
// cpu: remove in constraintData, reduce partition sizes and offsets
// cpu: reduce firstIndex and vertexOffset in following bodies
// gpu: update constraintData
// gpu: update vertices and faces, take from remaining softbodies
// cpu: erase vector element
} }
void Application::createComputePipelines() { void Application::createComputePipelines() {
@ -387,7 +360,7 @@ void Application::drawFrame(float dt) {
VkBufferMemoryBarrier vertexBufferBarrier{}; VkBufferMemoryBarrier vertexBufferBarrier{};
vertexBufferBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; vertexBufferBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
vertexBufferBarrier.size = vertexBuffers[currentDrawVertexBuffer]->size; vertexBufferBarrier.size = vertexBuffers[currentDrawVertexBuffer]->allocationInfo.size;
vertexBufferBarrier.offset = 0; vertexBufferBarrier.offset = 0;
vertexBufferBarrier.buffer = vertexBuffers[currentDrawVertexBuffer]->handle; vertexBufferBarrier.buffer = vertexBuffers[currentDrawVertexBuffer]->handle;
vertexBufferBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; vertexBufferBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
@ -496,16 +469,6 @@ void Application::recordDrawCommands(VkCommandBuffer commandBuffer) {
} }
void Application::update() { void Application::update() {
vkWaitForFences(Instance::GetDevice(), 1, &computeFence->handle, VK_TRUE, UINT64_MAX);
vkResetFences(Instance::GetDevice(), 1, &computeFence->handle);
currentDrawVertexBuffer = 1 - currentDrawVertexBuffer;
vkWaitForFences(Instance::GetDevice(), 1, &transferFence->handle, VK_TRUE, UINT64_MAX);
vkResetFences(Instance::GetDevice(), 1, &transferFence->handle);
currentDrawVertexBuffer = 1 - currentDrawVertexBuffer;
VkCommandBufferBeginInfo beginInfo {}; VkCommandBufferBeginInfo beginInfo {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0; beginInfo.flags = 0;
@ -539,7 +502,7 @@ void Application::update() {
vkBeginCommandBuffer(cmdBuffer, &beginInfo); vkBeginCommandBuffer(cmdBuffer, &beginInfo);
VkBufferCopy copyRegion {}; VkBufferCopy copyRegion {};
copyRegion.size = vertexBuffers[1 - currentDrawVertexBuffer]->size; copyRegion.size = vertexBuffers[1 - currentDrawVertexBuffer]->allocationInfo.size;
copyRegion.srcOffset = 0; copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0; copyRegion.dstOffset = 0;
@ -561,6 +524,17 @@ void Application::update() {
vkQueueSubmit(Instance::instance->computeAndTransferQueue, 1, &submit, transferFence->handle); vkQueueSubmit(Instance::instance->computeAndTransferQueue, 1, &submit, transferFence->handle);
submitMutex.unlock(); submitMutex.unlock();
} }
vkWaitForFences(Instance::GetDevice(), 1, &computeFence->handle, VK_TRUE, UINT64_MAX);
vkResetFences(Instance::GetDevice(), 1, &computeFence->handle);
currentDrawVertexBuffer = 1 - currentDrawVertexBuffer;
vkWaitForFences(Instance::GetDevice(), 1, &transferFence->handle, VK_TRUE, UINT64_MAX);
vkResetFences(Instance::GetDevice(), 1, &transferFence->handle);
currentDrawVertexBuffer = 1 - currentDrawVertexBuffer;
// descriptorPool->bindBuffer(*vertexBuffers[1 - currentDrawVertexBuffer], VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DescriptorSet::MESH, 0);
} }
uint32_t Application::GetGroupCount(uint32_t threads, uint32_t blockSize) { uint32_t Application::GetGroupCount(uint32_t threads, uint32_t blockSize) {
@ -582,7 +556,7 @@ void Application::recordGrabCommands(VkCommandBuffer commandBuffer) {
pushConstants.screenPosition = grabber->previousCursorPosition; pushConstants.screenPosition = grabber->previousCursorPosition;
vkCmdPushConstants(commandBuffer, grabPipeline->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(GrabPushData), &pushConstants); vkCmdPushConstants(commandBuffer, grabPipeline->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(GrabPushData), &pushConstants);
uint32_t faceInvocations = GetGroupCount(faceBuffer->size / sizeof(Face), BLOCK_SIZE_GRAB); uint32_t faceInvocations = GetGroupCount(sizeInformation->faceCount, BLOCK_SIZE_GRAB);
vkCmdDispatch(commandBuffer, faceInvocations, 1, 1); vkCmdDispatch(commandBuffer, faceInvocations, 1, 1);
computePipelineBarrier(commandBuffer); computePipelineBarrier(commandBuffer);
@ -614,7 +588,7 @@ void Application::recordGrabCommands(VkCommandBuffer commandBuffer) {
} }
void Application::recordPBDCommands(VkCommandBuffer commandBuffer) { void Application::recordPBDCommands(VkCommandBuffer commandBuffer) {
uint32_t vertexGroupCount = GetGroupCount(vertexBuffers[1 - currentDrawVertexBuffer]->size / sizeof(Vertex), BLOCK_SIZE_PBD); uint32_t vertexGroupCount = GetGroupCount(sizeInformation->vertexCount, BLOCK_SIZE_PBD);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pbdPipeline->handle); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pbdPipeline->handle);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pbdPipeline->layout, 0, 1, &descriptorPool->sets[DescriptorSet::MESH], 0, nullptr); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pbdPipeline->layout, 0, 1, &descriptorPool->sets[DescriptorSet::MESH], 0, nullptr);
@ -659,8 +633,8 @@ void Application::recordPBDCommands(VkCommandBuffer commandBuffer) {
} }
void Application::recordNormalCommands(VkCommandBuffer commandBuffer) { void Application::recordNormalCommands(VkCommandBuffer commandBuffer) {
uint32_t vertexGroupCount = GetGroupCount(vertexBuffers[1 - currentDrawVertexBuffer]->size / sizeof(Vertex), BLOCK_SIZE_NORMAL); uint32_t vertexGroupCount = GetGroupCount(sizeInformation->vertexCount, BLOCK_SIZE_NORMAL);
uint32_t faceGroupCount = GetGroupCount(faceBuffer->size / sizeof(Face), BLOCK_SIZE_NORMAL); uint32_t faceGroupCount = GetGroupCount(sizeInformation->faceCount, BLOCK_SIZE_NORMAL);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, normalPipeline->handle); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, normalPipeline->handle);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, normalPipeline->layout, 0, 1, &descriptorPool->sets[DescriptorSet::MESH], 0, nullptr); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, normalPipeline->layout, 0, 1, &descriptorPool->sets[DescriptorSet::MESH], 0, nullptr);
@ -691,7 +665,7 @@ void Application::computePipelineBarrier(VkCommandBuffer commandBuffer) {
VkBufferMemoryBarrier bufferMemoryBarrier {}; VkBufferMemoryBarrier bufferMemoryBarrier {};
bufferMemoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; bufferMemoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
bufferMemoryBarrier.buffer = buffer.handle; bufferMemoryBarrier.buffer = buffer.handle;
bufferMemoryBarrier.size = buffer.size; bufferMemoryBarrier.size = buffer.allocationInfo.size;
bufferMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; bufferMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
bufferMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; bufferMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
bufferMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; bufferMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@ -710,10 +684,22 @@ void Application::imGuiWindows() {
ImGui_ImplGlfw_NewFrame(); ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame(); ImGui::NewFrame();
ImGui::Begin("Performance"); ImGui::Begin("Performance"); {
ImGui::Text("Update time: %f", performanceInformation.updateDuration); float updateMS = performanceInformation.updateDuration * 1000.f;
ImGui::Text("Updates per second: %f", 1 / performanceInformation.totalUpdateDuration); float updateHZ = 1 / performanceInformation.recentTotalUpdateDurations.average();
ImGui::Text("Frame time: %f", performanceInformation.frameDuration); ImGui::Text("Updates: %2.0fms | %.1fHz", updateMS, updateHZ);
ImGui::Text("Frames per second: %f", 1 / performanceInformation.frameDuration);
ImGui::End(); float frameMS = performanceInformation.recentFrameDurations.average() * 1000.f;
float frameHZ = 1 / performanceInformation.recentFrameDurations.average();
ImGui::Text("Frames: %.2fms | %.1fHz", frameMS, frameHZ);
} ImGui::End();
ImGui::Begin("Scene"); {
if (ImGui::Button("Add")){
addSoftBody("models/bunny_medium.ply");
}
for (const auto &softBody: softBodies){
ImGui::Text("Some softbody");
}
} ImGui::End();
} }

@ -10,6 +10,39 @@ void ConstraintData::writePartitionInformation() {
tetrahedronPartitions.emplace_back(prePartitionTetrahedronCount, tetrahedra.size() - prePartitionTetrahedronCount); tetrahedronPartitions.emplace_back(prePartitionTetrahedronCount, tetrahedra.size() - prePartitionTetrahedronCount);
} }
void ConstraintData::insert(const ConstraintData &other) {
if (other.partitionCount > partitionCount){
edgePartitions.resize(other.partitionCount);
tetrahedronPartitions.resize(other.partitionCount);
}
// insert constraints, increase partition offsets and sizes
for (size_t i = 0; i < other.partitionCount; i++){
edgePartitions[i].offset += other.edgePartitions[i].offset;
tetrahedronPartitions[i].offset += other.tetrahedronPartitions[i].offset;
auto baseEdgeInsert = other.edges.begin() + other.edgePartitions[i].offset;
edges.insert(edges.begin() + edgePartitions[i].offset + edgePartitions[i].size,
baseEdgeInsert, baseEdgeInsert + other.edgePartitions[i].size);
auto baseTetrahedronInsert = other.tetrahedra.begin() + other.tetrahedronPartitions[i].offset;
tetrahedra.insert(tetrahedra.begin() + tetrahedronPartitions[i].offset + tetrahedronPartitions[i].size,
baseTetrahedronInsert, baseTetrahedronInsert + other.tetrahedronPartitions[i].size);
edgePartitions[i].size += other.edgePartitions[i].size;
tetrahedronPartitions[i].size += other.tetrahedronPartitions[i].size;
}
// increase offsets for remaining partitions
for (size_t i = other.partitionCount; i < partitionCount; i++){
edgePartitions[i].offset += other.edgePartitions[other.partitionCount - 1].offset;
tetrahedronPartitions[i].offset += other.tetrahedronPartitions[other.partitionCount - 1].offset;
}
partitionCount = std::max(partitionCount, other.partitionCount);
}
void DistanceConstraint::writeData(ConstraintData &dataLists) const { void DistanceConstraint::writeData(ConstraintData &dataLists) const {
dataLists.edges.push_back(Edge(a, b, length, compliance)); dataLists.edges.push_back(Edge(a, b, length, compliance));
} }

@ -0,0 +1,17 @@
#include <numeric>
#include "fixed_list.hpp"
FixedList::FixedList(size_t maxSize) : std::list<float>(), maxSize(maxSize) {}
void FixedList::push(float value) {
push_back(value);
if (size() > maxSize)
erase(begin());
}
float FixedList::average() const {
if (empty())
return 0.0f;
return std::reduce(begin(), end(), 0.0f) / static_cast<float>(size());
}

@ -112,7 +112,7 @@ SoftBody::SoftBody(Mesh* mesh, float edgeCompliance, float triangleCompliance, f
splitConstraints(); splitConstraints();
} }
void SoftBody::applyVertexOffset(const glm::vec3 &offset) { void SoftBody::applyVertexWorldOffset(const glm::vec3 &offset) {
for (Vertex& vertex : vertices){ for (Vertex& vertex : vertices){
vertex.position += offset; vertex.position += offset;
} }

@ -6,23 +6,25 @@
#include "vulkan/image.hpp" #include "vulkan/image.hpp"
#include "vk_mem_alloc.h" #include "vk_mem_alloc.h"
Buffer::Buffer(VkDeviceSize bufferSize, VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags vmaAllocationFlags) : size(bufferSize) { Buffer::Buffer(VkDeviceSize bufferSize, VkBufferUsageFlags bufferUsageFlags, VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags)
: size(bufferSize), bufferUsageFlags(bufferUsageFlags), memoryUsage(memoryUsage), allocationCreateFlags(allocationFlags) {
VkBufferCreateInfo bufferCreateInfo {}; VkBufferCreateInfo bufferCreateInfo {};
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferCreateInfo.size = bufferSize; bufferCreateInfo.size = bufferSize;
bufferCreateInfo.usage = bufferUsage; bufferCreateInfo.usage = bufferUsageFlags;
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocationCreateInfo {}; VmaAllocationCreateInfo allocationCreateInfo {};
allocationCreateInfo.usage = memoryUsage; allocationCreateInfo.usage = memoryUsage;
allocationCreateInfo.flags = vmaAllocationFlags; allocationCreateInfo.flags = allocationFlags;
vmaCreateBuffer(Instance::GetAllocator(), &bufferCreateInfo, &allocationCreateInfo, &handle, &allocation, &allocationInfo); vmaCreateBuffer(Instance::GetAllocator(), &bufferCreateInfo, &allocationCreateInfo, &handle, &allocation, &allocationInfo);
} }
Buffer::Buffer(VkDeviceSize bufferSize, void *initialData, VkDeviceSize initialDataSize, VkBufferUsageFlags bufferUsage, Buffer::Buffer(VkDeviceSize bufferSize, void *initialData, VkDeviceSize initialDataSize, VkBufferUsageFlags bufferUsageFlags,
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags vmaAllocationFlags) VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags)
: Buffer(bufferSize, bufferUsage | VK_BUFFER_USAGE_TRANSFER_DST_BIT, memoryUsage, vmaAllocationFlags){ : Buffer(bufferSize, bufferUsageFlags | VK_BUFFER_USAGE_TRANSFER_DST_BIT, memoryUsage, allocationFlags){
Buffer stagingBuffer( Buffer stagingBuffer(
bufferSize, bufferSize,
@ -39,20 +41,20 @@ Buffer::~Buffer() {
vmaDestroyBuffer(Instance::GetAllocator(), handle, allocation); vmaDestroyBuffer(Instance::GetAllocator(), handle, allocation);
} }
void Buffer::copyTo(Buffer *buffer) { void Buffer::copyTo(Buffer *buffer) const {
VkQueue queue = Instance::instance->graphicsAndPresentQueue; VkQueue queue = Instance::instance->graphicsAndPresentQueue;
CommandPool* commandPool = Instance::instance->renderingCommandPool; CommandPool* commandPool = Instance::instance->renderingCommandPool;
VkCommandBuffer commandBuffer = commandPool->beginSingleTimeCommandBuffer(); VkCommandBuffer commandBuffer = commandPool->beginSingleTimeCommandBuffer();
VkBufferCopy copyRegion {}; VkBufferCopy copyRegion {};
copyRegion.size = size; copyRegion.size = allocationInfo.size;
vkCmdCopyBuffer(commandBuffer, handle, buffer->handle, 1, &copyRegion); vkCmdCopyBuffer(commandBuffer, handle, buffer->handle, 1, &copyRegion);
commandPool->endSingleTimeCommandBuffer(commandBuffer, queue); commandPool->endSingleTimeCommandBuffer(commandBuffer, queue);
} }
void Buffer::copyTo(Image *image) { void Buffer::copyTo(Image *image) const {
VkQueue queue = Instance::instance->graphicsAndPresentQueue; VkQueue queue = Instance::instance->graphicsAndPresentQueue;
CommandPool* commandPool = Instance::instance->renderingCommandPool; CommandPool* commandPool = Instance::instance->renderingCommandPool;
@ -74,6 +76,84 @@ void Buffer::copyTo(Image *image) {
commandPool->endSingleTimeCommandBuffer(commandBuffer, queue); commandPool->endSingleTimeCommandBuffer(commandBuffer, queue);
} }
void Buffer::setName(const std::string &name) { void Buffer::setData(void *data, VkDeviceSize offset, VkDeviceSize size, VkCommandBuffer commandBuffer) {
vmaSetAllocationName(Instance::GetAllocator(), allocation, name.data()); if (allocationInfo.pMappedData){
memcpy(allocationInfo.pMappedData, reinterpret_cast<u_char*>(data) + offset, size);
VkMemoryPropertyFlags flags;
vmaGetAllocationMemoryProperties(Instance::GetAllocator(), allocation, &flags);
assert(flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
} else {
auto stagingBuffer = getStagingBuffer();
stagingBuffer->setData(data, offset, size);
VkBufferMemoryBarrier bufferMemoryBarrier {};
bufferMemoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
bufferMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufferMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
bufferMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufferMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufferMemoryBarrier.buffer = stagingBuffer->handle;
bufferMemoryBarrier.size = size;
bufferMemoryBarrier.offset = offset;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0, nullptr,
1, &bufferMemoryBarrier,
0, nullptr);
VkBufferCopy region {};
region.srcOffset = offset;
region.dstOffset = offset;
region.size = size;
vkCmdCopyBuffer(commandBuffer, stagingBuffer->handle, handle, 1, &region);
}
}
void Buffer::setName(const std::string &newName) {
name = newName;
vmaSetAllocationName(Instance::GetAllocator(), allocation, newName.data());
}
unique_ptr<Buffer> Buffer::Append(const Buffer &old, void *data, VkDeviceSize size, VkCommandBuffer commandBuffer) {
auto buffer = make_unique<Buffer>(old.size + size,
old.bufferUsageFlags | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
old.memoryUsage,
old.allocationCreateFlags);
VkBufferCopy copyRegion {};
copyRegion.size = old.size;
vkCmdCopyBuffer(commandBuffer, old.handle, buffer->handle, 1, &copyRegion);
buffer->setData(data, old.size, size, commandBuffer);
return buffer;
}
unique_ptr<Buffer> Buffer::Replace(const Buffer &old, void *data, VkDeviceSize size, VkCommandBuffer commandBuffer) {
auto buffer = make_unique<Buffer>(size,
old.bufferUsageFlags | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
old.memoryUsage,
old.allocationCreateFlags);
buffer->setData(data, 0, size, commandBuffer);
return buffer;
}
shared_ptr<Buffer> Buffer::getStagingBuffer() {
if (stagingBufferOptional.has_value())
return stagingBufferOptional.value();
stagingBufferOptional = make_shared<Buffer>(
allocationInfo.size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VMA_MEMORY_USAGE_AUTO,
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT);
return stagingBufferOptional.value();
} }

Loading…
Cancel
Save