normal compute

feature/softbody-runtime-control
Benjamin Kraft 4 months ago
parent 95527a5a55
commit c84fe3e3b9
  1. 2
      include/application.hpp
  2. 2
      include/camera.hpp
  3. 3
      include/simulation.hpp
  4. 4
      include/soft_body.hpp
  5. 4
      include/vulkan/pipeline.hpp
  6. 5
      include/vulkan/vertex.hpp
  7. 3
      shaders/compile.sh
  8. 74
      shaders/normal.comp
  9. 1
      shaders/pbd.comp
  10. 3
      shaders/shader.frag
  11. 3
      shaders/shader.vert
  12. 2
      src/camera.cpp
  13. 46
      src/simulation.cpp
  14. 14
      src/soft_body.cpp
  15. 3
      src/vulkan/application.cpp
  16. 13
      src/vulkan/instance.cpp
  17. 33
      src/vulkan/pipeline.cpp
  18. 11
      src/vulkan/vertex.cpp

@ -37,8 +37,6 @@ protected:
Pipeline* graphicsPipeline = nullptr;
Buffer* uniformBuffer = nullptr;
ComputePipeline* computePipeline = nullptr;
void updateUniformBuffer();
void recordGraphicsCommandBuffer(uint32_t imageIndex);

@ -7,7 +7,7 @@
class Camera : private MouseListener {
private:
glm::vec3 position {0, 0, 0};
glm::vec3 position {0, 0, 5};
float phi = 0;
float theta = 0;

@ -16,6 +16,9 @@ private:
Buffer* faceBuffer;
Buffer* tetrahedronBuffer;
ComputePipeline* pbdPipeline = nullptr;
ComputePipeline* normalPipeline = nullptr;
std::vector<std::unique_ptr<SoftBody>> softBodies;
void recordDrawCommands() override;
void recordComputeCommands(VkCommandBuffer cmdBuffer) override;

@ -17,7 +17,9 @@ class Mesh;
class SoftBody {
public:
explicit SoftBody(Mesh* mesh, float compliance);
uint32_t indexCount = 0;
uint32_t firstIndex = 0;
int32_t vertexOffset = 0;
uint32_t partitionCount = 0;
float compliance;

@ -31,13 +31,13 @@ protected:
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
void createDescriptorSetLayout();
void createDescriptorPool(VkDescriptorType type);
void createDescriptorPool(VkDescriptorType type, uint32_t descriptorCount);
void allocateDescriptorSet();
};
class ComputePipeline : public Pipeline {
public:
explicit ComputePipeline();
explicit ComputePipeline(const std::string& shaderFile, uint32_t bindings);
void updateDescriptor(uint32_t binding, Buffer* buffer);
};

@ -5,10 +5,11 @@
#include <array>
struct Vertex {
alignas(16) glm::vec3 pos;
alignas(16) glm::vec3 position;
alignas(16) glm::vec3 color;
alignas(16) glm::vec3 normal;
static VkVertexInputBindingDescription getBindingDescription();
static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions();
static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions();
};

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

@ -0,0 +1,74 @@
#version 450
#extension GL_EXT_shader_atomic_float : enable
layout (local_size_x = 1) in;
struct Vertex {
vec3 position;
vec3 color;
uvec3 normal;
};
struct Face {
uint a;
uint b;
uint c;
};
layout (std430, set = 0, binding = 0) buffer VertexBuffer {
Vertex vertices[];
};
layout (std430, set = 0, binding = 1) buffer FaceBuffer {
Face faces[];
};
layout (push_constant) uniform StateConstant {
uint state;
};
void atomicAddVec3(uint vID, vec3 add){
for (int i = 0; i < 3; i++) {
uint expected_memory = vertices[vID].normal[i];
float floatInput = uintBitsToFloat(vertices[vID].normal[i]) + add[i];
uint actual_content = atomicCompSwap(vertices[vID].normal[i], expected_memory, floatBitsToUint(floatInput));
while (actual_content != expected_memory) {
expected_memory = actual_content;
floatInput = uintBitsToFloat(expected_memory) + add[i];
actual_content = atomicCompSwap(vertices[vID].normal[i], expected_memory, floatBitsToUint(floatInput));
}
}
}
void reset(){
uint vID = gl_GlobalInvocationID.x;
vertices[vID].normal = floatBitsToUint(vec3(0, 0, 0));
}
void accumulate(){
uint fID = gl_GlobalInvocationID.x;
Face f = faces[fID];
Vertex v1 = vertices[f.a];
Vertex v2 = vertices[f.b];
Vertex v3 = vertices[f.c];
vec3 weightedNormal = cross(v3.position - v1.position, v2.position - v1.position);
atomicAddVec3(f.a, weightedNormal);
atomicAddVec3(f.b, weightedNormal);
atomicAddVec3(f.c, weightedNormal);
}
void norm(){
uint vID = gl_GlobalInvocationID.x;
vertices[vID].normal = floatBitsToUint(normalize(uintBitsToFloat(vertices[vID].normal)));
}
void main() {
switch (state){
case 0: reset(); break;
case 1: accumulate(); break;
case 2: norm(); break;
}
}

@ -5,6 +5,7 @@ layout (local_size_x = 32) in;
struct Vertex {
vec3 position;
vec3 color;
vec3 normal;
};
layout (std140, set = 0, binding = 0) buffer VertexBuffer {

@ -2,7 +2,8 @@
layout (location = 0) out vec4 outColor;
layout (location = 0) in vec3 fragColor;
layout (location = 1) in vec3 normal;
void main() {
outColor = vec4(fragColor, 1.0);
outColor = vec4((normal + vec3(1, 1, 1)) / 2, 1.0);
}

@ -2,8 +2,10 @@
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inColor;
layout (location = 2) in vec3 inNormal;
layout (location = 0) out vec3 fragColor;
layout (location = 1) out vec3 normal;
layout (binding = 0) uniform UniformBufferObject {
mat4 model;
@ -14,4 +16,5 @@ layout (binding = 0) uniform UniformBufferObject {
void main() {
gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition, 1.0);
fragColor = inColor;
normal = inNormal;
}

@ -55,6 +55,6 @@ void Camera::mouseMoved(float deltaX, float deltaY) {
phi += deltaX / div;
theta += -deltaY / div;
float margin = 0.1;
float margin = 0.01;
theta = glm::clamp(theta, -glm::half_pi<float>() + margin, glm::half_pi<float>() - margin);
}

@ -8,9 +8,11 @@
#include "constraints.hpp"
Simulation::Simulation() {
Mesh mesh("models/sphere_high.ply");
Mesh sphere("models/sphere_high.ply");
Mesh bunny("models/bunny_high.ply");
softBodies.push_back(std::make_unique<SoftBody>(&mesh, 0.3f));
softBodies.push_back(std::make_unique<SoftBody>(&sphere, 0.3f));
// softBodies.push_back(std::make_unique<SoftBody>(&bunny, 0.3f));
vector<Vertex> vertices;
vector<Edge> edges;
@ -19,6 +21,9 @@ Simulation::Simulation() {
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());
@ -38,7 +43,12 @@ Simulation::Simulation() {
faceBuffer = new SimulationBuffer(faces.data(), faces.size() * sizeof(Face), VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
tetrahedronBuffer = new SimulationBuffer(tetrahedra.data(), tetrahedra.size() * sizeof(Tetrahedron));
computePipeline->updateDescriptor(0, vertexBuffer);
pbdPipeline = new ComputePipeline("shaders/pbd.spv", 1);
pbdPipeline->updateDescriptor(0, vertexBuffer);
normalPipeline = new ComputePipeline("shaders/normal.spv", 2);
normalPipeline->updateDescriptor(0, vertexBuffer);
normalPipeline->updateDescriptor(1, faceBuffer);
}
void Simulation::recordDrawCommands() {
@ -50,10 +60,14 @@ void Simulation::recordDrawCommands() {
vkCmdBindIndexBuffer(cmdBuffer, faceBuffer->handle, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline->layout, 0, 1, &graphicsPipeline->descriptorSet, 0, nullptr);
vkCmdDrawIndexed(cmdBuffer, faceBuffer->size / sizeof(Face) * 3, 1, 0, 0, 0);
for (const auto& softBody : softBodies){
vkCmdDrawIndexed(cmdBuffer, softBody->faces.size() * 3, 1, softBody->firstIndex, softBody->vertexOffset, 0);
}
}
void Simulation::recordComputeCommands(VkCommandBuffer cmdBuffer) {
vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pbdPipeline->handle);
SoftBody& body = *softBodies[0];
VkMemoryBarrier barrier {};
@ -61,7 +75,7 @@ void Simulation::recordComputeCommands(VkCommandBuffer cmdBuffer) {
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline->layout, 0, 1, &computePipeline->descriptorSet, 0, nullptr);
vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pbdPipeline->layout, 0, 1, &pbdPipeline->descriptorSet, 0, nullptr);
size_t subSteps = 1;
@ -80,6 +94,26 @@ void Simulation::recordComputeCommands(VkCommandBuffer cmdBuffer) {
vkCmdDispatch(cmdBuffer, postSolveInvocations, 1, 1);
}
vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, normalPipeline->handle);
vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, normalPipeline->layout, 0, 1, &normalPipeline->descriptorSet, 0, nullptr);
uint32_t vertexGroupCount = vertexBuffer->size / sizeof(Vertex);
uint32_t faceGroupCount = faceBuffer->size / sizeof(Face);
uint32_t state = 0;
vkCmdPushConstants(cmdBuffer, normalPipeline->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(uint32_t), &state);
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);
state = 1;
vkCmdPushConstants(cmdBuffer, normalPipeline->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(uint32_t), &state);
vkCmdDispatch(cmdBuffer, faceGroupCount, 1, 1);
vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr);
state = 2;
vkCmdPushConstants(cmdBuffer, normalPipeline->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(uint32_t), &state);
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);
}
Simulation::~Simulation() {
@ -88,4 +122,6 @@ Simulation::~Simulation() {
delete triangleBuffer;
delete faceBuffer;
delete tetrahedronBuffer;
delete pbdPipeline;
delete normalPipeline;
}

@ -21,12 +21,12 @@ SoftBody::SoftBody(Mesh* mesh, float compliance) : compliance(compliance) {
in.pointattributelist = new REAL[mesh->vertices.size() * 3];
for (size_t i = 0; i < mesh->vertices.size(); i++){
in.pointlist[i * 3 + 0] = mesh->vertices[i].pos.x;
in.pointlist[i * 3 + 1] = mesh->vertices[i].pos.y;
in.pointlist[i * 3 + 2] = mesh->vertices[i].pos.z;
in.pointattributelist[i * 3 + 0] = (mesh->vertices[i].pos.x + 1) / 2;
in.pointattributelist[i * 3 + 1] = (mesh->vertices[i].pos.y + 1) / 2;
in.pointattributelist[i * 3 + 2] = (mesh->vertices[i].pos.z + 1) / 2;
in.pointlist[i * 3 + 0] = mesh->vertices[i].position.x;
in.pointlist[i * 3 + 1] = mesh->vertices[i].position.y;
in.pointlist[i * 3 + 2] = mesh->vertices[i].position.z;
in.pointattributelist[i * 3 + 0] = (mesh->vertices[i].position.x + 1) / 2;
in.pointattributelist[i * 3 + 1] = (mesh->vertices[i].position.y + 1) / 2;
in.pointattributelist[i * 3 + 2] = (mesh->vertices[i].position.z + 1) / 2;
}
for (size_t i = 0; i < mesh->faces.size(); i++){
@ -65,7 +65,7 @@ SoftBody::SoftBody(Mesh* mesh, float compliance) : compliance(compliance) {
uint32_t a = out.trifacelist[i * 3 + 0];
uint32_t b = out.trifacelist[i * 3 + 1];
uint32_t c = out.trifacelist[i * 3 + 2];
if (out.trifacemarkerlist[i] != 1)
if (out.trifacemarkerlist[i] != 0)
faces.emplace_back(Face(a, b, c));
}
faces.shrink_to_fit();

@ -25,7 +25,6 @@ Application::Application() {
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT);
graphicsPipeline->updateDescriptor(0, uniformBuffer, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
computePipeline = new ComputePipeline();
camera = new Camera(swapchain->extent);
char* stats;
@ -175,7 +174,6 @@ void Application::update() {
beginInfo.flags = 0;
vkBeginCommandBuffer(cmdBuffer, &beginInfo);
vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline->handle);
recordComputeCommands(cmdBuffer);
vkEndCommandBuffer(cmdBuffer);
@ -198,7 +196,6 @@ Application::~Application() {
delete computeInFlight;
delete uniformBuffer;
delete graphicsPipeline;
delete computePipeline;
delete camera;
delete Instance::instance;
}

@ -7,7 +7,8 @@
#include <cstring>
const std::vector<const char*> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME,
};
#ifndef NDEBUG
@ -82,7 +83,7 @@ void Instance::createInstance() {
applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
applicationInfo.pEngineName = "No Engine";
applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
applicationInfo.apiVersion = VK_API_VERSION_1_2;
applicationInfo.apiVersion = VK_API_VERSION_1_3;
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
@ -146,13 +147,17 @@ void Instance::createLogicalDevice() {
queueCreateInfos.push_back(queueCreateInfo);
}
VkPhysicalDeviceFeatures deviceFeatures {};
VkPhysicalDeviceFeatures deviceFeaturesCore {};
VkPhysicalDeviceShaderAtomicFloatFeaturesEXT atomicFeatures {};
atomicFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT;
atomicFeatures.shaderBufferFloat32Atomics = VK_TRUE;
VkDeviceCreateInfo createInfo {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.queueCreateInfoCount = queueCreateInfos.size();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.pEnabledFeatures = &deviceFeaturesCore;
createInfo.pNext = &atomicFeatures;
createInfo.enabledExtensionCount = deviceExtensions.size();
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
#ifdef ENABLE_VALIDATION_LAYERS

@ -28,7 +28,7 @@ Pipeline::Pipeline() {
Pipeline::Pipeline(VkRenderPass renderPass) {
createDescriptorSetLayout();
createDescriptorPool(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
createDescriptorPool(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1);
allocateDescriptorSet();
auto vertShaderCode = readFile("shaders/vert.spv");
@ -89,7 +89,7 @@ Pipeline::Pipeline(VkRenderPass renderPass) {
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasClamp = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisample {};
@ -183,9 +183,9 @@ void Pipeline::createDescriptorSetLayout() {
vkCreateDescriptorSetLayout(Instance::instance->device, &layoutCreateInfo, nullptr, &descriptorSetLayout);
}
void Pipeline::createDescriptorPool(VkDescriptorType type) {
void Pipeline::createDescriptorPool(VkDescriptorType type, uint32_t descriptorCount) {
VkDescriptorPoolSize poolSize {};
poolSize.descriptorCount = 1;
poolSize.descriptorCount = descriptorCount;
poolSize.type = type;
VkDescriptorPoolCreateInfo poolInfo {};
@ -225,27 +225,29 @@ void Pipeline::updateDescriptor(uint32_t binding, Buffer *buffer, VkDescriptorTy
vkUpdateDescriptorSets(Instance::instance->device, 1, &descriptorWrite, 0, nullptr);
}
ComputePipeline::ComputePipeline() {
ComputePipeline::ComputePipeline(const std::string& shaderFile, uint32_t bindings) {
{
// Vertex buffer
VkDescriptorSetLayoutBinding layoutBinding {};
layoutBinding.binding = 0;
std::vector<VkDescriptorSetLayoutBinding> layoutBindings(bindings, VkDescriptorSetLayoutBinding());
for (uint32_t binding = 0; binding < bindings; binding++){
VkDescriptorSetLayoutBinding& layoutBinding = layoutBindings[binding];
layoutBinding.binding = binding;
layoutBinding.descriptorCount = 1;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
layoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
}
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo {};
descriptorSetLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutInfo.bindingCount = 1;
descriptorSetLayoutInfo.pBindings = &layoutBinding;
descriptorSetLayoutInfo.bindingCount = bindings;
descriptorSetLayoutInfo.pBindings = layoutBindings.data();
vkCreateDescriptorSetLayout(Instance::instance->device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout);
createDescriptorPool(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
createDescriptorPool(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, bindings);
allocateDescriptorSet();
}
VkShaderModule module = createShaderModule(readFile("shaders/comp.spv"));
VkShaderModule module = createShaderModule(readFile(shaderFile));
VkPipelineShaderStageCreateInfo stageInfo {};
stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@ -253,10 +255,17 @@ ComputePipeline::ComputePipeline() {
stageInfo.pName = "main";
stageInfo.module = module;
VkPushConstantRange pushConstantRange {};
pushConstantRange.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
pushConstantRange.offset = 0;
pushConstantRange.size = sizeof(uint32_t);
VkPipelineLayoutCreateInfo layoutInfo {};
layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layoutInfo.setLayoutCount = 1;
layoutInfo.pSetLayouts = &descriptorSetLayout;
layoutInfo.pushConstantRangeCount = 1;
layoutInfo.pPushConstantRanges = &pushConstantRange;
vkCreatePipelineLayout(Instance::instance->device, &layoutInfo, nullptr, &layout);

@ -8,18 +8,23 @@ VkVertexInputBindingDescription Vertex::getBindingDescription() {
return bindingDescription;
}
std::array<VkVertexInputAttributeDescription, 2> Vertex::getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions {};
std::array<VkVertexInputAttributeDescription, 3> Vertex::getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions {};
attributeDescriptions[0].binding = 0;
attributeDescriptions[0].location = 0;
attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[0].offset = offsetof(Vertex, pos);
attributeDescriptions[0].offset = offsetof(Vertex, position);
attributeDescriptions[1].binding = 0;
attributeDescriptions[1].location = 1;
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[1].offset = offsetof(Vertex, color);
attributeDescriptions[2].binding = 0;
attributeDescriptions[2].location = 2;
attributeDescriptions[2].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[2].offset = offsetof(Vertex, normal);
return attributeDescriptions;
}

Loading…
Cancel
Save