From c437f1c37ba865f8d991998fa6cab12a963d2616 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Fri, 15 Jul 2022 02:44:24 +0200
Subject: [PATCH] Abstracted dispatch size to reduce code from hell

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 config/Sources.cmake                          |  3 +
 include/vkcv/Core.hpp                         | 20 ++---
 include/vkcv/DispatchSize.hpp                 | 50 +++++++++++
 .../vkcv/algorithm/SinglePassDownsampler.cpp  |  9 +-
 .../src/vkcv/effects/BloomAndFlaresEffect.cpp | 34 ++++----
 modules/scene/src/vkcv/scene/Scene.cpp        | 83 ++++++++++++++++---
 .../src/vkcv/upscaling/FSRUpscaling.cpp       |  8 +-
 .../src/vkcv/upscaling/NISUpscaling.cpp       |  8 +-
 projects/bindless_textures/src/main.cpp       |  5 +-
 projects/first_mesh/src/main.cpp              |  5 +-
 projects/first_triangle/src/main.cpp          |  4 +-
 projects/indirect_dispatch/src/App.cpp        | 12 +--
 projects/indirect_dispatch/src/MotionBlur.cpp | 68 +++++++--------
 projects/indirect_draw/src/main.cpp           | 22 ++---
 projects/mesh_shader/src/main.cpp             |  9 +-
 projects/particle_simulation/src/main.cpp     | 25 +++---
 projects/path_tracer/src/main.cpp             | 18 ++--
 projects/saf_r/src/main.cpp                   | 11 ++-
 projects/sph/src/main.cpp                     | 62 ++++++++------
 projects/voxelization/src/ShadowMapping.cpp   | 21 +++--
 projects/voxelization/src/Voxelization.cpp    | 31 ++++---
 projects/voxelization/src/main.cpp            | 61 ++++++--------
 projects/wobble_bobble/src/main.cpp           | 11 +--
 src/vkcv/Core.cpp                             | 31 ++++---
 src/vkcv/DispatchSize.cpp                     | 44 ++++++++++
 25 files changed, 402 insertions(+), 253 deletions(-)
 create mode 100644 include/vkcv/DispatchSize.hpp
 create mode 100644 src/vkcv/DispatchSize.cpp

