From ce1cfdee51959f781c257f4f6ea27ab41517e208 Mon Sep 17 00:00:00 2001 From: Alexander Gauggel <agauggel@uni-koblenz.de> Date: Wed, 23 Jun 2021 11:51:16 +0200 Subject: [PATCH] [#82] Implement MSAA --- config/Sources.cmake | 3 ++ include/vkcv/Core.hpp | 18 +++++----- include/vkcv/Image.hpp | 23 ++++++------ include/vkcv/ImageConfig.hpp | 8 +++++ include/vkcv/PassConfig.hpp | 4 ++- include/vkcv/PipelineConfig.hpp | 22 ++++++------ projects/voxelization/src/main.cpp | 39 ++++++++++++++++---- src/vkcv/Core.cpp | 25 ++++++++----- src/vkcv/Image.cpp | 7 ++-- src/vkcv/ImageConfig.cpp | 14 ++++++++ src/vkcv/ImageManager.cpp | 58 +++++++++++++++++++++++++----- src/vkcv/ImageManager.hpp | 19 +++++----- src/vkcv/PassConfig.cpp | 4 +-- src/vkcv/PassManager.cpp | 2 +- src/vkcv/PipelineManager.cpp | 2 +- 15 files changed, 178 insertions(+), 70 deletions(-) create mode 100644 include/vkcv/ImageConfig.hpp create mode 100644 src/vkcv/ImageConfig.cpp diff --git a/config/Sources.cmake b/config/Sources.cmake index 4397e497..54bb3485 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -80,4 +80,7 @@ set(vkcv_sources ${vkcv_source}/vkcv/CommandStreamManager.cpp ${vkcv_include}/vkcv/CommandRecordingFunctionTypes.hpp + + ${vkcv_include}/vkcv/ImageConfig.hpp + ${vkcv_source}/vkcv/ImageConfig.cpp ) diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index c7512346..e3a42943 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -215,13 +215,14 @@ namespace vkcv */ [[nodiscard]] Image createImage( - vk::Format format, - uint32_t width, - uint32_t height, - uint32_t depth = 1, - bool createMipChain = false, - bool supportStorage = false, - bool supportColorAttachment = false); + vk::Format format, + uint32_t width, + uint32_t height, + uint32_t depth = 1, + bool createMipChain = false, + bool supportStorage = false, + bool supportColorAttachment = false, + Multisampling multisampling = Multisampling::None); /** TODO: * @param setDescriptions @@ -285,7 +286,8 @@ namespace vkcv void prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image); void recordImageMemoryBarrier(const CommandStreamHandle cmdStream, const ImageHandle image); void recordBufferMemoryBarrier(const CommandStreamHandle cmdStream, const BufferHandle buffer); - + void resolveMSAAImage(CommandStreamHandle cmdStream, ImageHandle src, ImageHandle dst); + vk::ImageView getSwapchainImageView() const; }; diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp index 9e1f9708..9a30935c 100644 --- a/include/vkcv/Image.hpp +++ b/include/vkcv/Image.hpp @@ -7,12 +7,10 @@ #include "vulkan/vulkan.hpp" #include "Handles.hpp" +#include "vkcv/ImageConfig.hpp" namespace vkcv { - // forward declares - class ImageManager; - bool isDepthFormat(const vk::Format format); class Image { @@ -48,15 +46,16 @@ namespace vkcv { Image(ImageManager* manager, const ImageHandle& handle); static Image create( - ImageManager* manager, - vk::Format format, - uint32_t width, - uint32_t height, - uint32_t depth, - uint32_t mipCount, - bool supportStorage, - bool supportColorAttachment); - + ImageManager* manager, + vk::Format format, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t mipCount, + bool supportStorage, + bool supportColorAttachment, + Multisampling msaa); + }; } diff --git a/include/vkcv/ImageConfig.hpp b/include/vkcv/ImageConfig.hpp new file mode 100644 index 00000000..8f5b46a5 --- /dev/null +++ b/include/vkcv/ImageConfig.hpp @@ -0,0 +1,8 @@ +#pragma once +#include <vulkan/vulkan.hpp> + +namespace vkcv { + enum class Multisampling { None, MSAA2X, MSAA4X, MSAA8X }; + + vk::SampleCountFlagBits msaaToVkSampleCountFlag(Multisampling msaa); +} diff --git a/include/vkcv/PassConfig.hpp b/include/vkcv/PassConfig.hpp index 8f3b516d..f3b2b802 100644 --- a/include/vkcv/PassConfig.hpp +++ b/include/vkcv/PassConfig.hpp @@ -2,6 +2,7 @@ #include <vector> #include <vulkan/vulkan.hpp> +#include "ImageConfig.hpp" namespace vkcv { @@ -45,7 +46,8 @@ namespace vkcv struct PassConfig { - explicit PassConfig(std::vector<AttachmentDescription> attachments) noexcept; + explicit PassConfig(std::vector<AttachmentDescription> attachments, Multisampling msaa = Multisampling::None) noexcept; std::vector<AttachmentDescription> attachments{}; + Multisampling msaa; }; } \ No newline at end of file diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp index db9a5923..70ef1633 100644 --- a/include/vkcv/PipelineConfig.hpp +++ b/include/vkcv/PipelineConfig.hpp @@ -10,22 +10,24 @@ #include "Handles.hpp" #include "ShaderProgram.hpp" #include "VertexLayout.hpp" +#include "ImageConfig.hpp" namespace vkcv { enum class PrimitiveTopology{PointList, LineList, TriangleList }; struct PipelineConfig { - ShaderProgram m_ShaderProgram; - uint32_t m_Width; - uint32_t m_Height; - PassHandle m_PassHandle; - VertexLayout m_VertexLayout; - std::vector<vk::DescriptorSetLayout> m_DescriptorLayouts; - bool m_UseDynamicViewport; - bool m_UseConservativeRasterization = false; - PrimitiveTopology m_PrimitiveTopology = PrimitiveTopology::TriangleList; - bool m_EnableDepthClamping = false; + ShaderProgram m_ShaderProgram; + uint32_t m_Width; + uint32_t m_Height; + PassHandle m_PassHandle; + VertexLayout m_VertexLayout; + std::vector<vk::DescriptorSetLayout> m_DescriptorLayouts; + bool m_UseDynamicViewport; + bool m_UseConservativeRasterization = false; + PrimitiveTopology m_PrimitiveTopology = PrimitiveTopology::TriangleList; + bool m_EnableDepthClamping = false; + Multisampling m_multisampling = Multisampling::None; }; } \ No newline at end of file diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp index b488426d..f5095e42 100644 --- a/projects/voxelization/src/main.cpp +++ b/projects/voxelization/src/main.cpp @@ -16,6 +16,8 @@ int main(int argc, const char** argv) { uint32_t windowWidth = 1280; uint32_t windowHeight = 720; + const vkcv::Multisampling msaa = vkcv::Multisampling::MSAA4X; + const bool usingMsaa = msaa != vkcv::Multisampling::None; vkcv::Window window = vkcv::Window::create( applicationName, @@ -121,7 +123,7 @@ int main(int argc, const char** argv) { depthBufferFormat ); - vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment }); + vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment }, msaa); vkcv::PassHandle forwardPass = core.createPass(forwardPassDefinition); vkcv::shader::GLSLCompiler compiler; @@ -230,6 +232,8 @@ int main(int argc, const char** argv) { core.getDescriptorSet(perMeshDescriptorSets[0]).layout }, true }; + + forwardPipelineConfig.m_multisampling = msaa; vkcv::PipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig); @@ -238,8 +242,18 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } - vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle(); - vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); + vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight, 1, false, false, false, msaa).getHandle(); + + const bool colorBufferRequiresStorage = !usingMsaa; + vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, colorBufferRequiresStorage, true, msaa).getHandle(); + + vkcv::ImageHandle resolvedColorBuffer; + if (msaa != vkcv::Multisampling::None) { + resolvedColorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); + } + else { + resolvedColorBuffer = colorBuffer; + } const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); @@ -346,8 +360,15 @@ int main(int argc, const char** argv) { } if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { - depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle(); - colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); + depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight, 1, false, false, false, msaa).getHandle(); + colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, colorBufferRequiresStorage, true, msaa).getHandle(); + + if (usingMsaa) { + resolvedColorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); + } + else { + resolvedColorBuffer = colorBuffer; + } windowWidth = swapchainWidth; windowHeight = swapchainHeight; @@ -359,7 +380,7 @@ int main(int argc, const char** argv) { // update descriptor sets which use swapchain image vkcv::DescriptorWrites tonemappingDescriptorWrites; tonemappingDescriptorWrites.storageImageWrites = { - vkcv::StorageImageDescriptorWrite(0, colorBuffer), + vkcv::StorageImageDescriptorWrite(0, resolvedColorBuffer), vkcv::StorageImageDescriptorWrite(1, swapchainInput) }; core.writeDescriptorSet(tonemappingDescriptorSet, tonemappingDescriptorWrites); @@ -418,6 +439,10 @@ int main(int argc, const char** argv) { voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip); } + if (usingMsaa) { + core.resolveMSAAImage(cmdStream, colorBuffer, resolvedColorBuffer); + } + const uint32_t tonemappingLocalGroupSize = 8; const uint32_t tonemappingDispatchCount[3] = { static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(tonemappingLocalGroupSize))), @@ -426,7 +451,7 @@ int main(int argc, const char** argv) { }; core.prepareImageForStorage(cmdStream, swapchainInput); - core.prepareImageForStorage(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, resolvedColorBuffer); core.recordComputeDispatchToCmdStream( cmdStream, diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 1492b1af..c230cfdc 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -116,7 +116,6 @@ namespace vkcv return m_PipelineManager->createComputePipeline(shaderProgram, descriptorSetLayouts); } - PassHandle Core::createPass(const PassConfig &config) { return m_PassManager->createPass(config); @@ -437,13 +436,14 @@ namespace vkcv } Image Core::createImage( - vk::Format format, - uint32_t width, - uint32_t height, - uint32_t depth, - bool createMipChain, - bool supportStorage, - bool supportColorAttachment) + vk::Format format, + uint32_t width, + uint32_t height, + uint32_t depth, + bool createMipChain, + bool supportStorage, + bool supportColorAttachment, + Multisampling multisampling) { uint32_t mipCount = 1; @@ -459,7 +459,8 @@ namespace vkcv depth, mipCount, supportStorage, - supportColorAttachment); + supportColorAttachment, + multisampling); } DescriptorSetHandle Core::createDescriptorSet(const std::vector<DescriptorBinding>& bindings) @@ -539,6 +540,12 @@ namespace vkcv }, nullptr); } + void Core::resolveMSAAImage(CommandStreamHandle cmdStream, ImageHandle src, ImageHandle dst) { + recordCommandsToStream(cmdStream, [src, dst, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordMSAAResolve(cmdBuffer, src, dst); + }, nullptr); + } + vk::ImageView Core::getSwapchainImageView() const { return m_ImageManager->getVulkanImageView(vkcv::ImageHandle::createSwapchainImageHandle()); } diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp index c48b0153..f8d94b73 100644 --- a/src/vkcv/Image.cpp +++ b/src/vkcv/Image.cpp @@ -27,9 +27,12 @@ namespace vkcv{ uint32_t depth, uint32_t mipCount, bool supportStorage, - bool supportColorAttachment) + bool supportColorAttachment, + Multisampling msaa) { - return Image(manager, manager->createImage(width, height, depth, format, mipCount, supportStorage, supportColorAttachment)); + return Image( + manager, + manager->createImage(width, height, depth, format, mipCount, supportStorage, supportColorAttachment, msaa)); } vk::Format Image::getFormat() const { diff --git a/src/vkcv/ImageConfig.cpp b/src/vkcv/ImageConfig.cpp new file mode 100644 index 00000000..a9d6a36f --- /dev/null +++ b/src/vkcv/ImageConfig.cpp @@ -0,0 +1,14 @@ +#include <vkcv/ImageConfig.hpp> +#include <vkcv/Logger.hpp> + +namespace vkcv { + vk::SampleCountFlagBits msaaToVkSampleCountFlag(Multisampling msaa) { + switch (msaa) { + case Multisampling::None: return vk::SampleCountFlagBits::e1; + case Multisampling::MSAA2X: return vk::SampleCountFlagBits::e2; + case Multisampling::MSAA4X: return vk::SampleCountFlagBits::e4; + case Multisampling::MSAA8X: return vk::SampleCountFlagBits::e8; + default: vkcv_log(vkcv::LogLevel::ERROR, "Unknown Multisampling enum setting"); return vk::SampleCountFlagBits::e1; + } + } +} \ No newline at end of file diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp index 98a8dfcf..384f72ff 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -85,13 +85,14 @@ namespace vkcv { } ImageHandle ImageManager::createImage( - uint32_t width, - uint32_t height, - uint32_t depth, - vk::Format format, - uint32_t mipCount, - bool supportStorage, - bool supportColorAttachment) + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t mipCount, + bool supportStorage, + bool supportColorAttachment, + Multisampling msaa) { const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice(); @@ -147,7 +148,9 @@ namespace vkcv { physicalDevice.getImageFormatProperties(format, imageType, imageTiling, imageUsageFlags); const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers); - + + vk::SampleCountFlagBits sampleCountFlag = msaaToVkSampleCountFlag(msaa); + const vk::ImageCreateInfo imageCreateInfo( createFlags, imageType, @@ -155,7 +158,7 @@ namespace vkcv { vk::Extent3D(width, height, depth), mipCount, arrayLayers, - vk::SampleCountFlagBits::e1, + sampleCountFlag, imageTiling, imageUsageFlags, vk::SharingMode::eExclusive, @@ -523,6 +526,43 @@ namespace vkcv { m_core->recordCommandsToStream(cmdStream, record, nullptr); } + void ImageManager::recordMSAAResolve(vk::CommandBuffer cmdBuffer, ImageHandle src, ImageHandle dst) { + + const uint64_t srcId = src.getId(); + const uint64_t dstId = dst.getId(); + + const bool isSrcSwapchainImage = src.isSwapchainImage(); + const bool isDstSwapchainImage = dst.isSwapchainImage(); + + const bool isSrcHandleInvalid = srcId >= m_images.size() && !isSrcSwapchainImage; + const bool isDstHandleInvalid = dstId >= m_images.size() && !isDstSwapchainImage; + + if (isSrcHandleInvalid || isDstHandleInvalid) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return; + } + + auto& srcImage = isSrcSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[srcId]; + auto& dstImage = isDstSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[dstId]; + + vk::ImageResolve region( + vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), + vk::Offset3D(0, 0, 0), + vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), + vk::Offset3D(0, 0, 0), + vk::Extent3D(dstImage.m_width, dstImage.m_height, dstImage.m_depth)); + + recordImageLayoutTransition(src, vk::ImageLayout::eTransferSrcOptimal, cmdBuffer); + recordImageLayoutTransition(dst, vk::ImageLayout::eTransferDstOptimal, cmdBuffer); + + cmdBuffer.resolveImage( + srcImage.m_handle, + srcImage.m_layout, + dstImage.m_handle, + dstImage.m_layout, + region); + } + uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const { const uint64_t id = handle.getId(); const bool isSwapchainImage = handle.isSwapchainImage(); diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index ecba7eb5..1d8ce207 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -9,6 +9,7 @@ #include "vkcv/BufferManager.hpp" #include "vkcv/Handles.hpp" +#include "vkcv/ImageConfig.hpp" namespace vkcv { @@ -72,13 +73,14 @@ namespace vkcv { ImageManager& operator=(const ImageManager& other) = delete; ImageHandle createImage( - uint32_t width, - uint32_t height, - uint32_t depth, - vk::Format format, - uint32_t mipCount, - bool supportStorage, - bool supportColorAttachment); + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t mipCount, + bool supportStorage, + bool supportColorAttachment, + Multisampling msaa); ImageHandle createSwapchainImage(); @@ -104,7 +106,8 @@ namespace vkcv { void fillImage(const ImageHandle& handle, void* data, size_t size); void generateImageMipChainImmediate(const ImageHandle& handle); void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle& cmdStream, const ImageHandle& handle); - + void recordMSAAResolve(vk::CommandBuffer cmdBuffer, ImageHandle src, ImageHandle dst); + [[nodiscard]] uint32_t getImageWidth(const ImageHandle& handle) const; diff --git a/src/vkcv/PassConfig.cpp b/src/vkcv/PassConfig.cpp index 602f1d3e..78bd5808 100644 --- a/src/vkcv/PassConfig.cpp +++ b/src/vkcv/PassConfig.cpp @@ -13,7 +13,7 @@ namespace vkcv format(format) {}; - PassConfig::PassConfig(std::vector<AttachmentDescription> attachments) noexcept : - attachments{std::move(attachments)} + PassConfig::PassConfig(std::vector<AttachmentDescription> attachments, Multisampling msaa) noexcept : + attachments{std::move(attachments) }, msaa(msaa) {} } \ No newline at end of file diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp index c34b0d36..e50f800a 100644 --- a/src/vkcv/PassManager.cpp +++ b/src/vkcv/PassManager.cpp @@ -94,7 +94,7 @@ namespace vkcv vk::AttachmentDescription attachmentDesc( {}, format, - vk::SampleCountFlagBits::e1, + msaaToVkSampleCountFlag(config.msaa), getVKLoadOpFromAttachOp(config.attachments[i].load_operation), getVkStoreOpFromAttachOp(config.attachments[i].store_operation), vk::AttachmentLoadOp::eDontCare, diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 5ac2d978..b1b08272 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -169,7 +169,7 @@ namespace vkcv // multisample state vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo( {}, - vk::SampleCountFlagBits::e1, + msaaToVkSampleCountFlag(config.m_multisampling), false, 0.f, nullptr, -- GitLab