From 79570906bf6e7880ced85eb85e8d1a621ce39e1f Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Fri, 18 Jun 2021 17:35:24 +0200
Subject: [PATCH] [#55] Add mipmap generation

---
 include/vkcv/Core.hpp              |  5 +-
 include/vkcv/Image.hpp             |  1 +
 modules/gui/src/vkcv/gui/GUI.cpp   |  4 +-
 projects/first_mesh/src/main.cpp   |  4 +-
 projects/voxelization/src/main.cpp |  8 ++--
 src/vkcv/BufferManager.cpp         |  2 +-
 src/vkcv/Core.cpp                  | 14 ++----
 src/vkcv/Image.cpp                 |  4 ++
 src/vkcv/ImageManager.cpp          | 76 ++++++++++++++++++++++++++++--
 src/vkcv/ImageManager.hpp          |  1 +
 src/vkcv/SamplerManager.cpp        |  2 +-
 11 files changed, 95 insertions(+), 26 deletions(-)

diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 1771fba7..c7512346 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -41,7 +41,6 @@ namespace vkcv
 		QueueType queueType;
 		std::vector<vk::Semaphore> waitSemaphores;
 		std::vector<vk::Semaphore> signalSemaphores;
-		vk::Fence fence;
 	};
 
     class Core final
@@ -220,7 +219,7 @@ namespace vkcv
 			uint32_t    width,
 			uint32_t    height,
 			uint32_t    depth = 1,
-			bool        createMipChain = true,
+			bool        createMipChain = false,
 			bool        supportStorage = false,
 			bool        supportColorAttachment = false);
 
@@ -268,7 +267,7 @@ namespace vkcv
 		 * @param record Record-command-function
 		 * @param finish Finish-command-function or nullptr
 		 */