diff --git a/config/Sources.cmake b/config/Sources.cmake
index 9c86a9d5..85a87f5d 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -70,6 +70,9 @@ set(vkcv_sources
 
 		${vkcv_include}/vkcv/VertexLayout.hpp
 		${vkcv_source}/vkcv/VertexLayout.cpp
+		
+		${vkcv_include}/vkcv/DispatchSize.hpp
+		${vkcv_source}/vkcv/DispatchSize.cpp
 
 		${vkcv_include}/vkcv/Event.hpp
 		
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index e6529ce8..bf944a61 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -24,6 +24,7 @@
 #include "Event.hpp"
 #include "DrawcallRecording.hpp"
 #include "CommandRecordingFunctionTypes.hpp"
+#include "DispatchSize.hpp"
 
 #define VKCV_FRAMEWORK_NAME "VkCV"
 #define VKCV_FRAMEWORK_VERSION (VK_MAKE_VERSION(0, 1, 0))
@@ -501,7 +502,6 @@ namespace vkcv
 		 * @brief Records drawcalls to a command stream
 		 * 
 		 * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into
-		 * @param renderpassHandle Handle of the renderpass that is used for the drawcalls
 		 * @param pipelineHandle Handle of the pipeline that is used for the drawcalls
 		 * @param pushConstants Push constants that are used for the drawcalls, ignored if constant size is set to 0
 		 * @param drawcalls Information about each drawcall, consisting of mesh handle, descriptor set bindings and instance count
@@ -510,7 +510,6 @@ namespace vkcv
 		*/
 		void recordDrawcallsToCmdStream(
 			const CommandStreamHandle&      cmdStreamHandle,
-			const PassHandle&               renderpassHandle,
 			const GraphicsPipelineHandle    &pipelineHandle,
 			const PushConstants             &pushConstants,
 			const std::vector<DrawcallInfo> &drawcalls,
@@ -521,7 +520,6 @@ namespace vkcv
 		 * @brief Records indirect drawcalls to a command stream
 		 *
 		 * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into
-		 * @param renderpassHandle Handle of the renderpass that is used for the drawcalls
 		 * @param pipelineHandle Handle of the pipeline that is used for the drawcalls
 		 * @param pushConstantData Push constants that are used for the drawcalls, ignored if constant size is set to 0
 		 * @param compiledDescriptorSet TODO
@@ -534,7 +532,6 @@ namespace vkcv
 		*/
 		void recordIndexedIndirectDrawcallsToCmdStream(
 				const CommandStreamHandle                           cmdStreamHandle,
-				const PassHandle                                    renderpassHandle,
 				const GraphicsPipelineHandle                        &pipelineHandle,
 				const PushConstants                                 &pushConstantData,
                 const vkcv::DescriptorSetHandle                     &compiledDescriptorSet,
@@ -548,7 +545,6 @@ namespace vkcv
 		 * @brief Records mesh shader drawcalls to a command stream
 		 *
 		 * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into
-		 * @param renderpassHandle Handle of the renderpass that is used for the drawcalls
 		 * @param pipelineHandle Handle of the pipeline that is used for the drawcalls
 		 * @param pushConstantData Push constants that are used for the drawcalls, ignored if constant size is set to 0
 		 * @param drawcalls Information about each drawcall, consisting of descriptor set bindings and task shader dispatch count
@@ -557,7 +553,6 @@ namespace vkcv
 		*/
 		void recordMeshShaderDrawcalls(
 			const CommandStreamHandle&              cmdStreamHandle,
-			const PassHandle&                       renderpassHandle,
 			const GraphicsPipelineHandle            &pipelineHandle,
 			const PushConstants&                    pushConstantData,
             const std::vector<MeshShaderDrawcall>&  drawcalls,
@@ -596,16 +591,15 @@ namespace vkcv
 		 * 
 		 * @param cmdStream Handle of the command stream that the dispatch is recorded into
 		 * @param computePipeline Handle of the pipeline that is used for the dispatch
-		 * @param dispatchCount How many work groups are dispatched
+		 * @param dispatchSize How many work groups are dispatched
 		 * @param descriptorSetUsages Descriptor set bindings of the dispatch
 		 * @param pushConstants Push constant data for the dispatch
 		 */
-		void recordComputeDispatchToCmdStream(
-			CommandStreamHandle cmdStream,
-            ComputePipelineHandle computePipeline,
-			const uint32_t dispatchCount[3],
-			const std::vector<DescriptorSetUsage> &descriptorSetUsages,
-			const PushConstants& pushConstants);
+		void recordComputeDispatchToCmdStream(const CommandStreamHandle& cmdStream,
+											  const ComputePipelineHandle& computePipeline,
+											  const DispatchSize& dispatchSize,
+											  const std::vector<DescriptorSetUsage> &descriptorSetUsages,
+											  const PushConstants& pushConstants);
 		
 		/**
 		 * @brief Record the start of a debug label into a command stream. 
diff --git a/include/vkcv/DispatchSize.hpp b/include/vkcv/DispatchSize.hpp
new file mode 100644
index 00000000..10d0d602
--- /dev/null
+++ b/include/vkcv/DispatchSize.hpp
@@ -0,0 +1,50 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/DispatchSize.hpp
+ * @brief Class to handle dispatch sizes.
+ */
+ 
+#include <array>
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+	
+	class DispatchSize final {
+	private:
+		std::array<uint32_t, 3> m_Dispatch;
+		
+	public:
+		DispatchSize(uint32_t count);
+		
+		DispatchSize(uint32_t dimensionX, uint32_t dimentionY, uint32_t dimensionZ = 1);
+		
+		DispatchSize(const DispatchSize& other) = default;
+		DispatchSize(DispatchSize&& other) = default;
+		
+		~DispatchSize() = default;
+		
+		DispatchSize& operator=(const DispatchSize& other) = default;
+		DispatchSize& operator=(DispatchSize&& other) = default;
+		
+		[[nodiscard]]
+		const uint32_t* data() const;
+		
+		[[nodiscard]]
+		uint32_t operator[](size_t index) const;
+		
+		[[nodiscard]]
+		uint32_t x() const;
+		
+		[[nodiscard]]
+		uint32_t y() const;
+		
+		[[nodiscard]]
+		uint32_t z() const;
+		
+	};
+	
+	[[nodiscard]]
+	DispatchSize dispatchInvocations(DispatchSize globalInvocations, DispatchSize groupSize);
+	
+}
diff --git a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
index fe97d6b9..3a8408eb 100644
--- a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
+++ b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
@@ -307,10 +307,11 @@ namespace vkcv::algorithm {
 			m_core.prepareImageForStorage(cmdStream, image);
 		}
 		
-		uint32_t dispatch [3];
-		dispatch[0] = dispatchThreadGroupCountXY[0];
-		dispatch[1] = dispatchThreadGroupCountXY[1];
-		dispatch[2] = m_core.getImageArrayLayers(image);
+		vkcv::DispatchSize dispatch (
+				dispatchThreadGroupCountXY[0],
+				dispatchThreadGroupCountXY[1],
+				m_core.getImageArrayLayers(image)
+		);
 		
 		vkcv::PushConstants pushConstants = (m_sampler?
 				vkcv::pushConstants<SPDConstantsSampler>() :
diff --git a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
index d0872f19..e68fd54b 100644
--- a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
+++ b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
@@ -293,10 +293,10 @@ namespace vkcv::effects {
 			
 			static const uint32_t threadGroupWorkRegionDim = 8;
 			
-			uint32_t dispatch[3];
-			dispatch[0] = calcDispatchSize(downsampleSizeX, threadGroupWorkRegionDim);
-			dispatch[1] = calcDispatchSize(downsampleSizeY, threadGroupWorkRegionDim);
-			dispatch[2] = 1;
+			DispatchSize dispatch (
+					calcDispatchSize(downsampleSizeX, threadGroupWorkRegionDim),
+					calcDispatchSize(downsampleSizeY, threadGroupWorkRegionDim)
+			);
 			
 			// mip blur dispatch
 			m_core.recordComputeDispatchToCmdStream(
@@ -342,10 +342,10 @@ namespace vkcv::effects {
 			
 			static const uint32_t threadGroupWorkRegionDim = 8;
 			
-			uint32_t dispatch[3];
-			dispatch[0] = calcDispatchSize(upsampleSizeX, threadGroupWorkRegionDim);
-			dispatch[1] = calcDispatchSize(upsampleSizeY, threadGroupWorkRegionDim);
-			dispatch[2] = 1;
+			DispatchSize dispatch (
+					calcDispatchSize(upsampleSizeX, threadGroupWorkRegionDim),
+					calcDispatchSize(upsampleSizeY, threadGroupWorkRegionDim)
+			);
 			
 			m_core.recordComputeDispatchToCmdStream(
 					cmdStream,
@@ -385,13 +385,13 @@ namespace vkcv::effects {
 			mipDivisor *= 2.0f;
 		}
 		
-		static const uint32_t threadGroupWorkRegionDim = 8.0f;
+		static const uint32_t threadGroupWorkRegionDim = 8;
 		
 		// lens feature generation dispatch
-		uint32_t dispatch[3];
-		dispatch[0] = calcDispatchSize(sampleSizeX / mipDivisor, threadGroupWorkRegionDim);
-		dispatch[1] = calcDispatchSize(sampleSizeY / mipDivisor, threadGroupWorkRegionDim);
-		dispatch[2] = 1;
+		DispatchSize dispatch (
+				calcDispatchSize(sampleSizeX / mipDivisor, threadGroupWorkRegionDim),
+				calcDispatchSize(sampleSizeY / mipDivisor, threadGroupWorkRegionDim)
+		);
 		
 		m_core.recordComputeDispatchToCmdStream(
 				cmdStream,
@@ -451,10 +451,10 @@ namespace vkcv::effects {
 		
 		static const uint32_t threadGroupWorkRegionDim = 8;
 		
-		uint32_t dispatch[3];
-		dispatch[0] = calcDispatchSize(sampleWidth, threadGroupWorkRegionDim);
-		dispatch[1] = calcDispatchSize(sampleHeight, threadGroupWorkRegionDim);
-		dispatch[2] = 1;
+		DispatchSize dispatch (
+				calcDispatchSize(sampleWidth, threadGroupWorkRegionDim),
+				calcDispatchSize(sampleHeight, threadGroupWorkRegionDim)
+		);
 		
 		PushConstants pushConstants = vkcv::pushConstants<glm::vec3>();
 		pushConstants.appendDrawcall(m_cameraDirection);
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index 9a8ded0f..a2334474 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -138,7 +138,6 @@ namespace vkcv::scene {
 		
 		m_core->recordDrawcallsToCmdStream(
 				cmdStream,
-				pass,
 				pipeline,
 				pushConstants,
 				drawcalls,
@@ -157,26 +156,88 @@ namespace vkcv::scene {
 						  const asset::Texture& asset_texture,
 						  const vk::Format& format,
 						  ImageHandle& image, SamplerHandle& sampler) {
-		asset::Sampler* asset_sampler = nullptr;
+		const asset::Sampler* asset_sampler = nullptr;
 		
 		if ((asset_texture.sampler >= 0) && (asset_texture.sampler < asset_scene.samplers.size())) {
-			//asset_sampler = &(asset_scene.samplers[asset_texture.sampler]); // TODO
+			asset_sampler = &(asset_scene.samplers[asset_texture.sampler]);
 		}
 		
 		Image img = core.createImage(format, asset_texture.w, asset_texture.h, 1, true);
 		img.fill(asset_texture.data.data());
 		image = img.getHandle();
 		
+		SamplerFilterType magFilter = SamplerFilterType::LINEAR;
+		SamplerFilterType minFilter = SamplerFilterType::LINEAR;
+		SamplerMipmapMode mipmapMode = SamplerMipmapMode::LINEAR;
+		SamplerAddressMode addressMode = SamplerAddressMode::REPEAT;
+		
+		float mipLodBias = 0.0f;
+		
 		if (asset_sampler) {
-			//sampler = core.createSampler(asset_sampler) // TODO
-		} else {
-			sampler = core.createSampler(
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerMipmapMode::LINEAR,
-					vkcv::SamplerAddressMode::REPEAT
-			);
+			switch (asset_sampler->magFilter) {
+				case VK_FILTER_NEAREST:
+					magFilter = SamplerFilterType::NEAREST;
+					break;
+				case VK_FILTER_LINEAR:
+					magFilter = SamplerFilterType::LINEAR;
+					break;
+				default:
+					break;
+			}
+			
+			switch (asset_sampler->minFilter) {
+				case VK_FILTER_NEAREST:
+					minFilter = SamplerFilterType::NEAREST;
+					break;
+				case VK_FILTER_LINEAR:
+					minFilter = SamplerFilterType::LINEAR;
+					break;
+				default:
+					break;
+			}
+			
+			switch (asset_sampler->mipmapMode) {
+				case VK_SAMPLER_MIPMAP_MODE_NEAREST:
+					mipmapMode = SamplerMipmapMode::NEAREST;
+					break;
+				case VK_SAMPLER_MIPMAP_MODE_LINEAR:
+					mipmapMode = SamplerMipmapMode::LINEAR;
+					break;
+				default:
+					break;
+			}
+			
+			switch (asset_sampler->addressModeU) {
+				case VK_SAMPLER_ADDRESS_MODE_REPEAT:
+					addressMode = SamplerAddressMode::REPEAT;
+					break;
+				case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
+					addressMode = SamplerAddressMode::MIRRORED_REPEAT;
+					break;
+				case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
+					addressMode = SamplerAddressMode::CLAMP_TO_EDGE;
+					break;
+				case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
+					addressMode = SamplerAddressMode::MIRROR_CLAMP_TO_EDGE;
+					break;
+				case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
+					addressMode = SamplerAddressMode::CLAMP_TO_BORDER;
+					break;
+				default:
+					break;
+			}
+			
+			mipLodBias = asset_sampler->minLOD;
 		}
+		
+		sampler = core.createSampler(
+				magFilter,
+				minFilter,
+				mipmapMode,
+				addressMode,
+				
+				mipLodBias
+		);
 	}
 	
 	void Scene::loadMaterial(size_t index, const asset::Scene& scene,
diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
index a0858c17..aaa09325 100644
--- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
@@ -306,10 +306,10 @@ namespace vkcv::upscaling {
 		
 		static const uint32_t threadGroupWorkRegionDim = 16;
 		
-		uint32_t dispatch[3];
-		dispatch[0] = (outputWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
-		dispatch[1] = (outputHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
-		dispatch[2] = 1;
+		DispatchSize dispatch = dispatchInvocations(
+				DispatchSize(outputWidth, outputHeight),
+				DispatchSize(threadGroupWorkRegionDim, threadGroupWorkRegionDim)
+		);
 		
 		m_core.recordBufferMemoryBarrier(cmdStream, m_easuConstants.getHandle());
 		
diff --git a/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
index 21ad9652..906b1e3c 100644
--- a/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
@@ -235,10 +235,10 @@ namespace vkcv::upscaling {
 				sizeof(config)
 		);
 		
-		uint32_t dispatch[3];
-		dispatch[0] = (outputWidth + (m_blockWidth - 1)) / m_blockWidth;
-		dispatch[1] = (outputHeight + (m_blockHeight - 1)) / m_blockHeight;
-		dispatch[2] = 1;
+		DispatchSize dispatch = dispatchInvocations(
+				DispatchSize(outputWidth, outputHeight),
+				DispatchSize(m_blockWidth, m_blockHeight)
+		);
 		
 		m_core.recordBufferMemoryBarrier(cmdStream, m_scalerConstants.getHandle());
 		
diff --git a/projects/bindless_textures/src/main.cpp b/projects/bindless_textures/src/main.cpp
index e2f4c4ac..bec0ca45 100644
--- a/projects/bindless_textures/src/main.cpp
+++ b/projects/bindless_textures/src/main.cpp
@@ -266,12 +266,13 @@ int main(int argc, const char** argv) {
 
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			firstMeshPass,
 			firstMeshPipeline,
 			pushConstants,
 			{ drawcall },
 			renderTargets,
-			windowHandle);
+			windowHandle
+		);
+		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
 		core.endFrame(windowHandle);
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 1220b36e..c68238d4 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -206,12 +206,13 @@ int main(int argc, const char** argv) {
 
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			firstMeshPass,
 			firstMeshPipeline,
 			pushConstants,
 			{ drawcall },
 			renderTargets,
-			windowHandle);
+			windowHandle
+		);
+		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
 		core.endFrame(windowHandle);
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index bfbc9909..4723d17e 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -106,12 +106,12 @@ int main(int argc, const char** argv) {
 
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			trianglePass,
 			trianglePipeline,
 			vkcv::pushConstants<glm::mat4>(mvp),
 			{ drawcall },
 			{ swapchainInput },
-			windowHandle);
+			windowHandle
+		);
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp
index e327ba82..44eb74f8 100644
--- a/projects/indirect_dispatch/src/App.cpp
+++ b/projects/indirect_dispatch/src/App.cpp
@@ -223,7 +223,6 @@ void App::run() {
 
 		m_core.recordDrawcallsToCmdStream(
 			cmdStream,
-			m_prePass.renderPass,
 			m_prePass.pipeline,
 			prepassPushConstants,
 			prepassSceneDrawcalls,
@@ -239,7 +238,6 @@ void App::run() {
 
 		m_core.recordDrawcallsToCmdStream(
 			cmdStream,
-			m_skyPrePass.renderPass,
 			m_skyPrePass.pipeline,
 			skyPrepassPushConstants,
 			{ skyDrawcall },
@@ -266,7 +264,6 @@ void App::run() {
 
 		m_core.recordDrawcallsToCmdStream(
 			cmdStream,
-			m_meshPass.renderPass,
 			m_meshPass.pipeline,
 			meshPushConstants,
 			forwardSceneDrawcalls,
@@ -279,7 +276,6 @@ void App::run() {
 
 		m_core.recordDrawcallsToCmdStream(
 			cmdStream,
-			m_skyPass.renderPass,
 			m_skyPass.pipeline,
 			skyPushConstants,
 			{ skyDrawcall },
@@ -326,10 +322,10 @@ void App::run() {
 		m_core.prepareImageForSampling(cmdStream, motionBlurOutput);
 		m_core.prepareImageForStorage (cmdStream, swapchainInput);
 
-		const uint32_t fullScreenImageDispatch[3] = {
-			static_cast<uint32_t>((m_windowWidth  + 7) / 8),
-			static_cast<uint32_t>((m_windowHeight + 7) / 8),
-			static_cast<uint32_t>(1) };
+		const auto fullScreenImageDispatch = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(m_windowWidth, m_windowHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 
 		m_core.recordComputeDispatchToCmdStream(
 			cmdStream,
diff --git a/projects/indirect_dispatch/src/MotionBlur.cpp b/projects/indirect_dispatch/src/MotionBlur.cpp
index df5399ef..adffd1ea 100644
--- a/projects/indirect_dispatch/src/MotionBlur.cpp
+++ b/projects/indirect_dispatch/src/MotionBlur.cpp
@@ -4,18 +4,6 @@
 #include <array>
 #include <vkcv/Buffer.hpp>
 
-std::array<uint32_t, 3> computeFullscreenDispatchSize(
-	const uint32_t imageWidth,
-	const uint32_t imageHeight,
-	const uint32_t localGroupSize) {
-
-	// optimized divide and ceil
-	return std::array<uint32_t, 3>{
-		static_cast<uint32_t>(imageWidth  + (localGroupSize - 1)) / localGroupSize,
-		static_cast<uint32_t>(imageHeight + (localGroupSize - 1)) / localGroupSize,
-		static_cast<uint32_t>(1) };
-}
-
 bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, const uint32_t targetHeight) {
 
 	if (!corePtr) {
@@ -116,15 +104,13 @@ vkcv::ImageHandle MotionBlur::render(
 
 	computeMotionTiles(cmdStream, motionBufferFullRes);
 
-	// work tile reset
-	const uint32_t dispatchSizeOne[3] = { 1, 1, 1 };
-
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_tileResetPass.pipeline,
-		dispatchSizeOne,
+		1,
 		{ vkcv::DescriptorSetUsage(0, m_tileResetPass.descriptorSet) },
-		vkcv::PushConstants(0));
+		vkcv::PushConstants(0)
+	);
 
 	m_core->recordBufferMemoryBarrier(cmdStream, m_fullPathWorkTileBuffer);
 	m_core->recordBufferMemoryBarrier(cmdStream, m_copyPathWorkTileBuffer);
@@ -149,10 +135,13 @@ vkcv::ImageHandle MotionBlur::render(
 
 	m_core->writeDescriptorSet(m_tileClassificationPass.descriptorSet, tileClassificationDescriptorWrites);
 
-	const auto tileClassificationDispatch = computeFullscreenDispatchSize(
-		m_core->getImageWidth(m_renderTargets.motionMaxNeighbourhood), 
-		m_core->getImageHeight(m_renderTargets.motionMaxNeighbourhood),
-		8);
+	const auto tileClassificationDispatch = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(
+					m_core->getImageWidth(m_renderTargets.motionMaxNeighbourhood),
+					m_core->getImageHeight(m_renderTargets.motionMaxNeighbourhood)
+			),
+			vkcv::DispatchSize(8, 8)
+	);
 
 	struct ClassificationConstants {
 		uint32_t    width;
@@ -173,7 +162,7 @@ vkcv::ImageHandle MotionBlur::render(
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_tileClassificationPass.pipeline,
-		tileClassificationDispatch.data(),
+		tileClassificationDispatch,
 		{ vkcv::DescriptorSetUsage(0, m_tileClassificationPass.descriptorSet) },
 		classificationPushConstants);
 
@@ -302,15 +291,10 @@ vkcv::ImageHandle MotionBlur::render(
 			(m_core->getImageWidth(m_renderTargets.outputColor)  + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize * 
 			(m_core->getImageHeight(m_renderTargets.outputColor) + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize;
 
-		const uint32_t dispatchCounts[3] = {
-			tileCount,
-			1,
-			1 };
-
 		m_core->recordComputeDispatchToCmdStream(
 			cmdStream,
 			m_tileVisualisationPass.pipeline,
-			dispatchCounts,
+			tileCount,
 			{ vkcv::DescriptorSetUsage(0, m_tileVisualisationPass.descriptorSet) },
 			vkcv::PushConstants(0));
 	}
@@ -365,15 +349,18 @@ vkcv::ImageHandle MotionBlur::renderMotionVectorVisualisation(
 	vkcv::PushConstants motionVectorVisualisationPushConstants = vkcv::pushConstants<float>();
 	motionVectorVisualisationPushConstants.appendDrawcall(velocityRange);
 
-	const auto dispatchSizes = computeFullscreenDispatchSize(
-		m_core->getImageWidth(m_renderTargets.outputColor), 
-		m_core->getImageHeight(m_renderTargets.outputColor), 
-		8);
+	const auto dispatchSizes = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(
+					m_core->getImageWidth(m_renderTargets.outputColor),
+					m_core->getImageHeight(m_renderTargets.outputColor)
+			),
+			vkcv::DispatchSize(8, 8)
+	);
 
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_motionVectorVisualisationPass.pipeline,
-		dispatchSizes.data(),
+		dispatchSizes,
 		{ vkcv::DescriptorSetUsage(0, m_motionVectorVisualisationPass.descriptorSet) },
 		motionVectorVisualisationPushConstants);
 
@@ -400,15 +387,18 @@ void MotionBlur::computeMotionTiles(
 	m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMax);
 	m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMin);
 
-	const std::array<uint32_t, 3> motionTileDispatchCounts = computeFullscreenDispatchSize(
-		m_core->getImageWidth( m_renderTargets.motionMax),
-		m_core->getImageHeight(m_renderTargets.motionMax),
-		8);
+	const auto motionTileDispatchCounts = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(
+					m_core->getImageWidth( m_renderTargets.motionMax),
+					m_core->getImageHeight(m_renderTargets.motionMax)
+			),
+			vkcv::DispatchSize(8, 8)
+	);
 
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_motionVectorMinMaxPass.pipeline,
-		motionTileDispatchCounts.data(),
+		motionTileDispatchCounts,
 		{ vkcv::DescriptorSetUsage(0, m_motionVectorMinMaxPass.descriptorSet) },
 		vkcv::PushConstants(0));
 
@@ -438,7 +428,7 @@ void MotionBlur::computeMotionTiles(
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_motionVectorMinMaxNeighbourhoodPass.pipeline,
-		motionTileDispatchCounts.data(),
+		motionTileDispatchCounts,
 		{ vkcv::DescriptorSetUsage(0, m_motionVectorMinMaxNeighbourhoodPass.descriptorSet) },
 		vkcv::PushConstants(0));
 }
\ No newline at end of file
diff --git a/projects/indirect_draw/src/main.cpp b/projects/indirect_draw/src/main.cpp
index cba4be9a..ce4bada1 100644
--- a/projects/indirect_draw/src/main.cpp
+++ b/projects/indirect_draw/src/main.cpp
@@ -522,8 +522,7 @@ int main(int argc, const char** argv) {
 
     float ceiledDispatchCount = static_cast<float>(indexedIndirectCommands.size()) / 64.0f;
     ceiledDispatchCount = std::ceil(ceiledDispatchCount);
-    const uint32_t dispatchCount[3] = {static_cast<uint32_t>(ceiledDispatchCount), 1, 1};
-
+    const vkcv::DispatchSize dispatchCount = static_cast<uint32_t>(ceiledDispatchCount);
 
     vkcv::DescriptorSetUsage cullingUsage(0, cullingDescSet, {});
     vkcv::PushConstants emptyPushConstant(0);
@@ -556,8 +555,7 @@ int main(int argc, const char** argv) {
 		vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>();
 		pushConstants.appendDrawcall(cam.getProjection() * cam.getView());
 
-        if(updateFrustumPlanes)
-        {
+        if (updateFrustumPlanes) {
             const CameraPlanes cameraPlanes = computeCameraPlanes(cam);
             cameraPlaneBuffer.fill({ cameraPlanes });
         }
@@ -565,17 +563,18 @@ int main(int argc, const char** argv) {
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              cullingPipelineHandle,
-                                              dispatchCount,
-                                              {cullingUsage},
-                                              emptyPushConstant);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				cullingPipelineHandle,
+				dispatchCount,
+				{cullingUsage},
+				emptyPushConstant
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, indirectBuffer.getHandle());
 
 		core.recordIndexedIndirectDrawcallsToCmdStream(
 			cmdStream,
-			passHandle,
             sponzaPipelineHandle,
             pushConstants,
             descriptorSet,
@@ -583,7 +582,8 @@ int main(int argc, const char** argv) {
 			renderTargets,
 			indirectBuffer.getHandle(),
             indexedIndirectCommands.size(),
-			windowHandle);
+			windowHandle
+		);
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index f369e42d..34a9de9e 100644
--- a/projects/mesh_shader/src/main.cpp
+++ b/projects/mesh_shader/src/main.cpp
@@ -366,12 +366,12 @@ int main(int argc, const char** argv) {
 
 			core.recordMeshShaderDrawcalls(
 				cmdStream,
-				renderPass,
 				meshShaderPipeline,
 				pushConstantData,
 				{ vkcv::MeshShaderDrawcall({descriptorUsage}, taskCount)},
 				{ renderTargets },
-				windowHandle);
+				windowHandle
+			);
 		}
 		else {
 
@@ -379,12 +379,12 @@ int main(int argc, const char** argv) {
 
 			core.recordDrawcallsToCmdStream(
 				cmdStream,
-				renderPass,
 				bunnyPipeline,
 				pushConstantData,
 				{ vkcv::DrawcallInfo(renderMesh, { descriptorUsage }) },
 				{ renderTargets },
-				windowHandle);
+				windowHandle
+			);
 		}
 
 		core.prepareSwapchainImageForPresent(cmdStream);
@@ -397,7 +397,6 @@ int main(int argc, const char** argv) {
 		ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes);
 
 		ImGui::End();
-		
 		gui.endGUI();
 
 		core.endFrame(windowHandle);
diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp
index 764f3c06..c87afc98 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -280,12 +280,13 @@ int main(int argc, const char **argv) {
         vkcv::PushConstants pushConstantsCompute = vkcv::pushConstants<glm::vec2>();
         pushConstantsCompute.appendDrawcall(pushData);
         
-        uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil(particleSystem.getParticles().size()/256.f)),1,1};
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              computePipeline,
-                                              computeDispatchCount,
-                                              {vkcv::DescriptorSetUsage(0, computeDescriptorSet)},
-											  pushConstantsCompute);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				computePipeline,
+				vkcv::dispatchInvocations(particleSystem.getParticles().size(), 256),
+				{vkcv::DescriptorSetUsage(0, computeDescriptorSet)},
+				pushConstantsCompute
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle());
 
@@ -294,12 +295,12 @@ int main(int argc, const char **argv) {
         
         core.recordDrawcallsToCmdStream(
                 cmdStream,
-                particlePass,
                 particlePipeline,
 				pushConstantsDraw,
                 {drawcalls},
                 { colorBuffer },
-                windowHandle);
+                windowHandle
+		);
 	
 		bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer);
 
@@ -315,10 +316,10 @@ int main(int argc, const char **argv) {
 		
         core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites);
 
-        uint32_t tonemappingDispatchCount[3];
-        tonemappingDispatchCount[0] = std::ceil(swapchainWidth / 8.f);
-        tonemappingDispatchCount[1] = std::ceil(swapchainHeight / 8.f);
-        tonemappingDispatchCount[2] = 1;
+        const auto tonemappingDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 
         core.recordComputeDispatchToCmdStream(
             cmdStream, 
diff --git a/projects/path_tracer/src/main.cpp b/projects/path_tracer/src/main.cpp
index e7147e6b..3b9d8ab4 100644
--- a/projects/path_tracer/src/main.cpp
+++ b/projects/path_tracer/src/main.cpp
@@ -306,10 +306,10 @@ int main(int argc, const char** argv) {
 
 		const vkcv::CommandStreamHandle cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
-		uint32_t fullscreenDispatchCount[3] = {
-			static_cast<uint32_t> (std::ceil(swapchainWidth  / 8.f)),
-			static_cast<uint32_t> (std::ceil(swapchainHeight / 8.f)),
-			1 };
+		const auto fullscreenDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 
 		if (updateMaterials) {
 			std::vector<Material> materials;
@@ -368,11 +368,11 @@ int main(int argc, const char** argv) {
 
 		vkcv::PushConstants pushConstantsCompute = vkcv::pushConstants<RaytracingPushConstantData>();
 		pushConstantsCompute.appendDrawcall(raytracingPushData);
-
-		uint32_t traceDispatchCount[3] = { 
-			static_cast<uint32_t> (std::ceil(swapchainWidth  / 16.f)),
-			static_cast<uint32_t> (std::ceil(swapchainHeight / 16.f)),
-			1 };
+		
+		const auto traceDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(16, 16)
+		);
 
 		core.prepareImageForStorage(cmdStream, outputImage);
 
diff --git a/projects/saf_r/src/main.cpp b/projects/saf_r/src/main.cpp
index dd20e132..dda23973 100644
--- a/projects/saf_r/src/main.cpp
+++ b/projects/saf_r/src/main.cpp
@@ -278,14 +278,17 @@ int main(int argc, const char** argv) {
         pushConstantsCompute.appendDrawcall(raytracingPushData);
 
 		//dispatch compute shader
-		uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil(swapchainWidth/16.f)),
-                                            static_cast<uint32_t> (std::ceil(swapchainHeight/16.f)),
-                                            1 }; // Anzahl workgroups
+		const auto computeDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(16, 16)
+		);
+		
 		core.recordComputeDispatchToCmdStream(cmdStream,
 			computePipeline,
 			computeDispatchCount,
 			{ vkcv::DescriptorSetUsage(0, computeDescriptorSet) },
-			pushConstantsCompute);
+			pushConstantsCompute
+		);
 
 		core.recordBufferMemoryBarrier(cmdStream, lightsBuffer.getHandle());
 
diff --git a/projects/sph/src/main.cpp b/projects/sph/src/main.cpp
index 6955281c..a5bf4bf0 100644
--- a/projects/sph/src/main.cpp
+++ b/projects/sph/src/main.cpp
@@ -341,44 +341,52 @@ int main(int argc, const char **argv) {
         vkcv::PushConstants pushConstantsCompute (sizeof(pushData));
         pushConstantsCompute.appendDrawcall(pushData);
 
-        uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil(numberParticles/256.f)),1,1};
+        const auto computeDispatchCount = vkcv::dispatchInvocations(numberParticles, 256);
 
         // computing pressure pipeline
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              pressurePipeline,
-                                              computeDispatchCount,
-                                              {vkcv::DescriptorSetUsage(0, pressureDescriptorSet)},
-											  pushConstantsCompute);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				pressurePipeline,
+				computeDispatchCount,
+				{vkcv::DescriptorSetUsage(0, pressureDescriptorSet)},
+				pushConstantsCompute
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle());
 		core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle());
 
         // computing force pipeline
-		core.recordComputeDispatchToCmdStream(cmdStream,
-											  forcePipeline,
-											  computeDispatchCount,
-											  {vkcv::DescriptorSetUsage(0, forceDescriptorSet)},
-											  pushConstantsCompute);
+		core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				forcePipeline,
+				computeDispatchCount,
+				{vkcv::DescriptorSetUsage(0, forceDescriptorSet)},
+				pushConstantsCompute
+		);
 
 		core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle());
 		core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle());
 
         // computing update data pipeline
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              updateDataPipeline,
-                                              computeDispatchCount,
-                                              { vkcv::DescriptorSetUsage(0, updateDataDescriptorSet) },
-                                              pushConstantsCompute);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				updateDataPipeline,
+				computeDispatchCount,
+				{ vkcv::DescriptorSetUsage(0, updateDataDescriptorSet) },
+				pushConstantsCompute
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle());
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle());
 
         // computing flip pipeline
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              flipPipeline,
-                                              computeDispatchCount,
-                                              { vkcv::DescriptorSetUsage(0, flipDescriptorSet) },
-                                              pushConstantsCompute);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				flipPipeline,
+				computeDispatchCount,
+				{ vkcv::DescriptorSetUsage(0, flipDescriptorSet) },
+				pushConstantsCompute
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle());
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle());
@@ -390,7 +398,6 @@ int main(int argc, const char **argv) {
         
         core.recordDrawcallsToCmdStream(
                 cmdStream,
-                particlePass,
                 particlePipeline,
 				pushConstantsDraw,
                 {drawcalls},
@@ -412,17 +419,18 @@ int main(int argc, const char **argv) {
 		
         core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites);
 
-        uint32_t tonemappingDispatchCount[3];
-        tonemappingDispatchCount[0] = std::ceil(swapchainWidth / 8.f);
-        tonemappingDispatchCount[1] = std::ceil(swapchainHeight / 8.f);
-        tonemappingDispatchCount[2] = 1;
+        const auto tonemappingDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 
         core.recordComputeDispatchToCmdStream(
             cmdStream, 
             tonemappingPipe, 
             tonemappingDispatchCount, 
             {vkcv::DescriptorSetUsage(0, tonemappingDescriptor) },
-            vkcv::PushConstants(0));
+            vkcv::PushConstants(0)
+		);
 
         core.prepareSwapchainImageForPresent(cmdStream);
         core.submitCommandStream(cmdStream);
diff --git a/projects/voxelization/src/ShadowMapping.cpp b/projects/voxelization/src/ShadowMapping.cpp
index 78cf1fda..6aa31577 100644
--- a/projects/voxelization/src/ShadowMapping.cpp
+++ b/projects/voxelization/src/ShadowMapping.cpp
@@ -268,20 +268,20 @@ void ShadowMapping::recordShadowMapRendering(
 	m_corePtr->recordBeginDebugLabel(cmdStream, "Shadow map depth", {1, 1, 1, 1});
 	m_corePtr->recordDrawcallsToCmdStream(
 		cmdStream,
-		m_shadowMapPass,
 		m_shadowMapPipe,
 		shadowPushConstants,
 		drawcalls,
 		{ m_shadowMapDepth.getHandle() },
-		windowHandle);
+		windowHandle
+	);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapDepth.getHandle());
 	m_corePtr->recordEndDebugLabel(cmdStream);
 
 	// depth to moments
-	uint32_t dispatchCount[3];
-	dispatchCount[0] = static_cast<uint32_t>(std::ceil(shadowMapResolution / 8.f));
-	dispatchCount[1] = static_cast<uint32_t>(std::ceil(shadowMapResolution / 8.f));
-	dispatchCount[2] = 1;
+	const auto dispatchCount = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(shadowMapResolution, shadowMapResolution),
+			vkcv::DispatchSize(8, 8)
+	);
 
 	const uint32_t msaaSampleCount = vkcv::msaaToSampleCount(msaa);
 	
@@ -296,7 +296,8 @@ void ShadowMapping::recordShadowMapRendering(
 		m_depthToMomentsPipe,
 		dispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_depthToMomentsDescriptorSet) },
-		msaaPushConstants);
+		msaaPushConstants
+	);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMap.getHandle());
 	m_corePtr->recordEndDebugLabel(cmdStream);
 
@@ -309,7 +310,8 @@ void ShadowMapping::recordShadowMapRendering(
 		m_shadowBlurXPipe,
 		dispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_shadowBlurXDescriptorSet) },
