From 9f881313714bd93f6477bb3b941bfdcf42438d30 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Tue, 21 Mar 2023 20:10:23 +0000 Subject: [PATCH] Add combined image sampler descriptor support --- include/application.hpp | 4 +- include/gfx.hpp | 21 ++++++- include/gfx_device.hpp | 11 ++-- src/application.cpp | 25 ++++++--- src/gfx_device_vulkan.cpp | 114 ++++++++++++++++++++++++++------------ src/systems/render.cpp | 4 +- 6 files changed, 122 insertions(+), 57 deletions(-) diff --git a/include/application.hpp b/include/application.hpp index e84b63b..93180ef 100644 --- a/include/application.hpp +++ b/include/application.hpp @@ -34,14 +34,14 @@ namespace engine { struct SetZeroBuffer { glm::mat4 proj; }; - gfx::DescriptorBuffer* setZeroBuffer; + gfx::UniformBuffer* setZeroBuffer; /* uniforms for per-frame data */ const gfx::DescriptorSetLayout* setOneLayout; const gfx::DescriptorSet* setOne; struct SetOneBuffer { glm::mat4 view; }; - gfx::DescriptorBuffer* setOneBuffer; + gfx::UniformBuffer* setOneBuffer; }; class Application { diff --git a/include/gfx.hpp b/include/gfx.hpp index 6fc5475..11779e7 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -2,6 +2,7 @@ #include #include +#include // Enums and structs for the graphics abstraction @@ -9,7 +10,7 @@ namespace engine::gfx { // handles (incomplete types) struct Pipeline; - struct DescriptorBuffer; + struct UniformBuffer; struct Buffer; struct Texture; struct DrawBuffer; @@ -80,10 +81,19 @@ namespace engine::gfx { LINEAR, }; - struct VertexBufferDesc { - uint64_t size; + enum class DescriptorType { + UNIFORM_BUFFER, + COMBINED_IMAGE_SAMPLER, }; + namespace ShaderStageFlags { + enum Bits : uint32_t { + VERTEX = 1 << 0, + FRAGMENT = 1 << 1, + }; + typedef std::underlying_type::type Flags; + } + struct VertexAttribDescription { VertexAttribDescription(uint32_t location, VertexAttribFormat format, uint32_t offset) : location(location), @@ -108,4 +118,9 @@ namespace engine::gfx { std::vector descriptorSetLayouts; }; + struct DescriptorSetLayoutBinding { + DescriptorType descriptorType = DescriptorType::UNIFORM_BUFFER; + ShaderStageFlags::Flags stageFlags = 0; + }; + } diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index c1d0dd6..f23e73e 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -32,17 +32,18 @@ namespace engine { gfx::Pipeline* createPipeline(const gfx::PipelineInfo& info); void destroyPipeline(const gfx::Pipeline* pipeline); - gfx::DescriptorSetLayout* createDescriptorSetLayout(); + gfx::DescriptorSetLayout* createDescriptorSetLayout(const std::vector& bindings); void destroyDescriptorSetLayout(const gfx::DescriptorSetLayout* layout); gfx::DescriptorSet* allocateDescriptorSet(const gfx::DescriptorSetLayout* layout); // This updates all copies of the descriptor. This cannot be used after any frames have been renderered - void updateDescriptor(const gfx::DescriptorSet* set, uint32_t binding, const gfx::DescriptorBuffer* buffer, size_t offset, size_t range); + void updateDescriptorUniformBuffer(const gfx::DescriptorSet* set, uint32_t binding, const gfx::UniformBuffer* buffer, size_t offset, size_t range); + void updateDescriptorCombinedImageSampler(const gfx::DescriptorSet* set, uint32_t binding); - gfx::DescriptorBuffer* createDescriptorBuffer(uint64_t size, const void* initialData); - void destroyDescriptorBuffer(const gfx::DescriptorBuffer* descriptorBuffer); + gfx::UniformBuffer* createUniformBuffer(uint64_t size, const void* initialData); + void destroyUniformBuffer(const gfx::UniformBuffer* descriptorBuffer); - void writeDescriptorBuffer(gfx::DescriptorBuffer* buffer, uint64_t offset, uint64_t size, const void* data); + void writeUniformBuffer(gfx::UniformBuffer* buffer, uint64_t offset, uint64_t size, const void* data); // Loads data into staging buffer and copies that into a single GPU buffer. gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data); diff --git a/src/application.cpp b/src/application.cpp index 4c545bd..e0c87ae 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -75,22 +75,29 @@ namespace engine { // initialise the render data renderData.gfxdev = std::make_unique(appName, appVersion, m_window->getHandle(), graphicsSettings); - - renderData.setZeroLayout = gfx()->createDescriptorSetLayout(); + + std::vector layoutBindings; + { + auto& binding0 = layoutBindings.emplace_back(); + binding0.descriptorType = gfx::DescriptorType::UNIFORM_BUFFER; + binding0.stageFlags = gfx::ShaderStageFlags::VERTEX; + } + + renderData.setZeroLayout = gfx()->createDescriptorSetLayout(layoutBindings); renderData.setZero = gfx()->allocateDescriptorSet(renderData.setZeroLayout); RenderData::SetZeroBuffer initialSetZeroData{ .proj = glm::perspectiveZO(glm::radians(70.0f), 1024.0f / 768.0f, 0.1f, 1000.0f), }; - renderData.setZeroBuffer = gfx()->createDescriptorBuffer(sizeof(RenderData::SetZeroBuffer), &initialSetZeroData); - gfx()->updateDescriptor(renderData.setZero, 0, renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer)); + renderData.setZeroBuffer = gfx()->createUniformBuffer(sizeof(RenderData::SetZeroBuffer), &initialSetZeroData); + gfx()->updateDescriptorUniformBuffer(renderData.setZero, 0, renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer)); - renderData.setOneLayout = gfx()->createDescriptorSetLayout(); + renderData.setOneLayout = gfx()->createDescriptorSetLayout(layoutBindings); renderData.setOne = gfx()->allocateDescriptorSet(renderData.setOneLayout); RenderData::SetOneBuffer initialSetOneData{ .view = glm::mat4{ 1.0f }, }; - renderData.setOneBuffer = gfx()->createDescriptorBuffer(sizeof(RenderData::SetOneBuffer), &initialSetOneData); - gfx()->updateDescriptor(renderData.setOne, 0, renderData.setOneBuffer, 0, sizeof(RenderData::SetOneBuffer)); + renderData.setOneBuffer = gfx()->createUniformBuffer(sizeof(RenderData::SetOneBuffer), &initialSetOneData); + gfx()->updateDescriptorUniformBuffer(renderData.setOne, 0, renderData.setOneBuffer, 0, sizeof(RenderData::SetOneBuffer)); // default resources { @@ -135,9 +142,9 @@ namespace engine { Application::~Application() { - gfx()->destroyDescriptorBuffer(renderData.setOneBuffer); + gfx()->destroyUniformBuffer(renderData.setOneBuffer); gfx()->destroyDescriptorSetLayout(renderData.setOneLayout); - gfx()->destroyDescriptorBuffer(renderData.setZeroBuffer); + gfx()->destroyUniformBuffer(renderData.setZeroBuffer); gfx()->destroyDescriptorSetLayout(renderData.setZeroLayout); } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 01518aa..99291aa 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -87,7 +87,6 @@ namespace engine { VkBuffer buffer = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; VkDeviceSize size = 0; - bool hostVisible = false; }; struct gfx::Pipeline { @@ -113,7 +112,7 @@ namespace engine { std::array sets; // frames in flight cannot use the same descriptor set in case the buffer needs updating }; - struct gfx::DescriptorBuffer { + struct gfx::UniformBuffer { gfx::Buffer stagingBuffer{}; std::array gpuBuffers; }; @@ -165,24 +164,39 @@ namespace engine { switch (level) { case gfx::MSAALevel::MSAA_OFF: return VK_SAMPLE_COUNT_1_BIT; - break; case gfx::MSAALevel::MSAA_2X: return VK_SAMPLE_COUNT_2_BIT; - break; case gfx::MSAALevel::MSAA_4X: return VK_SAMPLE_COUNT_4_BIT; - break; case gfx::MSAALevel::MSAA_8X: return VK_SAMPLE_COUNT_8_BIT; - break; case gfx::MSAALevel::MSAA_16X: return VK_SAMPLE_COUNT_16_BIT; - break; default: throw std::runtime_error("Unknown MSAA level"); } } + static VkDescriptorType getDescriptorType(gfx::DescriptorType type) + { + switch (type) { + case gfx::DescriptorType::UNIFORM_BUFFER: + return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + case gfx::DescriptorType::COMBINED_IMAGE_SAMPLER: + return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + default: + throw std::runtime_error("Unknown descriptor type"); + } + } + + static VkShaderStageFlags getShaderStageFlags(gfx::ShaderStageFlags::Flags flags) + { + VkShaderStageFlags out = 0; + if (flags & gfx::ShaderStageFlags::VERTEX) out |= VK_SHADER_STAGE_VERTEX_BIT; + if (flags & gfx::ShaderStageFlags::FRAGMENT) out |= VK_SHADER_STAGE_FRAGMENT_BIT; + return out; + } + } // functions @@ -296,7 +310,7 @@ namespace engine { Swapchain swapchain{}; VkDescriptorPool descriptorPool; - std::array, FRAMES_IN_FLIGHT> descriptorBufferWriteQueues{}; + std::array, FRAMES_IN_FLIGHT> uniformBufferWriteQueues{}; VkCommandPool transferCommandPool = VK_NULL_HANDLE; @@ -540,16 +554,16 @@ namespace engine { // transfer cmds... std::vector barriers{}; - for (gfx::DescriptorBuffer* descriptorBuffer : pimpl->descriptorBufferWriteQueues[currentFrameIndex]) { + for (gfx::UniformBuffer* uniformBuffer : pimpl->uniformBufferWriteQueues[currentFrameIndex]) { VkBufferCopy copyRegion{}; copyRegion.srcOffset = 0; copyRegion.dstOffset = 0; - copyRegion.size = descriptorBuffer->stagingBuffer.size; + copyRegion.size = uniformBuffer->stagingBuffer.size; vkCmdCopyBuffer( frameData.transferBuf, - descriptorBuffer->stagingBuffer.buffer, - descriptorBuffer->gpuBuffers[currentFrameIndex].buffer, + uniformBuffer->stagingBuffer.buffer, + uniformBuffer->gpuBuffers[currentFrameIndex].buffer, 1, ©Region ); @@ -562,11 +576,11 @@ namespace engine { barrier.dstAccessMask = 0; barrier.srcQueueFamilyIndex = pimpl->device.queues.transferQueueFamily; barrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily; - barrier.buffer = descriptorBuffer->gpuBuffers[currentFrameIndex].buffer; + barrier.buffer = uniformBuffer->gpuBuffers[currentFrameIndex].buffer; barrier.offset = 0; - barrier.size = descriptorBuffer->gpuBuffers[currentFrameIndex].size; + barrier.size = uniformBuffer->gpuBuffers[currentFrameIndex].size; } - pimpl->descriptorBufferWriteQueues[currentFrameIndex].clear(); + pimpl->uniformBufferWriteQueues[currentFrameIndex].clear(); VkDependencyInfo dependencyInfo{}; dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; @@ -989,23 +1003,28 @@ namespace engine { delete pipeline; } - gfx::DescriptorSetLayout* GFXDevice::createDescriptorSetLayout() + gfx::DescriptorSetLayout* GFXDevice::createDescriptorSetLayout(const std::vector& bindings) { gfx::DescriptorSetLayout* out = new gfx::DescriptorSetLayout{}; - std::vector bindings{}; - auto& binding = bindings.emplace_back(); - binding.binding = 0; // This should be as low as possible to avoid wasting memory - binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - binding.descriptorCount = 1; // if > 1, accessible as an array in the shader - binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; // only accessible in vertex + std::vector vulkanBindings{}; + uint32_t i = 0; + for (const auto& binding : bindings) { + auto& vulkanBinding = vulkanBindings.emplace_back(); + vulkanBinding.binding = i; // This should be as low as possible to avoid wasting memory + vulkanBinding.descriptorType = converters::getDescriptorType(binding.descriptorType); + vulkanBinding.descriptorCount = 1; // if > 1, accessible as an array in the shader + vulkanBinding.stageFlags = converters::getShaderStageFlags(binding.stageFlags); + + ++i; + } VkDescriptorSetLayoutCreateInfo info{}; info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; info.pNext = nullptr; info.flags = 0; - info.bindingCount = (uint32_t)bindings.size(); - info.pBindings = bindings.data(); + info.bindingCount = (uint32_t)vulkanBindings.size(); + info.pBindings = vulkanBindings.data(); VKCHECK(vkCreateDescriptorSetLayout(pimpl->device.device, &info, nullptr, &out->layout)); return out; @@ -1039,7 +1058,7 @@ namespace engine { return set; } - void GFXDevice::updateDescriptor(const gfx::DescriptorSet* set, uint32_t binding, const gfx::DescriptorBuffer* buffer, size_t offset, size_t range) + void GFXDevice::updateDescriptorUniformBuffer(const gfx::DescriptorSet* set, uint32_t binding, const gfx::UniformBuffer* buffer, size_t offset, size_t range) { assert(pimpl->FRAMECOUNT == 0); @@ -1065,14 +1084,39 @@ namespace engine { } } - gfx::DescriptorBuffer* GFXDevice::createDescriptorBuffer(uint64_t size, const void* initialData) + void GFXDevice::updateDescriptorCombinedImageSampler(const gfx::DescriptorSet *set, uint32_t binding) + { + assert(pimpl->FRAMECOUNT == 0); + + VkDescriptorImageInfo imageInfo{}; + imageInfo.sampler = VK_NULL_HANDLE; + imageInfo.imageView = VK_NULL_HANDLE; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { + VkWriteDescriptorSet descriptorWrite{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = set->sets[i], + .dstBinding = binding, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &imageInfo, + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr + }; + vkUpdateDescriptorSets(pimpl->device.device, 1, &descriptorWrite, 0, nullptr); + } + } + + gfx::UniformBuffer* GFXDevice::createUniformBuffer(uint64_t size, const void* initialData) { - gfx::DescriptorBuffer* out = new gfx::DescriptorBuffer{}; + gfx::UniformBuffer* out = new gfx::UniformBuffer{}; /* first make staging buffer */ out->stagingBuffer.size = size; out->stagingBuffer.type = gfx::BufferType::UNIFORM; - out->stagingBuffer.hostVisible = true; { VkBufferCreateInfo stagingBufferInfo{}; stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -1098,7 +1142,6 @@ namespace engine { for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { out->gpuBuffers[i].size = out->stagingBuffer.size; out->gpuBuffers[i].type = gfx::BufferType::UNIFORM; - out->gpuBuffers[i].hostVisible = false; VkBufferCreateInfo gpuBufferInfo{}; gpuBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -1121,18 +1164,18 @@ namespace engine { } - void GFXDevice::destroyDescriptorBuffer(const gfx::DescriptorBuffer* descriptorBuffer) + void GFXDevice::destroyUniformBuffer(const gfx::UniformBuffer* uniformBuffer) { for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { - vmaDestroyBuffer(pimpl->allocator, descriptorBuffer->gpuBuffers[i].buffer, descriptorBuffer->gpuBuffers[i].allocation); + vmaDestroyBuffer(pimpl->allocator, uniformBuffer->gpuBuffers[i].buffer, uniformBuffer->gpuBuffers[i].allocation); } - vmaDestroyBuffer(pimpl->allocator, descriptorBuffer->stagingBuffer.buffer, descriptorBuffer->stagingBuffer.allocation); + vmaDestroyBuffer(pimpl->allocator, uniformBuffer->stagingBuffer.buffer, uniformBuffer->stagingBuffer.allocation); - delete descriptorBuffer; + delete uniformBuffer; } - void GFXDevice::writeDescriptorBuffer(gfx::DescriptorBuffer* buffer, uint64_t offset, uint64_t size, const void* data) + void GFXDevice::writeUniformBuffer(gfx::UniformBuffer* buffer, uint64_t offset, uint64_t size, const void* data) { assert(offset + size <= buffer->stagingBuffer.size); @@ -1145,7 +1188,7 @@ namespace engine { /* queue the writes to each gpu buffer */ // This is required as buffers cannot be updated if they are currently in use for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { - pimpl->descriptorBufferWriteQueues[i].insert(buffer); + pimpl->uniformBufferWriteQueues[i].insert(buffer); } } @@ -1157,7 +1200,6 @@ namespace engine { auto out = new gfx::Buffer{}; out->size = size; out->type = type; - out->hostVisible = false; VkBuffer stagingBuffer; VmaAllocation stagingAllocation; diff --git a/src/systems/render.cpp b/src/systems/render.cpp index 874f26b..dc10c14 100644 --- a/src/systems/render.cpp +++ b/src/systems/render.cpp @@ -48,13 +48,13 @@ namespace engine { RenderData::SetZeroBuffer setZeroBuffer{ .proj = projMatrix }; - m_gfx->writeDescriptorBuffer(renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer), &setZeroBuffer); + m_gfx->writeUniformBuffer(renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer), &setZeroBuffer); } RenderData::SetOneBuffer setOneBuffer{ .view = viewMatrix }; - m_gfx->writeDescriptorBuffer(renderData.setOneBuffer, 0, sizeof(RenderData::SetOneBuffer), &setOneBuffer); + m_gfx->writeUniformBuffer(renderData.setOneBuffer, 0, sizeof(RenderData::SetOneBuffer), &setOneBuffer); /* render all renderable entities */