From 8c0c19c003b6747002a7337479d572c3a41fec51 Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Fri, 18 Jun 2021 16:30:09 +0200
Subject: [PATCH] [#55] Add ability to create image with mip chain

---
 include/vkcv/Core.hpp                      |   9 +-
 include/vkcv/Image.hpp                     |  10 +-
 projects/voxelization/src/Voxelization.cpp |   4 +-
 projects/voxelization/src/main.cpp         |  10 +-
 src/vkcv/Core.cpp                          |  25 ++++-
 src/vkcv/Image.cpp                         |  12 ++-
 src/vkcv/ImageLayoutTransitions.cpp        |   2 +-
 src/vkcv/ImageManager.cpp                  | 107 ++++++++++++---------
 src/vkcv/ImageManager.hpp                  |  47 +++++----
 9 files changed, 148 insertions(+), 78 deletions(-)

diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 6a428194..1771fba7 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -215,7 +215,14 @@ namespace vkcv
          * @return Image-Object
          */
         [[nodiscard]]
-        Image createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth = 1, bool supportStorage = false, bool supportColorAttachment = false);
+        Image createImage(
+			vk::Format  format,
+			uint32_t    width,
+			uint32_t    height,
+			uint32_t    depth = 1,
+			bool        createMipChain = true,
+			bool        supportStorage = false,
+			bool        supportColorAttachment = false);
 
         /** TODO:
          *   @param setDescriptions
diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp
index 1795b63e..ef913306 100644
--- a/include/vkcv/Image.hpp
+++ b/include/vkcv/Image.hpp
@@ -42,7 +42,15 @@ 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, bool supportStorage, bool supportColorAttachment);
+		static Image create(
+			ImageManager* manager,
+			vk::Format format,
+			uint32_t width,
+			uint32_t height,
+			uint32_t depth,
+			uint32_t mipCount,
+			bool supportStorage,
+			bool supportColorAttachment);
 		
 	};
 	
diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp
index a6717744..b04fab9a 100644
--- a/projects/voxelization/src/Voxelization.cpp
+++ b/projects/voxelization/src/Voxelization.cpp
@@ -65,8 +65,8 @@ const vk::Format voxelizationDummyFormat = vk::Format::eR8Unorm;
 Voxelization::Voxelization(vkcv::Core* corePtr, const Dependencies& dependencies) 
 	:
 	m_corePtr(corePtr), 
-	m_voxelImage(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true)),
-	m_dummyRenderTarget(m_corePtr->createImage(voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, true)),
+	m_voxelImage(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, false, true)),
+	m_dummyRenderTarget(m_corePtr->createImage(voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, false, true)),
 	m_voxelInfoBuffer(m_corePtr->createBuffer<VoxelizationInfo>(vkcv::BufferType::UNIFORM, 1)),
 	m_voxelBuffer(m_corePtr->createBuffer<VoxelBufferContent>(vkcv::BufferType::STORAGE, voxelCount)){
 
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 81d99f8a..88c55b43 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -151,7 +151,7 @@ int main(int argc, const char** argv) {
 	);
 	const vk::Format shadowMapFormat = vk::Format::eD16Unorm;
 	const uint32_t shadowMapResolution = 1024;
-	const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1);
+	const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, false);
 
 	// light info buffer
 	struct LightInfo {
@@ -225,8 +225,8 @@ 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, true, true).getHandle();
+	vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight, 1, false).getHandle();
+	vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle();
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
@@ -323,8 +323,8 @@ 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, true, true).getHandle();
+			depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight, 1, false).getHandle();
+			colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle();
 
 			windowWidth = swapchainWidth;
 			windowHeight = swapchainHeight;
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 4b718db1..a32be983 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -442,9 +442,30 @@ namespace vkcv
 		return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode);
 	}
 
-	Image Core::createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth, bool supportStorage, bool supportColorAttachment)
+	Image Core::createImage(
+		vk::Format  format,
+		uint32_t    width,
+		uint32_t    height,
+		uint32_t    depth,
+		bool        createMipChain,
+		bool        supportStorage,
+		bool        supportColorAttachment)
 	{
-    	return Image::create(m_ImageManager.get(), format, width, height, depth, supportStorage, supportColorAttachment);
+
+		uint32_t mipCount = 1;
+		if (createMipChain) {
+			mipCount = 1 + (uint32_t)std::floor(std::log2(std::max(width, std::max(height, depth))));
+		}
+
+		return Image::create(
+			m_ImageManager.get(), 
+			format,
+			width,
+			height,
+			depth,
+			mipCount,
+			supportStorage,
+			supportColorAttachment);
 	}
 
     DescriptorSetHandle Core::createDescriptorSet(const std::vector<DescriptorBinding>& bindings)
diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp
index 9f7fdadc..34feec44 100644
--- a/src/vkcv/Image.cpp
+++ b/src/vkcv/Image.cpp
@@ -19,9 +19,17 @@ namespace vkcv{
 		}
 	}
 
-	Image Image::create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth, bool supportStorage, bool supportColorAttachment)
+	Image Image::create(
+		ImageManager*   manager,
+		vk::Format      format,
+		uint32_t        width,
+		uint32_t        height,
+		uint32_t        depth,
+		uint32_t        mipCount,
+		bool            supportStorage,
+		bool            supportColorAttachment)
 	{
-		return Image(manager, manager->createImage(width, height, depth, format, supportStorage, supportColorAttachment));
+		return Image(manager, manager->createImage(width, height, depth, format, mipCount, supportStorage, supportColorAttachment));
 	}
 	
 	vk::Format Image::getFormat() const {
diff --git a/src/vkcv/ImageLayoutTransitions.cpp b/src/vkcv/ImageLayoutTransitions.cpp
index cb0f90a7..8d31c64c 100644
--- a/src/vkcv/ImageLayoutTransitions.cpp
+++ b/src/vkcv/ImageLayoutTransitions.cpp
@@ -15,7 +15,7 @@ namespace vkcv {
 		vk::ImageSubresourceRange imageSubresourceRange(
 			aspectFlags,
 			0,
-			image.m_levels,
+			image.m_viewPerMip.size(),
 			0,
 			image.m_layers
 		);
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index fff98dc5..2064b912 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -13,25 +13,23 @@
 namespace vkcv {
 
 	ImageManager::Image::Image(
-		vk::Image           handle,
-		vk::DeviceMemory    memory,
-		vk::ImageView       view,
-		uint32_t            width,
-		uint32_t            height,
-		uint32_t            depth,
-		vk::Format          format,
-		uint32_t            layers,
-		uint32_t            levels)
+		vk::Image                   handle,
+		vk::DeviceMemory            memory,
+		std::vector<vk::ImageView>  views,
+		uint32_t                    width,
+		uint32_t                    height,
+		uint32_t                    depth,
+		vk::Format                  format,
+		uint32_t                    layers)
 		:
 		m_handle(handle),
 		m_memory(memory),
-		m_view(view),
+        m_viewPerMip(views),
 		m_width(width),
 		m_height(height),
 		m_depth(depth),
 		m_format(format),
-		m_layers(layers),
-		m_levels(levels)
+		m_layers(layers)
 	{}
 
 	/**
@@ -69,8 +67,11 @@ namespace vkcv {
 		for (uint64_t id = 0; id < m_images.size(); id++) {
 			destroyImageById(id);
 		}
-		for (const auto swapchainImage : m_swapchainImages)
-			m_core->getContext().getDevice().destroy(swapchainImage.m_view);
+		for (const auto swapchainImage : m_swapchainImages) {
+			for (const auto view : swapchainImage.m_viewPerMip) {
+				m_core->getContext().getDevice().destroy(view);
+			}
+		}
 	}
 	
 	bool isDepthImageFormat(vk::Format format) {
@@ -83,7 +84,14 @@ namespace vkcv {
 		}
 	}
 
-	ImageHandle ImageManager::createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format, bool supportStorage, bool supportColorAttachment)
+	ImageHandle ImageManager::createImage(
+		uint32_t    width, 
+		uint32_t    height, 
+		uint32_t    depth, 
+		vk::Format  format, 
+		uint32_t    mipCount,
+		bool        supportStorage, 
+		bool        supportColorAttachment)
 	{
 		const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice();
 		
@@ -135,11 +143,9 @@ namespace vkcv {
 			imageTiling = vk::ImageTiling::eLinear;
 		}
 		
-		const vk::ImageFormatProperties imageFormatProperties = physicalDevice.getImageFormatProperties(
-				format, imageType, imageTiling, imageUsageFlags
-		);
+		const vk::ImageFormatProperties imageFormatProperties = 
+			physicalDevice.getImageFormatProperties(format, imageType, imageTiling, imageUsageFlags);
 		
-		const uint32_t mipLevels = std::min<uint32_t>(1, imageFormatProperties.maxMipLevels);
 		const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers);
 		
 		const vk::ImageCreateInfo imageCreateInfo(
@@ -147,7 +153,7 @@ namespace vkcv {
 			imageType,
 			format,
 			vk::Extent3D(width, height, depth),
-			mipLevels,
+			mipCount,
 			arrayLayers,
 			vk::SampleCountFlagBits::e1,
 			imageTiling,
@@ -180,30 +186,33 @@ namespace vkcv {
 			aspectFlags = vk::ImageAspectFlagBits::eColor;
 		}
 		
-		const vk::ImageViewCreateInfo imageViewCreateInfo (
+		std::vector<vk::ImageView> views;
+		for (int mip = 0; mip < mipCount; mip++) {
+			const vk::ImageViewCreateInfo imageViewCreateInfo(
 				{},
 				image,
 				imageViewType,
 				format,
 				vk::ComponentMapping(
-						vk::ComponentSwizzle::eIdentity,
-						vk::ComponentSwizzle::eIdentity,
-						vk::ComponentSwizzle::eIdentity,
-						vk::ComponentSwizzle::eIdentity
+					vk::ComponentSwizzle::eIdentity,
+					vk::ComponentSwizzle::eIdentity,
+					vk::ComponentSwizzle::eIdentity,
+					vk::ComponentSwizzle::eIdentity
 				),
 				vk::ImageSubresourceRange(
-						aspectFlags,
-						0,
-						mipLevels,
-						0,
-						arrayLayers
+					aspectFlags,
+					mip,
+					1,
+					0,
+					arrayLayers
 				)
-		);
-		
-		vk::ImageView view = device.createImageView(imageViewCreateInfo);
+			);
+
+			views.push_back(device.createImageView(imageViewCreateInfo));
+		}
 		
 		const uint64_t id = m_images.size();
-		m_images.push_back(Image(image, memory, view, width, height, depth, format, arrayLayers, mipLevels));
+		m_images.push_back(Image(image, memory, views, width, height, depth, format, arrayLayers));
 		return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); });
 	}
 	
@@ -246,10 +255,10 @@ namespace vkcv {
 		return image.m_memory;
 	}
 	
-	vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle) const {
+	vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, const size_t mipLevel) const {
 		
 		if (handle.isSwapchainImage()) {
-			return m_swapchainImages[m_currentSwapchainInputImage].m_view;
+			return m_swapchainImages[m_currentSwapchainInputImage].m_viewPerMip[0];
 		}
 
 		const uint64_t id = handle.getId();
@@ -258,7 +267,14 @@ namespace vkcv {
 			return nullptr;
 		}
 		
-		return m_images[id].m_view;
+		const auto& image = m_images[id];
+
+		if (mipLevel >= m_images.size()) {
+			vkcv_log(LogLevel::ERROR, "Image does not have requested mipLevel");
+			return nullptr;
+		}
+
+		return image.m_viewPerMip[mipLevel];
 	}
 	
 	void ImageManager::switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout) {
@@ -475,9 +491,11 @@ namespace vkcv {
 
 		const vk::Device& device = m_core->getContext().getDevice();
 		
-		if (image.m_view) {
-			device.destroyImageView(image.m_view);
-			image.m_view = nullptr;
+		for (auto& view : image.m_viewPerMip) {
+			if (view) {
+				device.destroyImageView(view);
+				view = nullptr;
+			}
 		}
 
 		if (image.m_memory) {
@@ -512,13 +530,16 @@ namespace vkcv {
 		uint32_t width, uint32_t height, vk::Format format) {
 
 		// destroy old views
-		for (auto image : m_swapchainImages)
-			m_core->getContext().getDevice().destroyImageView(image.m_view);
+		for (auto image : m_swapchainImages) {
+			for (const auto& view : image.m_viewPerMip) {
+				m_core->getContext().getDevice().destroyImageView(view);
+			}
+		}
 
 		assert(images.size() == views.size());
 		m_swapchainImages.clear();
 		for (int i = 0; i < images.size(); i++) {
-			m_swapchainImages.push_back(Image(images[i], nullptr, views[i], width, height, 1, format, 1, 1));
+			m_swapchainImages.push_back(Image(images[i], nullptr, { views[i] }, width, height, 1, format, 1));
 		}
 	}
 
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index 7bb4cc7e..f860470e 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -18,29 +18,27 @@ namespace vkcv {
 	public:
 		struct Image
 		{
-			vk::Image           m_handle;
-			vk::DeviceMemory    m_memory;
-			vk::ImageView       m_view;
-			uint32_t            m_width = 0;
-			uint32_t            m_height = 0;
-			uint32_t            m_depth = 0;
-			vk::Format          m_format;
-			uint32_t            m_layers = 1;
-			uint32_t            m_levels = 1;
-			vk::ImageLayout     m_layout = vk::ImageLayout::eUndefined;
+			vk::Image                   m_handle;
+			vk::DeviceMemory            m_memory;
+			std::vector<vk::ImageView>  m_viewPerMip;
+			uint32_t                    m_width     = 0;
+			uint32_t                    m_height    = 0;
+			uint32_t                    m_depth     = 0;
+			vk::Format                  m_format;
+			uint32_t                    m_layers    = 1;
+			vk::ImageLayout             m_layout    = vk::ImageLayout::eUndefined;
 		private:
 			// struct is public so utility functions can access members, but only ImageManager can create Image
 			friend ImageManager;
 			Image(
-				vk::Image           handle,
-				vk::DeviceMemory    memory,
-				vk::ImageView       view,
-				uint32_t            width,
-				uint32_t            height,
-				uint32_t            depth,
-				vk::Format          format,
-				uint32_t            layers,
-				uint32_t            levels);
+				vk::Image                   handle,
+				vk::DeviceMemory            memory,
+				std::vector<vk::ImageView>  views,
+				uint32_t                    width,
+				uint32_t                    height,
+				uint32_t                    depth,
+				vk::Format                  format,
+				uint32_t                    layers);
 
 			Image();
 		};
@@ -71,7 +69,14 @@ namespace vkcv {
 		ImageManager& operator=(ImageManager&& other) = delete;
 		ImageManager& operator=(const ImageManager& other) = delete;
 		
-		ImageHandle createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format, bool supportStorage, bool supportColorAttachment);
+		ImageHandle createImage(
+			uint32_t    width, 
+			uint32_t    height, 
+			uint32_t    depth, 
+			vk::Format  format, 
+			uint32_t    mipCount,
+			bool        supportStorage, 
+			bool        supportColorAttachment);
 		
 		ImageHandle createSwapchainImage();
 		
@@ -82,7 +87,7 @@ namespace vkcv {
 		vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle& handle) const;
 		
 		[[nodiscard]]
-		vk::ImageView getVulkanImageView(const ImageHandle& handle) const;
+		vk::ImageView getVulkanImageView(const ImageHandle& handle, const size_t mipLevel = 0) const;
 
 		void switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout);
 		void recordImageLayoutTransition(
-- 
GitLab