-		vkcv::PushConstants(0));
+		vkcv::PushConstants(0)
+	);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapIntermediate.getHandle());
 
 	// blur Y
@@ -319,7 +321,8 @@ void ShadowMapping::recordShadowMapRendering(
 		m_shadowBlurYPipe,
 		dispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_shadowBlurYDescriptorSet) },
-		vkcv::PushConstants(0));
+		vkcv::PushConstants(0)
+	);
 	m_shadowMap.recordMipChainGeneration(cmdStream, downsampler);
 
 	m_corePtr->recordEndDebugLabel(cmdStream);
diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp
index 4053a34a..dd2cda78 100644
--- a/projects/voxelization/src/Voxelization.cpp
+++ b/projects/voxelization/src/Voxelization.cpp
@@ -251,10 +251,6 @@ void Voxelization::voxelizeMeshes(
 
 	// reset voxels
 	const uint32_t resetVoxelGroupSize = 64;
-	uint32_t resetVoxelDispatchCount[3];
-	resetVoxelDispatchCount[0] = glm::ceil(voxelCount / float(resetVoxelGroupSize));
-	resetVoxelDispatchCount[1] = 1;
-	resetVoxelDispatchCount[2] = 1;
 	
 	vkcv::PushConstants voxelCountPushConstants = vkcv::pushConstants<uint32_t>();
 	voxelCountPushConstants.appendDrawcall(voxelCount);
@@ -263,9 +259,10 @@ void Voxelization::voxelizeMeshes(
 	m_corePtr->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_voxelResetPipe,
-		resetVoxelDispatchCount,
+		vkcv::dispatchInvocations(voxelCount, resetVoxelGroupSize),
 		{ vkcv::DescriptorSetUsage(0, m_voxelResetDescriptorSet) },
-		voxelCountPushConstants);
+		voxelCountPushConstants
+	);
 	m_corePtr->recordBufferMemoryBarrier(cmdStream, m_voxelBuffer.getHandle());
 	m_corePtr->recordEndDebugLabel(cmdStream);
 
