From eff719099ac8450ed3af6a0d5f99200b2aa9948e Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 6 Jan 2024 16:47:11 +0100 Subject: [PATCH] Complete rework of the sample Simplified, added comments and only use single buffers to show best practices --- examples/gears/gears.cpp | 646 +++++++++++++++++++----------- examples/gears/vulkangear.cpp | 288 ------------- examples/gears/vulkangear.h | 102 ----- shaders/glsl/gears/gears.vert | 11 +- shaders/glsl/gears/gears.vert.spv | Bin 2820 -> 2924 bytes shaders/hlsl/gears/gears.vert | 14 +- shaders/hlsl/gears/gears.vert.spv | Bin 3576 -> 2312 bytes 7 files changed, 435 insertions(+), 626 deletions(-) delete mode 100644 examples/gears/vulkangear.cpp delete mode 100644 examples/gears/vulkangear.h diff --git a/examples/gears/gears.cpp b/examples/gears/gears.cpp index 277bbf5c..f1cd272e 100644 --- a/examples/gears/gears.cpp +++ b/examples/gears/gears.cpp @@ -1,55 +1,416 @@ /* -* Vulkan Example - Animated gears using multiple uniform buffers -* +* Vulkan Example - Drawing multiple animated gears (emulating the look of glxgears) +* +* All gears are using single index, vertex and uniform buffers to show the Vulkan best practices of keeping the no. of buffer/memory allocations to a mimimum +* We use index offsets and instance indices to offset into the buffers at draw time for each gear +* * Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ -#include "vulkangear.h" #include "vulkanexamplebase.h" +const uint32_t numGears = 3; +// Used for passing the definition of a gear during construction +struct GearDefinition { + float innerRadius; + float outerRadius; + float width; + int numTeeth; + float toothDepth; + glm::vec3 color; + glm::vec3 pos; + float rotSpeed; + float rotOffset; +}; + +/* + * Gear + * This class contains the properties of a single gear and a function to generate vertices and indices + */ +class Gear +{ +public: + // Definition for the vertex data used to render the gears + struct Vertex { + glm::vec3 position; + glm::vec3 normal; + glm::vec3 color; + }; + + glm::vec3 color; + glm::vec3 pos; + float rotSpeed{ 0.0f }; + float rotOffset{ 0.0f }; + // These are used at draw time to offset into the single buffers + uint32_t indexCount{ 0 }; + uint32_t indexStart{ 0 }; + + // Generates the indices and vertices for this gear + // They are added to the vertex and index buffers passed into the function + // This way we can put all gears into single vertex and index buffers instead of having to allocate single buffers for each gear (which would be bad practice) + void generate(GearDefinition& gearDefinition, std::vector& vertexBuffer, std::vector& indexBuffer) { + this->color = gearDefinition.color; + this->pos = gearDefinition.pos; + this->rotOffset = gearDefinition.rotOffset; + this->rotSpeed = gearDefinition.rotSpeed; + + int i; + float r0, r1, r2; + float ta, da; + float u1, v1, u2, v2, len; + float cos_ta, cos_ta_1da, cos_ta_2da, cos_ta_3da, cos_ta_4da; + float sin_ta, sin_ta_1da, sin_ta_2da, sin_ta_3da, sin_ta_4da; + int32_t ix0, ix1, ix2, ix3, ix4, ix5; + + // We need to know where this triangle's indices start within the single index buffer + indexStart = static_cast(indexBuffer.size()); + + r0 = gearDefinition.innerRadius; + r1 = gearDefinition.outerRadius - gearDefinition.toothDepth / 2.0f; + r2 = gearDefinition.outerRadius + gearDefinition.toothDepth / 2.0f; + da = static_cast (2.0 * M_PI / gearDefinition.numTeeth / 4.0); + + glm::vec3 normal; + + // Use lambda functions to simplify vertex and face creation + auto addFace = [&indexBuffer](int a, int b, int c) { + indexBuffer.push_back(a); + indexBuffer.push_back(b); + indexBuffer.push_back(c); + }; + + auto addVertex = [this, &vertexBuffer](float x, float y, float z, glm::vec3 normal) { + Vertex v{}; + v.position = { x, y, z }; + v.normal = normal; + v.color = this->color; + vertexBuffer.push_back(v); + return static_cast(vertexBuffer.size()) - 1; + }; + + for (i = 0; i < gearDefinition.numTeeth; i++) { + ta = i * static_cast (2.0 * M_PI / gearDefinition.numTeeth); + + cos_ta = cos(ta); + cos_ta_1da = cos(ta + da); + cos_ta_2da = cos(ta + 2.0f * da); + cos_ta_3da = cos(ta + 3.0f * da); + cos_ta_4da = cos(ta + 4.0f * da); + sin_ta = sin(ta); + sin_ta_1da = sin(ta + da); + sin_ta_2da = sin(ta + 2.0f * da); + sin_ta_3da = sin(ta + 3.0f * da); + sin_ta_4da = sin(ta + 4.0f * da); + + u1 = r2 * cos_ta_1da - r1 * cos_ta; + v1 = r2 * sin_ta_1da - r1 * sin_ta; + len = sqrt(u1 * u1 + v1 * v1); + u1 /= len; + v1 /= len; + u2 = r1 * cos_ta_3da - r2 * cos_ta_2da; + v2 = r1 * sin_ta_3da - r2 * sin_ta_2da; + + // Front face + normal = glm::vec3(0.0f, 0.0f, 1.0f); + ix0 = addVertex(r0 * cos_ta, r0 * sin_ta, gearDefinition.width * 0.5f, normal); + ix1 = addVertex(r1 * cos_ta, r1 * sin_ta, gearDefinition.width * 0.5f, normal); + ix2 = addVertex(r0 * cos_ta, r0 * sin_ta, gearDefinition.width * 0.5f, normal); + ix3 = addVertex(r1 * cos_ta_3da, r1 * sin_ta_3da, gearDefinition.width * 0.5f, normal); + ix4 = addVertex(r0 * cos_ta_4da, r0 * sin_ta_4da, gearDefinition.width * 0.5f, normal); + ix5 = addVertex(r1 * cos_ta_4da, r1 * sin_ta_4da, gearDefinition.width * 0.5f, normal); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + addFace(ix2, ix3, ix4); + addFace(ix3, ix5, ix4); + + // Teeth front face + normal = glm::vec3(0.0f, 0.0f, 1.0f); + ix0 = addVertex(r1 * cos_ta, r1 * sin_ta, gearDefinition.width * 0.5f, normal); + ix1 = addVertex(r2 * cos_ta_1da, r2 * sin_ta_1da, gearDefinition.width * 0.5f, normal); + ix2 = addVertex(r1 * cos_ta_3da, r1 * sin_ta_3da, gearDefinition.width * 0.5f, normal); + ix3 = addVertex(r2 * cos_ta_2da, r2 * sin_ta_2da, gearDefinition.width * 0.5f, normal); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + + // Back face + normal = glm::vec3(0.0f, 0.0f, -1.0f); + ix0 = addVertex(r1 * cos_ta, r1 * sin_ta, -gearDefinition.width * 0.5f, normal); + ix1 = addVertex(r0 * cos_ta, r0 * sin_ta, -gearDefinition.width * 0.5f, normal); + ix2 = addVertex(r1 * cos_ta_3da, r1 * sin_ta_3da, -gearDefinition.width * 0.5f, normal); + ix3 = addVertex(r0 * cos_ta, r0 * sin_ta, -gearDefinition.width * 0.5f, normal); + ix4 = addVertex(r1 * cos_ta_4da, r1 * sin_ta_4da, -gearDefinition.width * 0.5f, normal); + ix5 = addVertex(r0 * cos_ta_4da, r0 * sin_ta_4da, -gearDefinition.width * 0.5f, normal); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + addFace(ix2, ix3, ix4); + addFace(ix3, ix5, ix4); + + // Teeth back face + normal = glm::vec3(0.0f, 0.0f, -1.0f); + ix0 = addVertex(r1 * cos_ta_3da, r1 * sin_ta_3da, -gearDefinition.width * 0.5f, normal); + ix1 = addVertex(r2 * cos_ta_2da, r2 * sin_ta_2da, -gearDefinition.width * 0.5f, normal); + ix2 = addVertex(r1 * cos_ta, r1 * sin_ta, -gearDefinition.width * 0.5f, normal); + ix3 = addVertex(r2 * cos_ta_1da, r2 * sin_ta_1da, -gearDefinition.width * 0.5f, normal); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + + // Outard teeth faces + normal = glm::vec3(v1, -u1, 0.0f); + ix0 = addVertex(r1 * cos_ta, r1 * sin_ta, gearDefinition.width * 0.5f, normal); + ix1 = addVertex(r1 * cos_ta, r1 * sin_ta, -gearDefinition.width * 0.5f, normal); + ix2 = addVertex(r2 * cos_ta_1da, r2 * sin_ta_1da, gearDefinition.width * 0.5f, normal); + ix3 = addVertex(r2 * cos_ta_1da, r2 * sin_ta_1da, -gearDefinition.width * 0.5f, normal); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + + normal = glm::vec3(cos_ta, sin_ta, 0.0f); + ix0 = addVertex(r2 * cos_ta_1da, r2 * sin_ta_1da, gearDefinition.width * 0.5f, normal); + ix1 = addVertex(r2 * cos_ta_1da, r2 * sin_ta_1da, -gearDefinition.width * 0.5f, normal); + ix2 = addVertex(r2 * cos_ta_2da, r2 * sin_ta_2da, gearDefinition.width * 0.5f, normal); + ix3 = addVertex(r2 * cos_ta_2da, r2 * sin_ta_2da, -gearDefinition.width * 0.5f, normal); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + + normal = glm::vec3(v2, -u2, 0.0f); + ix0 = addVertex(r2 * cos_ta_2da, r2 * sin_ta_2da, gearDefinition.width * 0.5f, normal); + ix1 = addVertex(r2 * cos_ta_2da, r2 * sin_ta_2da, -gearDefinition.width * 0.5f, normal); + ix2 = addVertex(r1 * cos_ta_3da, r1 * sin_ta_3da, gearDefinition.width * 0.5f, normal); + ix3 = addVertex(r1 * cos_ta_3da, r1 * sin_ta_3da, -gearDefinition.width * 0.5f, normal); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + + normal = glm::vec3(cos_ta, sin_ta, 0.0f); + ix0 = addVertex(r1 * cos_ta_3da, r1 * sin_ta_3da, gearDefinition.width * 0.5f, normal); + ix1 = addVertex(r1 * cos_ta_3da, r1 * sin_ta_3da, -gearDefinition.width * 0.5f, normal); + ix2 = addVertex(r1 * cos_ta_4da, r1 * sin_ta_4da, gearDefinition.width * 0.5f, normal); + ix3 = addVertex(r1 * cos_ta_4da, r1 * sin_ta_4da, -gearDefinition.width * 0.5f, normal); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + + // Inside cylinder faces + ix0 = addVertex(r0 * cos_ta, r0 * sin_ta, -gearDefinition.width * 0.5f, glm::vec3(-cos_ta, -sin_ta, 0.0f)); + ix1 = addVertex(r0 * cos_ta, r0 * sin_ta, gearDefinition.width * 0.5f, glm::vec3(-cos_ta, -sin_ta, 0.0f)); + ix2 = addVertex(r0 * cos_ta_4da, r0 * sin_ta_4da, -gearDefinition.width * 0.5f, glm::vec3(-cos_ta_4da, -sin_ta_4da, 0.0f)); + ix3 = addVertex(r0 * cos_ta_4da, r0 * sin_ta_4da, gearDefinition.width * 0.5f, glm::vec3(-cos_ta_4da, -sin_ta_4da, 0.0f)); + addFace(ix0, ix1, ix2); + addFace(ix1, ix3, ix2); + } + + // We need to know how many indices this triangle has at draw time + indexCount = static_cast(indexBuffer.size()) - indexStart; + } +}; + +/* + * VulkanExample + */ class VulkanExample : public VulkanExampleBase { public: - struct { - VkPipelineVertexInputStateCreateInfo inputState; - std::vector bindingDescriptions; - std::vector attributeDescriptions; - } vertices; - - std::vector gears; + std::vector gears{}; VkPipeline pipeline{ VK_NULL_HANDLE }; VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; + // Even though this sample renders multiple objects (gears), we only use single buffers + // This is a best practices and Vulkan applications should keep the number of memory allocations as small as possible + // Having as little buffers as possible also reduces the number of buffer binds + vks::Buffer vertexBuffer; + vks::Buffer indexBuffer; + struct UniformData + { + glm::mat4 projection; + glm::mat4 view; + glm::vec4 lightPos; + // The model matrix is used to rotate a given gear, so we have one mat4 per gear + glm::mat4 model[numGears]; + } uniformData; + vks::Buffer uniformBuffer; + VulkanExample() : VulkanExampleBase() { title = "Vulkan gears"; camera.type = Camera::CameraType::lookat; camera.setPosition(glm::vec3(0.0f, 2.5f, -16.0f)); - camera.setRotation(glm::vec3(-23.75f, 41.25f, 21.0f)); + camera.setRotation(glm::vec3(0.0f, 0.0f, 0.0f)); camera.setPerspective(60.0f, (float)width / (float)height, 0.001f, 256.0f); timerSpeed *= 0.25f; } ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - vkDestroyPipeline(device, pipeline, nullptr); - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - for (auto& gear : gears) - { - delete(gear); + if (device) { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + indexBuffer.destroy(); + vertexBuffer.destroy(); + uniformBuffer.destroy(); } } + void prepareGears() + { + // Set up three differntly shaped and colored gears + std::vector gearDefinitions(3); + + // Large red gear + gearDefinitions[0].innerRadius = 1.0f; + gearDefinitions[0].outerRadius = 4.0f; + gearDefinitions[0].width = 1.0f; + gearDefinitions[0].numTeeth = 20; + gearDefinitions[0].toothDepth = 0.7f; + gearDefinitions[0].color = { 1.0f, 0.0f, 0.0f }; + gearDefinitions[0].pos = { -3.0f, 0.0f, 0.0f }; + gearDefinitions[0].rotSpeed = 1.0f; + gearDefinitions[0].rotOffset = 0.0f; + + // Medium sized green gear + gearDefinitions[1].innerRadius = 0.5f; + gearDefinitions[1].outerRadius = 2.0f; + gearDefinitions[1].width = 2.0f; + gearDefinitions[1].numTeeth = 10; + gearDefinitions[1].toothDepth = 0.7f; + gearDefinitions[1].color = { 0.0f, 1.0f, 0.2f }; + gearDefinitions[1].pos = { 3.1f, 0.0f, 0.0f }; + gearDefinitions[1].rotSpeed = -2.0f; + gearDefinitions[1].rotOffset = -9.0f; + + // Small blue gear + gearDefinitions[2].innerRadius = 1.3f; + gearDefinitions[2].outerRadius = 2.0f; + gearDefinitions[2].width = 0.5f; + gearDefinitions[2].numTeeth = 10; + gearDefinitions[2].toothDepth = 0.7f; + gearDefinitions[2].color = { 0.0f, 0.0f, 1.0f }; + gearDefinitions[2].pos = { -3.1f, -6.2f, 0.0f }; + gearDefinitions[2].rotSpeed = -2.0f; + gearDefinitions[2].rotOffset = -30.0f; + + // We'll be using a single vertex and a single index buffer for all the gears, no matter their number + // This is a Vulkan best practice as it keeps the no. of memory/buffer allocations low + // Vulkan offers all the tools to easily index into those buffers at a later point (see the buildCommandBuffers function) + std::vector vertices{}; + std::vector indices{}; + + // Fills the vertex and index buffers for each of the gear + gears.resize(gearDefinitions.size()); + for (int32_t i = 0; i < gears.size(); i++) { + gears[i].generate(gearDefinitions[i], vertices, indices); + } + + // Create buffers and stage to device for performances + size_t vertexBufferSize = vertices.size() * sizeof(Gear::Vertex); + size_t indexBufferSize = indices.size() * sizeof(uint32_t); + + vks::Buffer vertexStaging, indexStaging; + + // Temorary Staging buffers from vertex and index data + vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &vertexStaging, vertexBufferSize, vertices.data()); + vulkanDevice->createBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &indexStaging, indexBufferSize, indices.data()); + // Device local buffers to where our staging buffers will be copied to + vulkanDevice->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &vertexBuffer, vertexBufferSize); + vulkanDevice->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &indexBuffer, indexBufferSize); + + // Copy host (staging) to device + VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + VkBufferCopy copyRegion = {}; + copyRegion.size = vertexBufferSize; + vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, vertexBuffer.buffer, 1, ©Region); + copyRegion.size = indexBufferSize; + vkCmdCopyBuffer(copyCmd, indexStaging.buffer, indexBuffer.buffer, 1, ©Region); + vulkanDevice->flushCommandBuffer(copyCmd, queue, true); + + vertexStaging.destroy(); + indexStaging.destroy(); + } + + void setupDescriptors() + { + // We use a single descriptor set for the uniform data that contains both global matrices as well as per-gear model matrices + + // Pool + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, static_cast(gears.size())); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Layout + std::vector setLayoutBindings = { + // Binding 0 : Vertex shader uniform buffer + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0) + }; + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); + + VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor); + vkUpdateDescriptorSets(vulkanDevice->logicalDevice, 1, &writeDescriptorSet, 0, nullptr); + } + + void preparePipelines() + { + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + // Pipelines + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE, 0); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + + // Solid rendering pipeline + // Load shaders + std::array shaderStages; + + shaderStages[0] = loadShader(getShadersPath() + "gears/gears.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "gears/gears.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + // Vertex bindings and attributes to match the vertex buffers storing the vertices for the gears + VkVertexInputBindingDescription vertexInputBinding = { + vks::initializers::vertexInputBindingDescription(0, sizeof(Gear::Vertex), VK_VERTEX_INPUT_RATE_VERTEX) + }; + std::vector vertexInputAttributes = { + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Gear::Vertex, position)), // Location 0 : Position + vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Gear::Vertex, normal)), // Location 1 : Normal + vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Gear::Vertex, color)), // Location 2 : Color + }; + VkPipelineVertexInputStateCreateInfo vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(); + vertexInputStateCI.vertexBindingDescriptionCount = 1; + vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; + vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); + vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); + pipelineCreateInfo.pVertexInputState = &vertexInputStateCI; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pRasterizationState = &rasterizationState; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); + pipelineCreateInfo.pStages = shaderStages.data(); + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); + } + void buildCommandBuffers() { VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); @@ -83,9 +444,16 @@ public: vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - for (auto& gear : gears) - { - gear->draw(drawCmdBuffers[i], pipelineLayout); + // Vertices, indices and uniform data for all gears are stored in single buffers, so we only need to bind one buffer of each type and then index/offset into that for each separate gear + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets); + vkCmdBindIndexBuffer(drawCmdBuffers[i], indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); + for (auto j = 0; j < numGears; j++) { + // We use the instance index (last argument) to pass the index of the triangle to the shader + // With this we can index into the model matrices array of the uniform buffer like this (see gears.vert): + // ubo.model[gl_InstanceIndex]; + vkCmdDrawIndexed(drawCmdBuffers[i], gears[j].indexCount, 1, gears[j].indexStart, 0, j); } drawUI(drawCmdBuffers[i]); @@ -96,194 +464,43 @@ public: } } - void prepareVertices() + void prepareUniformBuffers() { - // Gear definitions - std::vector innerRadiuses = { 1.0f, 0.5f, 1.3f }; - std::vector outerRadiuses = { 4.0f, 2.0f, 2.0f }; - std::vector widths = { 1.0f, 2.0f, 0.5f }; - std::vector toothCount = { 20, 10, 10 }; - std::vector toothDepth = { 0.7f, 0.7f, 0.7f }; - std::vector colors = { - glm::vec3(1.0f, 0.0f, 0.0f), - glm::vec3(0.0f, 1.0f, 0.2f), - glm::vec3(0.0f, 0.0f, 1.0f) - }; - std::vector positions = { - glm::vec3(-3.0, 0.0, 0.0), - glm::vec3(3.1, 0.0, 0.0), - glm::vec3(-3.1, -6.2, 0.0) - }; - std::vector rotationSpeeds = { 1.0f, -2.0f, -2.0f }; - std::vector rotationOffsets = { 0.0f, -9.0f, -30.0f }; - - gears.resize(positions.size()); - for (int32_t i = 0; i < gears.size(); ++i) - { - GearInfo gearInfo = {}; - gearInfo.innerRadius = innerRadiuses[i]; - gearInfo.outerRadius = outerRadiuses[i]; - gearInfo.width = widths[i]; - gearInfo.numTeeth = toothCount[i]; - gearInfo.toothDepth = toothDepth[i]; - gearInfo.color = colors[i]; - gearInfo.pos = positions[i]; - gearInfo.rotSpeed = rotationSpeeds[i]; - gearInfo.rotOffset = rotationOffsets[i]; - - gears[i] = new VulkanGear(vulkanDevice); - gears[i]->generate(&gearInfo, queue); - } - - // Binding and attribute descriptions are shared across all gears - vertices.bindingDescriptions.resize(1); - vertices.bindingDescriptions[0] = - vks::initializers::vertexInputBindingDescription( - 0, - sizeof(Vertex), - VK_VERTEX_INPUT_RATE_VERTEX); - - // Attribute descriptions - // Describes memory layout and shader positions - vertices.attributeDescriptions.resize(3); - // Location 0 : Position - vertices.attributeDescriptions[0] = - vks::initializers::vertexInputAttributeDescription( - 0, - 0, - VK_FORMAT_R32G32B32_SFLOAT, - 0); - // Location 1 : Normal - vertices.attributeDescriptions[1] = - vks::initializers::vertexInputAttributeDescription( - 0, - 1, - VK_FORMAT_R32G32B32_SFLOAT, - sizeof(float) * 3); - // Location 2 : Color - vertices.attributeDescriptions[2] = - vks::initializers::vertexInputAttributeDescription( - 0, - 2, - VK_FORMAT_R32G32B32_SFLOAT, - sizeof(float) * 6); - - vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo(); - vertices.inputState.vertexBindingDescriptionCount = static_cast(vertices.bindingDescriptions.size()); - vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); - vertices.inputState.vertexAttributeDescriptionCount = static_cast(vertices.attributeDescriptions.size()); - vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); - } - - void setupDescriptorPool() - { - // One UBO for each gear - std::vector poolSizes = { - vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3), - }; - VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, static_cast(gears.size())); - VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - - void setupDescriptorSetLayout() - { - std::vector setLayoutBindings = - { - // Binding 0 : Vertex shader uniform buffer - vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0) - }; - VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); - VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - } - - void setupDescriptorSets() - { - for (auto& gear : gears) - { - gear->setupDescriptorSet(descriptorPool, descriptorSetLayout); - } - } - - void preparePipelines() - { - VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = - vks::initializers::pipelineInputAssemblyStateCreateInfo( - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - 0, - VK_FALSE); - - VkPipelineRasterizationStateCreateInfo rasterizationState = - vks::initializers::pipelineRasterizationStateCreateInfo( - VK_POLYGON_MODE_FILL, - VK_CULL_MODE_BACK_BIT, - VK_FRONT_FACE_CLOCKWISE, - 0); - - VkPipelineColorBlendAttachmentState blendAttachmentState = - vks::initializers::pipelineColorBlendAttachmentState( - 0xf, - VK_FALSE); - - VkPipelineColorBlendStateCreateInfo colorBlendState = - vks::initializers::pipelineColorBlendStateCreateInfo( - 1, - &blendAttachmentState); - - VkPipelineDepthStencilStateCreateInfo depthStencilState = - vks::initializers::pipelineDepthStencilStateCreateInfo( - VK_TRUE, - VK_TRUE, - VK_COMPARE_OP_LESS_OR_EQUAL); - - VkPipelineViewportStateCreateInfo viewportState = - vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); - - VkPipelineMultisampleStateCreateInfo multisampleState = - vks::initializers::pipelineMultisampleStateCreateInfo( - VK_SAMPLE_COUNT_1_BIT, - 0); - - std::vector dynamicStateEnables = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; - VkPipelineDynamicStateCreateInfo dynamicState = - vks::initializers::pipelineDynamicStateCreateInfo( - dynamicStateEnables.data(), - static_cast(dynamicStateEnables.size()), - 0); - - // Solid rendering pipeline - // Load shaders - std::array shaderStages; - - shaderStages[0] = loadShader(getShadersPath() + "gears/gears.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shaderStages[1] = loadShader(getShadersPath() + "gears/gears.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - - VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); - pipelineCreateInfo.pVertexInputState = &vertices.inputState; - pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; - pipelineCreateInfo.pRasterizationState = &rasterizationState; - pipelineCreateInfo.pColorBlendState = &colorBlendState; - pipelineCreateInfo.pMultisampleState = &multisampleState; - pipelineCreateInfo.pViewportState = &viewportState; - pipelineCreateInfo.pDepthStencilState = &depthStencilState; - pipelineCreateInfo.pDynamicState = &dynamicState; - pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); - pipelineCreateInfo.pStages = shaderStages.data(); - - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); + // Create the vertex shader uniform buffer block + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData))); + // Map persistent + VK_CHECK_RESULT(uniformBuffer.map()); } void updateUniformBuffers() { - for (auto& gear : gears) - { - gear->updateUniformBuffer(camera.matrices.perspective, camera.matrices.view, timer * 360.0f); + float degree = timer * 360.0f; + + // Camera specific global matrices + uniformData.projection = camera.matrices.perspective; + uniformData.view = camera.matrices.view; + uniformData.lightPos = glm::vec4(0.0f, 0.0f, 2.5f, 1.0f); + + // Update the model matrix for each gear that contains it's position and rotation + for (auto i = 0; i < numGears; i++) { + Gear gear = gears[i]; + uniformData.model[i] = glm::mat4(1.0f); + uniformData.model[i] = glm::translate(uniformData.model[i], gear.pos); + uniformData.model[i] = glm::rotate(uniformData.model[i], glm::radians((gear.rotSpeed * degree) + gear.rotOffset), glm::vec3(0.0f, 0.0f, 1.0f)); } + + memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData)); + } + + void prepare() + { + VulkanExampleBase::prepare(); + prepareGears(); + prepareUniformBuffers(); + setupDescriptors(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; } void draw() @@ -295,19 +512,6 @@ public: VulkanExampleBase::submitFrame(); } - void prepare() - { - VulkanExampleBase::prepare(); - prepareVertices(); - setupDescriptorSetLayout(); - preparePipelines(); - setupDescriptorPool(); - setupDescriptorSets(); - updateUniformBuffers(); - buildCommandBuffers(); - prepared = true; - } - virtual void render() { if (!prepared) @@ -316,10 +520,6 @@ public: draw(); } - virtual void viewChanged() - { - updateUniformBuffers(); - } }; VULKAN_EXAMPLE_MAIN() \ No newline at end of file diff --git a/examples/gears/vulkangear.cpp b/examples/gears/vulkangear.cpp deleted file mode 100644 index fa0652e7..00000000 --- a/examples/gears/vulkangear.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/* -* Vulkan Example - Animated gears using multiple uniform buffers -* -* Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -#include "vulkangear.h" - -int32_t VulkanGear::newVertex(std::vector *vBuffer, float x, float y, float z, const glm::vec3& normal) -{ - Vertex v(glm::vec3(x, y, z), normal, color); - vBuffer->push_back(v); - return static_cast(vBuffer->size()) - 1; -} - -void VulkanGear::newFace(std::vector *iBuffer, int a, int b, int c) -{ - iBuffer->push_back(a); - iBuffer->push_back(b); - iBuffer->push_back(c); -} - -VulkanGear::~VulkanGear() -{ - // Clean up vulkan resources - uniformBuffer.destroy(); - vertexBuffer.destroy(); - indexBuffer.destroy(); -} - -void VulkanGear::generate(GearInfo *gearinfo, VkQueue queue) -{ - this->color = gearinfo->color; - this->pos = gearinfo->pos; - this->rotOffset = gearinfo->rotOffset; - this->rotSpeed = gearinfo->rotSpeed; - - std::vector vBuffer; - std::vector iBuffer; - - int i; - float r0, r1, r2; - float ta, da; - float u1, v1, u2, v2, len; - float cos_ta, cos_ta_1da, cos_ta_2da, cos_ta_3da, cos_ta_4da; - float sin_ta, sin_ta_1da, sin_ta_2da, sin_ta_3da, sin_ta_4da; - int32_t ix0, ix1, ix2, ix3, ix4, ix5; - - r0 = gearinfo->innerRadius; - r1 = gearinfo->outerRadius - gearinfo->toothDepth / 2.0f; - r2 = gearinfo->outerRadius + gearinfo->toothDepth / 2.0f; - da = static_cast < float>(2.0 * M_PI / gearinfo->numTeeth / 4.0); - - glm::vec3 normal; - - for (i = 0; i < gearinfo->numTeeth; i++) - { - ta = i * static_cast (2.0 * M_PI / gearinfo->numTeeth); - - cos_ta = cos(ta); - cos_ta_1da = cos(ta + da); - cos_ta_2da = cos(ta + 2.0f * da); - cos_ta_3da = cos(ta + 3.0f * da); - cos_ta_4da = cos(ta + 4.0f * da); - sin_ta = sin(ta); - sin_ta_1da = sin(ta + da); - sin_ta_2da = sin(ta + 2.0f * da); - sin_ta_3da = sin(ta + 3.0f * da); - sin_ta_4da = sin(ta + 4.0f * da); - - u1 = r2 * cos_ta_1da - r1 * cos_ta; - v1 = r2 * sin_ta_1da - r1 * sin_ta; - len = sqrt(u1 * u1 + v1 * v1); - u1 /= len; - v1 /= len; - u2 = r1 * cos_ta_3da - r2 * cos_ta_2da; - v2 = r1 * sin_ta_3da - r2 * sin_ta_2da; - - // front face - normal = glm::vec3(0.0f, 0.0f, 1.0f); - ix0 = newVertex(&vBuffer, r0 * cos_ta, r0 * sin_ta, gearinfo->width * 0.5f, normal); - ix1 = newVertex(&vBuffer, r1 * cos_ta, r1 * sin_ta, gearinfo->width * 0.5f, normal); - ix2 = newVertex(&vBuffer, r0 * cos_ta, r0 * sin_ta, gearinfo->width * 0.5f, normal); - ix3 = newVertex(&vBuffer, r1 * cos_ta_3da, r1 * sin_ta_3da, gearinfo->width * 0.5f, normal); - ix4 = newVertex(&vBuffer, r0 * cos_ta_4da, r0 * sin_ta_4da, gearinfo->width * 0.5f, normal); - ix5 = newVertex(&vBuffer, r1 * cos_ta_4da, r1 * sin_ta_4da, gearinfo->width * 0.5f, normal); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - newFace(&iBuffer, ix2, ix3, ix4); - newFace(&iBuffer, ix3, ix5, ix4); - - // front sides of teeth - normal = glm::vec3(0.0f, 0.0f, 1.0f); - ix0 = newVertex(&vBuffer, r1 * cos_ta, r1 * sin_ta, gearinfo->width * 0.5f, normal); - ix1 = newVertex(&vBuffer, r2 * cos_ta_1da, r2 * sin_ta_1da, gearinfo->width * 0.5f, normal); - ix2 = newVertex(&vBuffer, r1 * cos_ta_3da, r1 * sin_ta_3da, gearinfo->width * 0.5f, normal); - ix3 = newVertex(&vBuffer, r2 * cos_ta_2da, r2 * sin_ta_2da, gearinfo->width * 0.5f, normal); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - - // back face - normal = glm::vec3(0.0f, 0.0f, -1.0f); - ix0 = newVertex(&vBuffer, r1 * cos_ta, r1 * sin_ta, -gearinfo->width * 0.5f, normal); - ix1 = newVertex(&vBuffer, r0 * cos_ta, r0 * sin_ta, -gearinfo->width * 0.5f, normal); - ix2 = newVertex(&vBuffer, r1 * cos_ta_3da, r1 * sin_ta_3da, -gearinfo->width * 0.5f, normal); - ix3 = newVertex(&vBuffer, r0 * cos_ta, r0 * sin_ta, -gearinfo->width * 0.5f, normal); - ix4 = newVertex(&vBuffer, r1 * cos_ta_4da, r1 * sin_ta_4da, -gearinfo->width * 0.5f, normal); - ix5 = newVertex(&vBuffer, r0 * cos_ta_4da, r0 * sin_ta_4da, -gearinfo->width * 0.5f, normal); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - newFace(&iBuffer, ix2, ix3, ix4); - newFace(&iBuffer, ix3, ix5, ix4); - - // back sides of teeth - normal = glm::vec3(0.0f, 0.0f, -1.0f); - ix0 = newVertex(&vBuffer, r1 * cos_ta_3da, r1 * sin_ta_3da, -gearinfo->width * 0.5f, normal); - ix1 = newVertex(&vBuffer, r2 * cos_ta_2da, r2 * sin_ta_2da, -gearinfo->width * 0.5f, normal); - ix2 = newVertex(&vBuffer, r1 * cos_ta, r1 * sin_ta, -gearinfo->width * 0.5f, normal); - ix3 = newVertex(&vBuffer, r2 * cos_ta_1da, r2 * sin_ta_1da, -gearinfo->width * 0.5f, normal); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - - // draw outward faces of teeth - normal = glm::vec3(v1, -u1, 0.0f); - ix0 = newVertex(&vBuffer, r1 * cos_ta, r1 * sin_ta, gearinfo->width * 0.5f, normal); - ix1 = newVertex(&vBuffer, r1 * cos_ta, r1 * sin_ta, -gearinfo->width * 0.5f, normal); - ix2 = newVertex(&vBuffer, r2 * cos_ta_1da, r2 * sin_ta_1da, gearinfo->width * 0.5f, normal); - ix3 = newVertex(&vBuffer, r2 * cos_ta_1da, r2 * sin_ta_1da, -gearinfo->width * 0.5f, normal); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - - normal = glm::vec3(cos_ta, sin_ta, 0.0f); - ix0 = newVertex(&vBuffer, r2 * cos_ta_1da, r2 * sin_ta_1da, gearinfo->width * 0.5f, normal); - ix1 = newVertex(&vBuffer, r2 * cos_ta_1da, r2 * sin_ta_1da, -gearinfo->width * 0.5f, normal); - ix2 = newVertex(&vBuffer, r2 * cos_ta_2da, r2 * sin_ta_2da, gearinfo->width * 0.5f, normal); - ix3 = newVertex(&vBuffer, r2 * cos_ta_2da, r2 * sin_ta_2da, -gearinfo->width * 0.5f, normal); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - - normal = glm::vec3(v2, -u2, 0.0f); - ix0 = newVertex(&vBuffer, r2 * cos_ta_2da, r2 * sin_ta_2da, gearinfo->width * 0.5f, normal); - ix1 = newVertex(&vBuffer, r2 * cos_ta_2da, r2 * sin_ta_2da, -gearinfo->width * 0.5f, normal); - ix2 = newVertex(&vBuffer, r1 * cos_ta_3da, r1 * sin_ta_3da, gearinfo->width * 0.5f, normal); - ix3 = newVertex(&vBuffer, r1 * cos_ta_3da, r1 * sin_ta_3da, -gearinfo->width * 0.5f, normal); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - - normal = glm::vec3(cos_ta, sin_ta, 0.0f); - ix0 = newVertex(&vBuffer, r1 * cos_ta_3da, r1 * sin_ta_3da, gearinfo->width * 0.5f, normal); - ix1 = newVertex(&vBuffer, r1 * cos_ta_3da, r1 * sin_ta_3da, -gearinfo->width * 0.5f, normal); - ix2 = newVertex(&vBuffer, r1 * cos_ta_4da, r1 * sin_ta_4da, gearinfo->width * 0.5f, normal); - ix3 = newVertex(&vBuffer, r1 * cos_ta_4da, r1 * sin_ta_4da, -gearinfo->width * 0.5f, normal); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - - // draw inside radius cylinder - ix0 = newVertex(&vBuffer, r0 * cos_ta, r0 * sin_ta, -gearinfo->width * 0.5f, glm::vec3(-cos_ta, -sin_ta, 0.0f)); - ix1 = newVertex(&vBuffer, r0 * cos_ta, r0 * sin_ta, gearinfo->width * 0.5f, glm::vec3(-cos_ta, -sin_ta, 0.0f)); - ix2 = newVertex(&vBuffer, r0 * cos_ta_4da, r0 * sin_ta_4da, -gearinfo->width * 0.5f, glm::vec3(-cos_ta_4da, -sin_ta_4da, 0.0f)); - ix3 = newVertex(&vBuffer, r0 * cos_ta_4da, r0 * sin_ta_4da, gearinfo->width * 0.5f, glm::vec3(-cos_ta_4da, -sin_ta_4da, 0.0f)); - newFace(&iBuffer, ix0, ix1, ix2); - newFace(&iBuffer, ix1, ix3, ix2); - } - - size_t vertexBufferSize = vBuffer.size() * sizeof(Vertex); - size_t indexBufferSize = iBuffer.size() * sizeof(uint32_t); - - vks::Buffer vertexStaging, indexStaging; - - // Create staging buffers - // Vertex data - vulkanDevice->createBuffer( - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, - &vertexStaging, - vertexBufferSize, - vBuffer.data()); - // Index data - vulkanDevice->createBuffer( - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, - &indexStaging, - indexBufferSize, - iBuffer.data()); - - // Create device local buffers - // Vertex buffer - vulkanDevice->createBuffer( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - &vertexBuffer, - vertexBufferSize); - // Index buffer - vulkanDevice->createBuffer( - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - &indexBuffer, - indexBufferSize); - - // Copy from staging buffers - VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - VkBufferCopy copyRegion = {}; - - copyRegion.size = vertexBufferSize; - vkCmdCopyBuffer( - copyCmd, - vertexStaging.buffer, - vertexBuffer.buffer, - 1, - ©Region); - - copyRegion.size = indexBufferSize; - vkCmdCopyBuffer( - copyCmd, - indexStaging.buffer, - indexBuffer.buffer, - 1, - ©Region); - - vulkanDevice->flushCommandBuffer(copyCmd, queue, true); - - vkDestroyBuffer(vulkanDevice->logicalDevice, vertexStaging.buffer, nullptr); - vkFreeMemory(vulkanDevice->logicalDevice, vertexStaging.memory, nullptr); - vkDestroyBuffer(vulkanDevice->logicalDevice, indexStaging.buffer, nullptr); - vkFreeMemory(vulkanDevice->logicalDevice, indexStaging.memory, nullptr); - - indexCount = static_cast(iBuffer.size()); - - prepareUniformBuffer(); -} - -void VulkanGear::draw(VkCommandBuffer cmdbuffer, VkPipelineLayout pipelineLayout) -{ - VkDeviceSize offsets[1] = { 0 }; - vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); - vkCmdBindVertexBuffers(cmdbuffer, 0, 1, &vertexBuffer.buffer, offsets); - vkCmdBindIndexBuffer(cmdbuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(cmdbuffer, indexCount, 1, 0, 0, 1); -} - -void VulkanGear::updateUniformBuffer(glm::mat4 perspective, glm::mat4 view, float timer) -{ - ubo.projection = perspective; - ubo.view = view; - ubo.model = glm::mat4(1.0f); - ubo.model = glm::translate(ubo.model, pos); - ubo.model = glm::rotate(ubo.model, glm::radians((rotSpeed * timer) + rotOffset), glm::vec3(0.0f, 0.0f, 1.0f)); - ubo.normal = glm::inverseTranspose(ubo.view * ubo.model); - ubo.lightPos = glm::vec3(0.0f, 0.0f, 2.5f); - ubo.lightPos.x = sin(glm::radians(timer)) * 8.0f; - ubo.lightPos.z = cos(glm::radians(timer)) * 8.0f; - memcpy(uniformBuffer.mapped, &ubo, sizeof(ubo)); -} - -void VulkanGear::setupDescriptorSet(VkDescriptorPool pool, VkDescriptorSetLayout descriptorSetLayout) -{ - VkDescriptorSetAllocateInfo allocInfo = - vks::initializers::descriptorSetAllocateInfo( - pool, - &descriptorSetLayout, - 1); - - VK_CHECK_RESULT(vkAllocateDescriptorSets(vulkanDevice->logicalDevice, &allocInfo, &descriptorSet)); - - // Binding 0 : Vertex shader uniform buffer - VkWriteDescriptorSet writeDescriptorSet = - vks::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &uniformBuffer.descriptor); - - vkUpdateDescriptorSets(vulkanDevice->logicalDevice, 1, &writeDescriptorSet, 0, NULL); -} - -void VulkanGear::prepareUniformBuffer() -{ - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBuffer, - sizeof(ubo))); - // Map persistent - VK_CHECK_RESULT(uniformBuffer.map()); -} diff --git a/examples/gears/vulkangear.h b/examples/gears/vulkangear.h deleted file mode 100644 index d261db71..00000000 --- a/examples/gears/vulkangear.h +++ /dev/null @@ -1,102 +0,0 @@ -/* -* Vulkan Example - Animated gears using multiple uniform buffers -* -* Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -#pragma once - -#include -#include - -#define GLM_FORCE_RADIANS -#define GLM_FORCE_DEPTH_ZERO_TO_ONE -#include -#include -#include - -#include "vulkan/vulkan.h" -#include "VulkanTools.h" -#include "VulkanBuffer.h" -#include "VulkanDevice.h" - -struct Vertex -{ - float pos[3]; - float normal[3]; - float color[3]; - - Vertex(const glm::vec3& p, const glm::vec3& n, const glm::vec3& c) - { - pos[0] = p.x; - pos[1] = p.y; - pos[2] = p.z; - color[0] = c.x; - color[1] = c.y; - color[2] = c.z; - normal[0] = n.x; - normal[1] = n.y; - normal[2] = n.z; - } -}; - -struct GearInfo -{ - float innerRadius; - float outerRadius; - float width; - int numTeeth; - float toothDepth; - glm::vec3 color; - glm::vec3 pos; - float rotSpeed; - float rotOffset; -}; - -class VulkanGear -{ -private: - struct UBO - { - glm::mat4 projection; - glm::mat4 model; - glm::mat4 normal; - glm::mat4 view; - glm::vec3 lightPos; - }; - - vks::VulkanDevice *vulkanDevice; - - glm::vec3 color; - glm::vec3 pos; - float rotSpeed; - float rotOffset; - - vks::Buffer vertexBuffer; - vks::Buffer indexBuffer; - uint32_t indexCount; - - UBO ubo; - vks::Buffer uniformBuffer; - - int32_t newVertex(std::vector *vBuffer, float x, float y, float z, const glm::vec3& normal); - void newFace(std::vector *iBuffer, int a, int b, int c); - - void prepareUniformBuffer(); -public: - VkDescriptorSet descriptorSet; - - void draw(VkCommandBuffer cmdbuffer, VkPipelineLayout pipelineLayout); - void updateUniformBuffer(glm::mat4 perspective, glm::mat4 view, float timer); - - void setupDescriptorSet(VkDescriptorPool pool, VkDescriptorSetLayout descriptorSetLayout); - - VulkanGear(vks::VulkanDevice *vulkanDevice) : vulkanDevice(vulkanDevice) {}; - ~VulkanGear(); - - void generate(GearInfo *gearinfo, VkQueue queue); - -}; - diff --git a/shaders/glsl/gears/gears.vert b/shaders/glsl/gears/gears.vert index 51f01195..d0799d1d 100644 --- a/shaders/glsl/gears/gears.vert +++ b/shaders/glsl/gears/gears.vert @@ -7,10 +7,9 @@ layout (location = 2) in vec3 inColor; layout (binding = 0) uniform UBO { mat4 projection; - mat4 model; - mat4 normal; mat4 view; - vec3 lightpos; + vec4 lightpos; + mat4 model[3]; } ubo; layout (location = 0) out vec3 outNormal; @@ -20,12 +19,12 @@ layout (location = 3) out vec3 outLightVec; void main() { - outNormal = normalize(mat3(ubo.normal) * inNormal); + outNormal = normalize(mat3x3(ubo.model[gl_InstanceIndex]) * inNormal); outColor = inColor; - mat4 modelView = ubo.view * ubo.model; + mat4 modelView = ubo.view * ubo.model[gl_InstanceIndex]; vec4 pos = modelView * inPos; outEyePos = vec3(modelView * pos); - vec4 lightPos = vec4(ubo.lightpos, 1.0) * modelView; + vec4 lightPos = vec4(ubo.lightpos.xyz, 1.0) * modelView; outLightVec = normalize(lightPos.xyz - outEyePos); gl_Position = ubo.projection * pos; } \ No newline at end of file diff --git a/shaders/glsl/gears/gears.vert.spv b/shaders/glsl/gears/gears.vert.spv index fc950d2f56d0f363e256d1b2b8b2bede4be478bd..0c30fe6f5b51dfb59718d65c7d0c94bdb6befc00 100644 GIT binary patch literal 2924 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 literal 3576 zcmZvd`A=L`6vtnIfi6g`wX|*s1$P~*sisMTT9E(-8H7swm9`Td0}P=&gD&ziV68mNQ$j&U_!6 zoSiHmZ5%1@y>5^8o~+$1w{6YN$n`eH{8 z^tsu}YGZk|fl5qA&Z$q=kAj;%b~#mFS)5}Rre z*WHb|>S5Hp?m6s@Y=)vb>&=ACHg!U{f?U}c^-Aw@=E=l>fuJM&S|YFZ*PnB zBUM^k=j?5%(64^>cC1#tmu)?#y)D?=vK`x7d$xtW*+W+zQ(Gz@n_nqUS7s*;PE@Ay zdnl2?*kqfk%-nWh5{ta^P5ek@vNB_UymwJwZRE)!K;HWaJ_1U3duPEHi)-^P!*+1= z&XG!G=9X*WN_16x9>hcNbF&jmUa?(^U2(tS*87z!-miUL1h+iDGQXIgpqA$wBcIQi zGjoZZb6<{{xFca}Xm>u^%`bK?MStfZ4u9uR^!Myy>lknC-H0pjKoe%;REuY4@P9aK zpNWio}qJd<&MBM~Rp8YfqbQ#a0`85i$T z&OwhcEjjD;qW7@(BRk3BS43T$?8TXx$Gh+zwGDu;VRwG=+V`?+ zmlI!~u;&r)OE~)25B3~!uHl6BcRu6n_UL?!zm?tmajq)5@#6j1or~|1oY=W|&J77W z7jqS{?{Z-0go{hdJMeQ&JeT|SJ+-;R%cJ(4)KtirE{y*-M^D=zQEdyJUJ z(9I(sF^{9mE3VdC%vF7w$lZ%}1p_?<}-$&Oc z;y*x_(=YDqLv(q?6?YbM{RqyvIwNa}jfweuj9#2i75oVz-;3Cb_w^|v@4Mk|%4dmV zTjxGcoIQl!7m4$I@Y!`PUn0iquif9Ln88=*&fpYv^!*xuBhL0cocsxDde$Eh^T@}u|0BA&MiK2X-=EOU=dA2ctZ&TsXY}HHhrz!f z^6{+ry#1Q^IM;9B;@$ramebGNK68H{);5o|#rm0}oBdBjySBB2!*)4XF1~eFqRYAT z?_`(L?o6&iv^xXsU5N{OIoZ8u?Hk$U!hUtK`;F7SiCr%2dy>5)**CMx{Y%At$bT8# B3gZ9(