diff --git a/config/Sources.cmake b/config/Sources.cmake index 4397e4978eb022d267571d185a1f122d053a5ea1..54bb3485ed975669668d987787975f019aa6358b 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 c7512346c9137b77c365e807b679b3950925f535..e3a42943be32ec734912eb98697d6cdb5ba18405 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 9e1f9708c6318aa6deb750097c414358ffde2c65..9a30935c42d5c539f32744f910ca47bf73ce9f82 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 0000000000000000000000000000000000000000..8f5b46a5f821a39a0f6bf4287841f7d9c720f6a9 --- /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 8f3b516d4b4451c513366fbd8469908bccde6a5f..f3b2b802d062a441dfb0c810154205effb7053a2 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 db9a592334044bfa5854aa3e06451bc8850cf748..70ef1633d9bacbcc52e6f1f78dabfb450089a79f 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 b488426d6e24a69d2267c0d1f4f3d847d5f331e9..f5095e42ef8c7bfdd1c12097d7ba062c0c8e5ad8 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 1492b1afa563543e6a9eef380295bcb71fef58b8..c230cfdcc9cc2503720eca402f63a9d6dda76b0b 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 c48b015335e00f23a892bb96d3e89a2c0877ae61..f8d94b734599cbf1f55aad7b590ab4796501d951 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 0000000000000000000000000000000000000000..a9d6a36fed7ecc3a9783a6f9674e3c894f81c049 --- /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 98a8dfcf3dc5aa06024546e204518a3c0fa56af4..384f72ff80b4fd7d14c152a11aa21a6ddf179f54 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 ecba7eb5959c1d78a0be41e0b3ac555bffd92d95..1d8ce207b645e30cee291816eac3c934ed40e92a 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 602f1d3e2a8100ebd9bbb83772312d3d659abe86..78bd5808b63fee7333243db4fca640047f76eae9 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 c34b0d3631c48561f42eb7f21ba5578156910f51..e50f800a482460cdb81687c76f8b1318598f689c 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 5ac2d9781a1bdfa36cd89f76b202b2028deb4d8d..b1b0827285ffce91b6be3bf20a58cdef7de233b2 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,