@@ -284,28 +281,28 @@ void Voxelization::voxelizeMeshes(
 	m_corePtr->prepareImageForStorage(cmdStream, m_voxelImageIntermediate.getHandle());
 	m_corePtr->recordDrawcallsToCmdStream(
 		cmdStream,
-		m_voxelizationPass,
 		m_voxelizationPipe,
 		voxelizationPushConstants,
 		drawcalls,
 		{ m_dummyRenderTarget.getHandle() },
-		windowHandle);
+		windowHandle
+	);
 	m_corePtr->recordEndDebugLabel(cmdStream);
 
 	// buffer to image
-	const uint32_t bufferToImageGroupSize[3] = { 4, 4, 4 };
-	uint32_t bufferToImageDispatchCount[3];
-	for (int i = 0; i < 3; i++) {
-		bufferToImageDispatchCount[i] = glm::ceil(voxelResolution / float(bufferToImageGroupSize[i]));
-	}
-
+	const auto bufferToImageDispatchCount = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(voxelResolution, voxelResolution, voxelResolution),
+			vkcv::DispatchSize(4, 4, 4)
+	);
+	
 	m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel buffer to image", { 1, 1, 1, 1 });
 	m_corePtr->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_bufferToImagePipe,
 		bufferToImageDispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_bufferToImageDescriptorSet) },
