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