-		void recordAndSubmitCommands(
+		void recordAndSubmitCommandsImmediate(
 			const SubmitInfo            &submitInfo, 
 			const RecordCommandFunction &record, 
 			const FinishCommandFunction &finish);
diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp
index ef913306..98a0943b 100644
--- a/include/vkcv/Image.hpp
+++ b/include/vkcv/Image.hpp
@@ -36,6 +36,7 @@ namespace vkcv {
 		void switchLayout(vk::ImageLayout newLayout);
 		
 		void fill(void* data, size_t size = SIZE_MAX);
+		void generateMipChainImmediate();
 	private:
 		ImageManager* const m_manager;
 		const ImageHandle   m_handle;
diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp
index 6a08cb4f..a9a8bd01 100644
--- a/modules/gui/src/vkcv/gui/GUI.cpp
+++ b/modules/gui/src/vkcv/gui/GUI.cpp
@@ -141,7 +141,7 @@ namespace vkcv::gui {
 		
 		const SubmitInfo submitInfo { QueueType::Graphics, {}, {} };
 		
-		m_core.recordAndSubmitCommands(submitInfo, [](const vk::CommandBuffer& commandBuffer) {
+		m_core.recordAndSubmitCommandsImmediate(submitInfo, [](const vk::CommandBuffer& commandBuffer) {
 			ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);
 		}, []() {
 			ImGui_ImplVulkan_DestroyFontUploadObjects();
@@ -214,7 +214,7 @@ namespace vkcv::gui {
 		SubmitInfo submitInfo;
 		submitInfo.queueType = QueueType::Graphics;
 		
-		m_core.recordAndSubmitCommands(submitInfo, [&](const vk::CommandBuffer& commandBuffer) {
+		m_core.recordAndSubmitCommandsImmediate(submitInfo, [&](const vk::CommandBuffer& commandBuffer) {
 			const vk::Rect2D renderArea (
 					vk::Offset2D(0, 0),
 					extent
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index d9650f35..74e6de3f 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -123,6 +123,8 @@ int main(int argc, const char** argv) {
 	vkcv::asset::Texture &tex = mesh.textures[0];
 	vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h);
 	texture.fill(tex.data.data());
+	texture.generateMipChainImmediate();
+	texture.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
 
 	vkcv::SamplerHandle sampler = core.createSampler(
 		vkcv::SamplerFilterType::LINEAR,
@@ -142,7 +144,7 @@ int main(int argc, const char** argv) {
 
 	core.writeDescriptorSet(descriptorSet, setWrites);
 
-	vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle();
+	vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight, 1, false).getHandle();
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 88c55b43..e2c8e605 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, false);
+	const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution);
 
 	// light info buffer
 	struct LightInfo {
@@ -196,6 +196,8 @@ int main(int argc, const char** argv) {
 
 		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h));
 		sceneImages.back().fill(sceneTexture.data.data());
+		sceneImages.back().generateMipChainImmediate();
+        sceneImages.back().switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
 
 		vkcv::DescriptorWrites setWrites;
 		setWrites.sampledImageWrites = {
@@ -225,7 +227,7 @@ int main(int argc, const char** argv) {
 		return EXIT_FAILURE;
 	}
 
-	vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight, 1, false).getHandle();
+	vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle();
 	vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle();
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
@@ -323,7 +325,7 @@ int main(int argc, const char** argv) {
 		}
 
 		if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) {
-			depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight, 1, false).getHandle();
+			depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle();
 			colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle();
 
 			windowWidth = swapchainWidth;
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index 4df411c1..aec96411 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -159,7 +159,7 @@ namespace vkcv {
 		SubmitInfo submitInfo;
 		submitInfo.queueType = QueueType::Transfer;
 		
-		core->recordAndSubmitCommands(
+		core->recordAndSubmitCommandsImmediate(
 				submitInfo,
 				[&info, &mapped_size](const vk::CommandBuffer& commandBuffer) {
 					const vk::BufferCopy region (
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index a32be983..76e93975 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -375,7 +375,7 @@ namespace vkcv
 		}
 	}
 	
-	void Core::recordAndSubmitCommands(
+	void Core::recordAndSubmitCommandsImmediate(
 		const SubmitInfo &submitInfo, 
 		const RecordCommandFunction &record, 
 		const FinishCommandFunction &finish)
@@ -390,18 +390,12 @@ namespace vkcv
 		record(cmdBuffer);
 		cmdBuffer.end();
 		
-		vk::Fence waitFence;
-		
-		if (!submitInfo.fence) {
-			waitFence = createFence(device);
-		}
-		
+		vk::Fence waitFence = createFence(device);
+
 		submitCommandBufferToQueue(queue.handle, cmdBuffer, waitFence, submitInfo.waitSemaphores, submitInfo.signalSemaphores);
 		waitForFence(device, waitFence);
 		
-		if (!submitInfo.fence) {
-			device.destroyFence(waitFence);
-		}
+		device.destroyFence(waitFence);
 		
 		device.freeCommandBuffers(cmdPool, cmdBuffer);
 		
diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp
index 34feec44..dca6358b 100644
--- a/src/vkcv/Image.cpp
+++ b/src/vkcv/Image.cpp
@@ -60,6 +60,10 @@ namespace vkcv{
 	void Image::fill(void *data, size_t size) {
 		m_manager->fillImage(m_handle, data, size);
 	}
+
+	void Image::generateMipChainImmediate() {
+		m_manager->generateImageMipChainImmediate(m_handle);
+	}
 	
 	Image::Image(ImageManager* manager, const ImageHandle& handle) :
 		m_manager(manager),
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index 2064b912..bab4ba1f 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -99,7 +99,7 @@ namespace vkcv {
 		
 		vk::ImageCreateFlags createFlags;
 		vk::ImageUsageFlags imageUsageFlags = (
-				vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst
+				vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc
 		);
 		if (supportStorage) {
 			imageUsageFlags |= vk::ImageUsageFlagBits::eStorage;
@@ -202,7 +202,7 @@ namespace vkcv {
 				vk::ImageSubresourceRange(
 					aspectFlags,
 					mip,
-					1,
+					mipCount - mip,
 					0,
 					arrayLayers
 				)
@@ -293,7 +293,7 @@ namespace vkcv {
 		SubmitInfo submitInfo;
 		submitInfo.queueType = QueueType::Graphics;
 		
-		m_core->recordAndSubmitCommands(
+		m_core->recordAndSubmitCommandsImmediate(
 			submitInfo,
 			[transitionBarrier](const vk::CommandBuffer& commandBuffer) {
 			// TODO: precise PipelineStageFlagBits, will require a lot of context
@@ -396,7 +396,7 @@ namespace vkcv {
 		SubmitInfo submitInfo;
 		submitInfo.queueType = QueueType::Transfer;
 		
-		m_core->recordAndSubmitCommands(
+		m_core->recordAndSubmitCommandsImmediate(
 				submitInfo,
 				[&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) {
 					vk::ImageAspectFlags aspectFlags;
@@ -437,7 +437,73 @@ namespace vkcv {
 				}
 		);
 	}
-	
+
+	void ImageManager::generateImageMipChainImmediate(const ImageHandle& handle) {
+
+		const auto& device = m_core->getContext().getDevice();
+
+		SubmitInfo submitInfo;
+		submitInfo.queueType = QueueType::Transfer;
+
+		if (handle.isSwapchainImage()) {
+			vkcv_log(vkcv::LogLevel::ERROR, "You cannot generate a mip chain for the swapchain, what are you smoking?");
+			return;
+		}
+
+		const auto id = handle.getId();
+		if (id >= m_images.size()) {
+			vkcv_log(vkcv::LogLevel::ERROR, "Invalid image handle");
+			return;
+		}
+		auto& image = m_images[id];
+		switchImageLayoutImmediate(handle, vk::ImageLayout::eGeneral);
+
+		const auto record = [&image, this, handle](const vk::CommandBuffer cmdBuffer) {
+
+			vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ?
+				vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor;
+
+			uint32_t srcWidth   = image.m_width;
+			uint32_t srcHeight  = image.m_height;
+			uint32_t srcDepth   = image.m_depth;
+
+			auto half = [](uint32_t in) {
+				return std::max(in / 2, (uint32_t)1);
+			};
+
+			uint32_t dstWidth   = half(image.m_width);
+			uint32_t dstHeight  = half(image.m_height);
+			uint32_t dstDepth   = half(image.m_depth);
+
+			for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) {
+				uint32_t dstMip = srcMip + 1;
+				vk::ImageBlit region(
+					vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1),
+					{ vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) },
+					vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1),
+					{ vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) });
+
+				cmdBuffer.blitImage(
+					image.m_handle,
+					vk::ImageLayout::eGeneral,
+					image.m_handle,
+					vk::ImageLayout::eGeneral,
+					region,
+					vk::Filter::eLinear);
+
+				srcWidth    = dstWidth;
+				srcHeight   = dstHeight;
+				srcDepth    = dstDepth;
+
+				dstWidth    = half(dstWidth);
+				dstHeight   = half(dstHeight);
+				dstDepth    = half(dstDepth);
+			}
+		};
+
+		m_core->recordAndSubmitCommandsImmediate(submitInfo, record, nullptr);
+	}
+
 	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 f860470e..69ab7943 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -100,6 +100,7 @@ namespace vkcv {
 			vk::CommandBuffer cmdBuffer);
 
 		void fillImage(const ImageHandle& handle, void* data, size_t size);
+		void generateImageMipChainImmediate(const ImageHandle& handle);
 		
 		[[nodiscard]]
 		uint32_t getImageWidth(const ImageHandle& handle) const;
diff --git a/src/vkcv/SamplerManager.cpp b/src/vkcv/SamplerManager.cpp
index eb44356f..a6ebb95b 100644
--- a/src/vkcv/SamplerManager.cpp
+++ b/src/vkcv/SamplerManager.cpp
@@ -87,7 +87,7 @@ namespace vkcv {
 				false,
 				vk::CompareOp::eAlways,
 				0.0f,
-				1.0f,
+				16.0f,
 				vk::BorderColor::eIntOpaqueBlack,
 				false
 		);
-- 
GitLab