-		vkcv::PushConstants(0));
+		vkcv::PushConstants(0)
+	);
 
 	m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImageIntermediate.getHandle());
 	m_corePtr->recordEndDebugLabel(cmdStream);
@@ -361,12 +358,12 @@ void Voxelization::renderVoxelVisualisation(
 	m_corePtr->prepareImageForStorage(cmdStream, m_voxelImage.getHandle());
 	m_corePtr->recordDrawcallsToCmdStream(
 		cmdStream,
-		m_visualisationPass,
 		m_visualisationPipe,
 		voxelVisualisationPushConstants,
 		{ drawcall },
 		renderTargets,
-		windowHandle);
+		windowHandle
+	);
 	m_corePtr->recordEndDebugLabel(cmdStream);
 }
 
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 821cc1c2..449993d1 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -802,19 +802,18 @@ int main(int argc, const char** argv) {
 		for (const auto& m : modelMatrices) {
 			prepassPushConstants.appendDrawcall(viewProjectionCamera * m);
 		}
-
 		
 		const std::vector<vkcv::ImageHandle>    prepassRenderTargets = { depthBuffer };
 
 		core.recordBeginDebugLabel(cmdStream, "Depth prepass", { 1, 1, 1, 1 });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			prepassPass,
 			prepassPipeline,
 			prepassPushConstants,
 			prepassDrawcalls,
 			prepassRenderTargets,
-			windowHandle);
+			windowHandle
+		);
 
 		core.recordImageMemoryBarrier(cmdStream, depthBuffer);
 		core.recordEndDebugLabel(cmdStream);
