From 3e5639002ccdbe689c0cc702b740be735831e8ba Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Sat, 21 Aug 2021 18:04:15 +0200
Subject: [PATCH] [#106] Very basic indirect dispatch implementation

---
 include/vkcv/Buffer.hpp                       |  4 +-
 include/vkcv/BufferManager.hpp                |  2 +-
 include/vkcv/Core.hpp                         | 12 +++++-
 .../shaders/motionBlurIndirectArguments.comp  | 20 ++++++++++
 projects/indirect_dispatch/src/MotionBlur.cpp | 40 +++++++++++++++----
 projects/indirect_dispatch/src/MotionBlur.hpp |  3 ++
 src/vkcv/BufferManager.cpp                    |  6 ++-
 src/vkcv/Core.cpp                             | 36 +++++++++++++++++
 src/vkcv/ImageManager.cpp                     |  2 +-
 9 files changed, 110 insertions(+), 15 deletions(-)
 create mode 100644 projects/indirect_dispatch/resources/shaders/motionBlurIndirectArguments.comp

diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp
index ae935ba9..f5cd183d 100644
--- a/include/vkcv/Buffer.hpp
+++ b/include/vkcv/Buffer.hpp
@@ -76,8 +76,8 @@ namespace vkcv {
 		{}
 		
 		[[nodiscard]]
-		static Buffer<T> create(BufferManager* manager, BufferType type, size_t count, BufferMemoryType memoryType) {
-			return Buffer<T>(manager, manager->createBuffer(type, count * sizeof(T), memoryType), type, count, memoryType);
+		static Buffer<T> create(BufferManager* manager, BufferType type, size_t count, BufferMemoryType memoryType, bool supportIndirect) {
+			return Buffer<T>(manager, manager->createBuffer(type, count * sizeof(T), memoryType, supportIndirect), type, count, memoryType);
 		}
 		
 	};
diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp
index c7f32d9f..7bec33d8 100644
--- a/include/vkcv/BufferManager.hpp
+++ b/include/vkcv/BufferManager.hpp
@@ -70,7 +70,7 @@ namespace vkcv
 		 * @param memoryType Type of buffers memory
 		 * @return New buffer handle
 		 */
-		BufferHandle createBuffer(BufferType type, size_t size, BufferMemoryType memoryType);
+		BufferHandle createBuffer(BufferType type, size_t size, BufferMemoryType memoryType, bool supportIndirect);
 		
 		/**
 		 * Returns the Vulkan buffer handle of a buffer
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 5677dbf6..7b5c1d94 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -185,8 +185,8 @@ namespace vkcv
             * return Buffer-Object
             */
         template<typename T>
-        Buffer<T> createBuffer(vkcv::BufferType type, size_t count, BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL) {
-        	return Buffer<T>::create(m_BufferManager.get(), type, count, memoryType);
+        Buffer<T> createBuffer(vkcv::BufferType type, size_t count, BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL, bool supportIndirect = false) {
+        	return Buffer<T>::create(m_BufferManager.get(), type, count, memoryType, supportIndirect);
         }
         
         /**
@@ -271,6 +271,14 @@ namespace vkcv
 			const std::vector<DescriptorSetUsage> &descriptorSetUsages,
 			const PushConstants& pushConstants);
 
+		void recordComputeIndirectDispatchToCmdStream(
+			const CommandStreamHandle               cmdStream,
+			const PipelineHandle                    computePipeline,
+			const vkcv::BufferHandle                buffer,
+			const size_t                            bufferArgOffset,
+			const std::vector<DescriptorSetUsage>&  descriptorSetUsages,
+			const PushConstants&                    pushConstants);
+
 		/**
 		 * @brief end recording and present image
 		*/
diff --git a/projects/indirect_dispatch/resources/shaders/motionBlurIndirectArguments.comp b/projects/indirect_dispatch/resources/shaders/motionBlurIndirectArguments.comp
new file mode 100644
index 00000000..1d225cf8
--- /dev/null
+++ b/projects/indirect_dispatch/resources/shaders/motionBlurIndirectArguments.comp
@@ -0,0 +1,20 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+layout(set=0, binding=0) buffer indirectArgumentBuffer {
+    uint dispatchArgs[3];
+};
+
+layout( push_constant ) uniform constants{
+    uint width;
+    uint height;
+};
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+void main(){
+
+    dispatchArgs[0] = (width  + 7) / 8;
+    dispatchArgs[1] = (height + 7) / 8;
+    dispatchArgs[2] = 1;
+}
\ No newline at end of file
diff --git a/projects/indirect_dispatch/src/MotionBlur.cpp b/projects/indirect_dispatch/src/MotionBlur.cpp
index 49e8b288..9aae3f00 100644
--- a/projects/indirect_dispatch/src/MotionBlur.cpp
+++ b/projects/indirect_dispatch/src/MotionBlur.cpp
@@ -36,6 +36,16 @@ bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, con
 	if (!loadComputePass(*m_core, "resources/shaders/motionVectorVisualisation.comp", &m_motionVectorVisualisationPass))
 		return false;
 
+	if (!loadComputePass(*m_core, "resources/shaders/motionBlurIndirectArguments.comp", &m_indirectArgumentPass))
+		return false;
+
+	m_indirectArgumentBuffer = m_core->createBuffer<uint32_t>(vkcv::BufferType::STORAGE, 3, vkcv::BufferMemoryType::DEVICE_LOCAL, true).getHandle();
+
+	vkcv::DescriptorWrites indirectArgumentDescriptorWrites;
+	indirectArgumentDescriptorWrites.storageBufferWrites = 
+		{ vkcv::BufferDescriptorWrite(0, m_indirectArgumentBuffer) };
+	m_core->writeDescriptorSet(m_indirectArgumentPass.descriptorSet, indirectArgumentDescriptorWrites);
+
 	m_renderTargets = MotionBlurSetup::createRenderTargets(targetWidth, targetHeight, *m_core);
 
 	m_nearestSampler = m_core->createSampler(
@@ -63,6 +73,26 @@ vkcv::ImageHandle MotionBlur::render(
 
 	computeMotionTiles(cmdStream, motionBufferFullRes);
 
+	// write indirect dispatch argument buffer
+	struct IndirectArgumentConstants {
+		uint32_t width;
+		uint32_t height;
+	};
+	vkcv::PushConstants indirectArgumentPassPushConstants(sizeof(IndirectArgumentConstants));
+	IndirectArgumentConstants indirectArgumentConstants;
+	indirectArgumentConstants.width  = m_core->getImageWidth( m_renderTargets.outputColor);
+	indirectArgumentConstants.height = m_core->getImageHeight(m_renderTargets.outputColor);
+	indirectArgumentPassPushConstants.appendDrawcall(indirectArgumentConstants);
+
+	const uint32_t dispatchSizeOne[3] = { 1, 1, 1 };
+
+	m_core->recordComputeDispatchToCmdStream(
+		cmdStream,
+		m_indirectArgumentPass.pipeline,
+		dispatchSizeOne,
+		{ vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_indirectArgumentPass.descriptorSet).vulkanHandle) },
+		indirectArgumentPassPushConstants);
+
 	// usually this is the neighbourhood max, but other modes can be used for comparison/debugging
 	vkcv::ImageHandle inputMotionTiles;
 	if (motionVectorMode == eMotionVectorMode::FullResolution)
@@ -113,15 +143,11 @@ vkcv::ImageHandle MotionBlur::render(
 	m_core->prepareImageForSampling(cmdStream, depthBuffer);
 	m_core->prepareImageForSampling(cmdStream, inputMotionTiles);
 
-	const auto fullscreenDispatchSizes = computeFullscreenDispatchSize(
-		m_core->getImageWidth(m_renderTargets.outputColor),
-		m_core->getImageHeight(m_renderTargets.outputColor),
-		8);
-
-	m_core->recordComputeDispatchToCmdStream(
+	m_core->recordComputeIndirectDispatchToCmdStream(
 		cmdStream,
 		m_motionBlurPass.pipeline,
-		fullscreenDispatchSizes.data(),
+		m_indirectArgumentBuffer,
+		0,
 		{ vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_motionBlurPass.descriptorSet).vulkanHandle) },
 		motionBlurPushConstants);
 
diff --git a/projects/indirect_dispatch/src/MotionBlur.hpp b/projects/indirect_dispatch/src/MotionBlur.hpp
index b90cdfa4..7fede9d9 100644
--- a/projects/indirect_dispatch/src/MotionBlur.hpp
+++ b/projects/indirect_dispatch/src/MotionBlur.hpp
@@ -55,4 +55,7 @@ private:
 	ComputePassHandles m_motionVectorMaxPass;
 	ComputePassHandles m_motionVectorMaxNeighbourhoodPass;
 	ComputePassHandles m_motionVectorVisualisationPass;
+	ComputePassHandles m_indirectArgumentPass;
+
+	vkcv::BufferHandle m_indirectArgumentBuffer;
 };
\ No newline at end of file
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index cfa23329..3d955016 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -19,7 +19,7 @@ namespace vkcv {
 			return;
 		}
 		
-		m_stagingBuffer = createBuffer(BufferType::STAGING, 1024 * 1024, BufferMemoryType::HOST_VISIBLE);
+		m_stagingBuffer = createBuffer(BufferType::STAGING, 1024 * 1024, BufferMemoryType::HOST_VISIBLE, false);
 	}
 	
 	BufferManager::~BufferManager() noexcept {
@@ -28,7 +28,7 @@ namespace vkcv {
 		}
 	}
 	
-	BufferHandle BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) {
+	BufferHandle BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType, bool supportIndirect) {
 		vk::BufferCreateFlags createFlags;
 		vk::BufferUsageFlags usageFlags;
 		
@@ -56,6 +56,8 @@ namespace vkcv {
 		if (memoryType == BufferMemoryType::DEVICE_LOCAL) {
 			usageFlags |= vk::BufferUsageFlagBits::eTransferDst;
 		}
+		if (supportIndirect)
+			usageFlags |= vk::BufferUsageFlagBits::eIndirectBuffer;
 		
 		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 		
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index e8e172dd..92e2df4f 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -507,6 +507,42 @@ namespace vkcv
 		recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
 	}
 
+	void Core::recordComputeIndirectDispatchToCmdStream(
+		const CommandStreamHandle               cmdStream,
+		const PipelineHandle                    computePipeline,
+		const vkcv::BufferHandle                buffer,
+		const size_t                            bufferArgOffset,
+		const std::vector<DescriptorSetUsage>&  descriptorSetUsages,
+		const PushConstants&                    pushConstants) {
+
+		auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
+
+			const auto pipelineLayout = m_PipelineManager->getVkPipelineLayout(computePipeline);
+
+			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_PipelineManager->getVkPipeline(computePipeline));
+			for (const auto& usage : descriptorSetUsages) {
+				cmdBuffer.bindDescriptorSets(
+					vk::PipelineBindPoint::eCompute,
+					pipelineLayout,
+					usage.setLocation,
+					{ usage.vulkanHandle },
+					usage.dynamicOffsets
+				);
+			}
+			if (pushConstants.getSizePerDrawcall() > 0) {
+				cmdBuffer.pushConstants(
+					pipelineLayout,
+					vk::ShaderStageFlagBits::eCompute,
+					0,
+					pushConstants.getSizePerDrawcall(),
+					pushConstants.getData());
+			}
+			cmdBuffer.dispatchIndirect(m_BufferManager->getBuffer(buffer), bufferArgOffset);
+		};
+
+		recordCommandsToStream(cmdStream, submitFunction, nullptr);
+	}
+
 	void Core::endFrame() {
 		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index 1cb6ad3a..4ddd7f8c 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -387,7 +387,7 @@ namespace vkcv {
 		const size_t max_size = std::min(size, image_size);
 		
 		BufferHandle bufferHandle = m_bufferManager.createBuffer(
-				BufferType::STAGING, max_size, BufferMemoryType::HOST_VISIBLE
+				BufferType::STAGING, max_size, BufferMemoryType::HOST_VISIBLE, false
 		);
 		
 		m_bufferManager.fillBuffer(bufferHandle, data, max_size, 0);
-- 
GitLab