@@ -837,12 +836,12 @@ int main(int argc, const char** argv) {
 		core.recordBeginDebugLabel(cmdStream, "Forward rendering", { 1, 1, 1, 1 });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			forwardPass,
 			forwardPipeline,
 			pushConstants,
 			drawcalls,
 			renderTargets,
-			windowHandle);
+			windowHandle
+		);
 		core.recordEndDebugLabel(cmdStream);
 
 		if (renderVoxelVis) {
@@ -856,27 +855,19 @@ int main(int argc, const char** argv) {
 		core.recordBeginDebugLabel(cmdStream, "Sky", { 1, 1, 1, 1 });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			skyPass,
 			skyPipe,
 			skySettingsPushConstants,
 			{ vkcv::DrawcallInfo(vkcv::Mesh({}, nullptr, 3), {}) },
 			renderTargets,
-			windowHandle);
-		core.recordEndDebugLabel(cmdStream);
-
-		const uint32_t fullscreenLocalGroupSize = 8;
-		
-		uint32_t fulsscreenDispatchCount [3];
-		
-		fulsscreenDispatchCount[0] = static_cast<uint32_t>(
-				glm::ceil(fsrWidth  / static_cast<float>(fullscreenLocalGroupSize))
+			windowHandle
 		);
+		core.recordEndDebugLabel(cmdStream);
 		
-		fulsscreenDispatchCount[1] = static_cast<uint32_t>(
-				glm::ceil(fsrHeight / static_cast<float>(fullscreenLocalGroupSize))
+		const uint32_t fullscreenLocalGroupSize = 8;
+		auto fullscreenDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(fsrWidth, fsrHeight),
+				vkcv::DispatchSize(fullscreenLocalGroupSize, fullscreenLocalGroupSize)
 		);
-		
-		fulsscreenDispatchCount[2] = 1;
 
 		if (usingMsaa) {
 			core.recordBeginDebugLabel(cmdStream, "MSAA resolve", { 1, 1, 1, 1 });
@@ -886,11 +877,12 @@ int main(int argc, const char** argv) {
 
 				assert(msaa == vkcv::Multisampling::MSAA4X);	// shaders is written for msaa 4x
 				core.recordComputeDispatchToCmdStream(
-					cmdStream,
-					resolvePipeline,
-					fulsscreenDispatchCount,
-					{ vkcv::DescriptorSetUsage(0, resolveDescriptorSet) },
-					vkcv::PushConstants(0));
+						cmdStream,
+						resolvePipeline,
+						fullscreenDispatchCount,
+						{ vkcv::DescriptorSetUsage(0, resolveDescriptorSet) },
+						vkcv::PushConstants(0)
+				);
 
 				core.recordImageMemoryBarrier(cmdStream, resolvedColorBuffer);
 			}
@@ -908,11 +900,11 @@ int main(int argc, const char** argv) {
 		
 		core.recordBeginDebugLabel(cmdStream, "Tonemapping", { 1, 1, 1, 1 });
 		core.recordComputeDispatchToCmdStream(
-			cmdStream, 
-			tonemappingPipeline, 
-			fulsscreenDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, tonemappingDescriptorSet) },
-			vkcv::PushConstants(0)
+				cmdStream,
+				tonemappingPipeline,
+				fullscreenDispatchCount,
+				{ vkcv::DescriptorSetUsage(0, tonemappingDescriptorSet) },
+				vkcv::PushConstants(0)
 		);
 		
 		core.prepareImageForStorage(cmdStream, swapBuffer2);
@@ -942,19 +934,16 @@ int main(int argc, const char** argv) {
 		vkcv::PushConstants timePushConstants = vkcv::pushConstants<float>();
 		timePushConstants.appendDrawcall(timeF);
 		
-		fulsscreenDispatchCount[0] = static_cast<uint32_t>(
-				glm::ceil(swapchainWidth  / static_cast<float>(fullscreenLocalGroupSize))
-		);
-		
-		fulsscreenDispatchCount[1] = static_cast<uint32_t>(
-				glm::ceil(swapchainHeight / static_cast<float>(fullscreenLocalGroupSize))
+		fullscreenDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(fullscreenLocalGroupSize, fullscreenLocalGroupSize)
 		);
 		
 		core.recordBeginDebugLabel(cmdStream, "Post Processing", { 1, 1, 1, 1 });
 		core.recordComputeDispatchToCmdStream(
 				cmdStream,
 				postEffectsPipeline,
-				fulsscreenDispatchCount,
+				fullscreenDispatchCount,
 				{ vkcv::DescriptorSetUsage(0, postEffectsDescriptorSet) },
 				timePushConstants
 		);
diff --git a/projects/wobble_bobble/src/main.cpp b/projects/wobble_bobble/src/main.cpp
index 24c203f7..5e31155d 100644
--- a/projects/wobble_bobble/src/main.cpp
+++ b/projects/wobble_bobble/src/main.cpp
@@ -692,8 +692,12 @@ int main(int argc, const char **argv) {
 		
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 		
-		const uint32_t dispatchSizeGrid[3] = {grid.getWidth() / 4, grid.getHeight() / 4, grid.getDepth() / 4};
-		const uint32_t dispatchSizeParticles[3] = {static_cast<uint32_t>(sim->count + 63) / 64, 1, 1};
+		const auto dispatchSizeGrid = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(grid.getWidth(), grid.getHeight(), grid.getDepth()),
+				vkcv::DispatchSize(4, 4, 4)
+		);
+		
+		const auto dispatchSizeParticles = vkcv::dispatchInvocations(sim->count, 64);
 		
 		for (int step = 0; step < 1; step++) {
 			core.recordBeginDebugLabel(cmdStream, "INIT PARTICLE WEIGHTS", {0.78f, 0.89f, 0.94f, 1.0f});
@@ -782,7 +786,6 @@ int main(int argc, const char **argv) {
 			
 			core.recordDrawcallsToCmdStream(
 					cmdStream,
-					gfxPassGrid,
 					gfxPipelineGrid,
 					cameraPushConstants,
 					drawcallsGrid,
@@ -797,7 +800,6 @@ int main(int argc, const char **argv) {
 			
 			core.recordDrawcallsToCmdStream(
 					cmdStream,
-					gfxPassParticles,
 					gfxPipelineParticles,
 					cameraPushConstants,
 					drawcallsParticles,
@@ -812,7 +814,6 @@ int main(int argc, const char **argv) {
 		
 		core.recordDrawcallsToCmdStream(
 				cmdStream,
-				gfxPassLines,
 				gfxPipelineLines,
 				cameraPushConstants,
 				drawcallsLines,
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 8454ae9a..c853f383 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -441,7 +441,6 @@ namespace vkcv
 
 	void Core::recordDrawcallsToCmdStream(
 		const CommandStreamHandle&      cmdStreamHandle,
-		const PassHandle&               renderpassHandle,
 		const GraphicsPipelineHandle    &pipelineHandle,
         const PushConstants             &pushConstantData,
         const std::vector<DrawcallInfo> &drawcalls,
@@ -462,6 +461,10 @@ namespace vkcv
 		
 		const auto width  = widthHeight[0];
 		const auto height = widthHeight[1];
+		
+		const PassHandle &renderpassHandle = m_GraphicsPipelineManager->getPipelineConfig(
+				pipelineHandle
+		).getPass();
 
 		const vk::RenderPass        renderpass      = m_PassManager->getVkPass(renderpassHandle);
 		const PassConfig            passConfig      = m_PassManager->getPassConfig(renderpassHandle);
@@ -516,7 +519,6 @@ namespace vkcv
 
     void Core::recordIndexedIndirectDrawcallsToCmdStream(
             const CommandStreamHandle                           cmdStreamHandle,
-            const PassHandle                                    renderpassHandle,
             const GraphicsPipelineHandle                        &pipelineHandle,
             const PushConstants                                 &pushConstantData,
             const vkcv::DescriptorSetHandle                     &compiledDescriptorSet,
@@ -536,6 +538,10 @@ namespace vkcv
 				*m_ImageManager);
         const auto width = widthHeight[0];
         const auto height = widthHeight[1];
+	
+		const PassHandle &renderpassHandle = m_GraphicsPipelineManager->getPipelineConfig(
+				pipelineHandle
+		).getPass();
 
         const vk::RenderPass        renderpass      = m_PassManager->getVkPass(renderpassHandle);
         const PassConfig            passConfig      = m_PassManager->getPassConfig(renderpassHandle);
@@ -657,7 +663,6 @@ namespace vkcv
 
 	void Core::recordMeshShaderDrawcalls(
 		const CommandStreamHandle&                          cmdStreamHandle,
-		const PassHandle&                                   renderpassHandle,
 		const GraphicsPipelineHandle                        &pipelineHandle,
 		const PushConstants&                                pushConstantData,
 		const std::vector<MeshShaderDrawcall>&              drawcalls,
@@ -676,6 +681,10 @@ namespace vkcv
 				*m_ImageManager);
 		const auto width  = widthHeight[0];
 		const auto height = widthHeight[1];
+		
+		const PassHandle &renderpassHandle = m_GraphicsPipelineManager->getPipelineConfig(
+				pipelineHandle
+		).getPass();
 
 		const vk::RenderPass        renderpass = m_PassManager->getVkPass(renderpassHandle);
 		const PassConfig            passConfig = m_PassManager->getPassConfig(renderpassHandle);
@@ -781,15 +790,12 @@ namespace vkcv
 		recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
     }
 
-	void Core::recordComputeDispatchToCmdStream(
-		CommandStreamHandle cmdStreamHandle,
-		ComputePipelineHandle computePipeline,
-		const uint32_t dispatchCount[3],
-		const std::vector<DescriptorSetUsage>& descriptorSetUsages,
-		const PushConstants& pushConstants) {
-
+	void Core::recordComputeDispatchToCmdStream(const CommandStreamHandle& cmdStreamHandle,
+												const ComputePipelineHandle& computePipeline,
+												const DispatchSize& dispatchSize,
+												const std::vector<DescriptorSetUsage>& descriptorSetUsages,
+												const PushConstants& pushConstants) {
 		auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
-
 			const auto pipelineLayout = m_ComputePipelineManager->getVkPipelineLayout(computePipeline);
 
 			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_ComputePipelineManager->getVkPipeline(computePipeline));
@@ -810,7 +816,8 @@ namespace vkcv
 					pushConstants.getSizePerDrawcall(),
 					pushConstants.getData());
 			}
-			cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]);
+			
+			cmdBuffer.dispatch(dispatchSize.x(), dispatchSize.y(), dispatchSize.z());
 		};
 
 		recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
diff --git a/src/vkcv/DispatchSize.cpp b/src/vkcv/DispatchSize.cpp
new file mode 100644
index 00000000..4f758672
--- /dev/null
+++ b/src/vkcv/DispatchSize.cpp
@@ -0,0 +1,44 @@
+
+#include "vkcv/DispatchSize.hpp"
+
+#include <cmath>
+
+namespace vkcv {
+	
+	DispatchSize::DispatchSize(uint32_t count)
+	: DispatchSize(count, 1, 1)
+	{}
+	
+	DispatchSize::DispatchSize(uint32_t dimensionX, uint32_t dimentionY, uint32_t dimensionZ)
+	: m_Dispatch({ dimensionX, dimentionY, dimensionZ })
+	{}
+	
+	const uint32_t *DispatchSize::data() const {
+		return m_Dispatch.data();
+	}
+	
+	uint32_t DispatchSize::operator[](size_t index) const {
+		return m_Dispatch.at(index);
+	}
+	
+	uint32_t DispatchSize::x() const {
+		return m_Dispatch[0];
+	}
+	
+	uint32_t DispatchSize::y() const {
+		return m_Dispatch[1];
+	}
+	
+	uint32_t DispatchSize::z() const {
+		return m_Dispatch[2];
+	}
+	
+	DispatchSize dispatchInvocations(DispatchSize globalInvocations, DispatchSize groupSize) {
+		const uint32_t dimensionX = std::ceil(1.0f * globalInvocations.x() / groupSize.x());
+		const uint32_t dimensionY = std::ceil(1.0f * globalInvocations.y() / groupSize.y());
+		const uint32_t dimensionZ = std::ceil(1.0f * globalInvocations.z() / groupSize.z());
+		
+		return DispatchSize(dimensionX, dimensionY, dimensionZ);
+	}
+	
+}
-- 
GitLab