diff --git a/config/Sources.cmake b/config/Sources.cmake
index 54bb3485ed975669668d987787975f019aa6358b..7ae106e2538c66179ab1ed50408551c43b785bc3 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -21,6 +21,8 @@ set(vkcv_sources
 
 		${vkcv_include}/vkcv/Buffer.hpp
 		
+		${vkcv_include}/vkcv/PushConstants.hpp
+		
 		${vkcv_include}/vkcv/BufferManager.hpp
 		${vkcv_source}/vkcv/BufferManager.cpp
 
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index cbbe1e908cdb74891ab9bfe4416c03e487e76b26..9f4ebb665ac0ee10bd228a880c12ef3345a5e60c 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -246,7 +246,7 @@ namespace vkcv
             const CommandStreamHandle       cmdStreamHandle,
 			const PassHandle                renderpassHandle, 
 			const PipelineHandle            pipelineHandle,
-			const PushConstantData          &pushConstantData,
+			const PushConstants             &pushConstants,
 			const std::vector<DrawcallInfo> &drawcalls,
 			const std::vector<ImageHandle>  &renderTargets);
 
@@ -255,7 +255,7 @@ namespace vkcv
 			PipelineHandle computePipeline,
 			const uint32_t dispatchCount[3],
 			const std::vector<DescriptorSetUsage> &descriptorSetUsages,
-			const PushConstantData& pushConstantData);
+			const PushConstants& pushConstants);
 
 		/**
 		 * @brief end recording and present image
diff --git a/include/vkcv/DrawcallRecording.hpp b/include/vkcv/DrawcallRecording.hpp
index 9f162a499a38d5633703f70eec8a8682e3328d72..572fc2b6b51735bdcd7eb77c1dd9d4a3482a1640 100644
--- a/include/vkcv/DrawcallRecording.hpp
+++ b/include/vkcv/DrawcallRecording.hpp
@@ -2,6 +2,7 @@
 #include <vulkan/vulkan.hpp>
 #include <vkcv/Handles.hpp>
 #include <vkcv/DescriptorConfig.hpp>
+#include <vkcv/PushConstants.hpp>
 
 namespace vkcv {
     struct VertexBufferBinding {
@@ -29,13 +30,6 @@ namespace vkcv {
         size_t                              indexCount;
     };
 
-    struct PushConstantData {
-        inline PushConstantData(void* data, size_t sizePerDrawcall) : data(data), sizePerDrawcall(sizePerDrawcall) {}
-
-        void* data;
-        size_t  sizePerDrawcall;
-    };
-
     struct DrawcallInfo {
         inline DrawcallInfo(const Mesh& mesh, const std::vector<DescriptorSetUsage>& descriptorSets, const uint32_t instanceCount = 1)
             : mesh(mesh), descriptorSets(descriptorSets), instanceCount(instanceCount){}
@@ -49,7 +43,7 @@ namespace vkcv {
         const DrawcallInfo      &drawcall,
         vk::CommandBuffer       cmdBuffer,
         vk::PipelineLayout      pipelineLayout,
-        const PushConstantData  &pushConstantData,
+        const PushConstants     &pushConstants,
         const size_t            drawcallIndex);
 
 }
\ No newline at end of file
diff --git a/include/vkcv/PushConstants.hpp b/include/vkcv/PushConstants.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b433d214d5319634a7498c9f0b87f97eeb58b063
--- /dev/null
+++ b/include/vkcv/PushConstants.hpp
@@ -0,0 +1,89 @@
+#pragma once
+
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+	
+	class PushConstants {
+	private:
+		std::vector<uint8_t> m_data;
+		size_t m_sizePerDrawcall;
+		
+	public:
+		template<typename T>
+		PushConstants() : PushConstants(sizeof(T)) {}
+		
+		explicit PushConstants(size_t sizePerDrawcall) :
+		m_data(),
+		m_sizePerDrawcall(sizePerDrawcall) {}
+		
+		PushConstants(const PushConstants& other) = default;
+		PushConstants(PushConstants&& other) = default;
+		
+		~PushConstants() = default;
+		
+		PushConstants& operator=(const PushConstants& other) = default;
+		PushConstants& operator=(PushConstants&& other) = default;
+		
+		[[nodiscard]]
+		size_t getSizePerDrawcall() const {
+			return m_sizePerDrawcall;
+		}
+		
+		[[nodiscard]]
+		size_t getFullSize() const {
+			return m_data.size();
+		}
+		
+		[[nodiscard]]
+		size_t getDrawcallCount() const {
+			return (m_data.size() / m_sizePerDrawcall);
+		}
+		
+		void clear() {
+			m_data.clear();
+		}
+		
+		template<typename T = uint8_t>
+		bool appendDrawcall(const T& value) {
+			if (sizeof(T) != m_sizePerDrawcall) {
+				return false;
+			}
+			
+			const size_t offset = m_data.size();
+			m_data.resize(offset + sizeof(value));
+			std::memcpy(m_data.data() + offset, &value, sizeof(value));
+			return true;
+		}
+		
+		template<typename T = uint8_t>
+		T& getDrawcall(size_t index) {
+			const size_t offset = (index * m_sizePerDrawcall);
+			return *reinterpret_cast<T*>(m_data.data() + offset);
+		}
+		
+		template<typename T = uint8_t>
+		const T& getDrawcall(size_t index) const {
+			const size_t offset = (index * m_sizePerDrawcall);
+			return *reinterpret_cast<const T*>(m_data.data() + offset);
+		}
+		
+		[[nodiscard]]
+		const void* getDrawcallData(size_t index) const {
+			const size_t offset = (index * m_sizePerDrawcall);
+			return reinterpret_cast<const void*>(m_data.data() + offset);
+		}
+		
+		[[nodiscard]]
+		const void* getData() const {
+			if (m_data.empty()) {
+				return nullptr;
+			} else {
+				return m_data.data();
+			}
+		}
+		
+	};
+	
+}
diff --git a/modules/scene/include/vkcv/scene/Mesh.hpp b/modules/scene/include/vkcv/scene/Mesh.hpp
index 899e5e0ce11589b6021d6554cda1f7f32f356e37..bc82af4bfabed5e8bfc286bc53cd7b89791726fc 100644
--- a/modules/scene/include/vkcv/scene/Mesh.hpp
+++ b/modules/scene/include/vkcv/scene/Mesh.hpp
@@ -8,6 +8,8 @@
 
 namespace vkcv::scene {
 	
+	typedef typename event_function<const glm::mat4&, const glm::mat4&, PushConstants&, vkcv::DrawcallInfo&>::type RecordMeshDrawcallFunction;
+	
 	class Node;
 	
 	class Mesh {
@@ -26,8 +28,9 @@ namespace vkcv::scene {
 				  const asset::Mesh& mesh);
 		
 		void recordDrawcalls(const glm::mat4& viewProjection,
-							 std::vector<glm::mat4>& matrices,
-							 std::vector<DrawcallInfo>& drawcalls);
+							 PushConstants& pushConstants,
+							 std::vector<DrawcallInfo>& drawcalls,
+							 const RecordMeshDrawcallFunction& record);
 		
 		[[nodiscard]]
 		size_t getDrawcallCount() const;
diff --git a/modules/scene/include/vkcv/scene/Node.hpp b/modules/scene/include/vkcv/scene/Node.hpp
index 0772172d67f80772360d411bd6a1a18b02725bff..1fcca5b9cbecf1064070d7737d008d2b108371db 100644
--- a/modules/scene/include/vkcv/scene/Node.hpp
+++ b/modules/scene/include/vkcv/scene/Node.hpp
@@ -28,8 +28,9 @@ namespace vkcv::scene {
 		void loadMesh(const asset::Scene& asset_scene, const asset::Mesh& asset_mesh);
 		
 		void recordDrawcalls(const glm::mat4& viewProjection,
-							 std::vector<glm::mat4>& matrices,
-							 std::vector<DrawcallInfo>& drawcalls);
+							 PushConstants& pushConstants,
+							 std::vector<DrawcallInfo>& drawcalls,
+							 const RecordMeshDrawcallFunction& record);
 		
 		void splitMeshesToSubNodes(size_t maxMeshesPerNode);
 		
diff --git a/modules/scene/include/vkcv/scene/Scene.hpp b/modules/scene/include/vkcv/scene/Scene.hpp
index d9104ab40f2f633b5f7d14cb95a1a202cfeb7c32..429c0bcf729f9afb7dd76cdd58c54931862e1a4a 100644
--- a/modules/scene/include/vkcv/scene/Scene.hpp
+++ b/modules/scene/include/vkcv/scene/Scene.hpp
@@ -4,6 +4,7 @@
 #include <mutex>
 
 #include <vkcv/Core.hpp>
+#include <vkcv/Event.hpp>
 #include <vkcv/camera/Camera.hpp>
 #include <vkcv/material/Material.hpp>
 
@@ -54,11 +55,13 @@ namespace vkcv::scene {
 		[[nodiscard]]
 		const material::Material& getMaterial(size_t index) const;
 		
-		void recordDrawcalls(CommandStreamHandle       		 &cmdStream,
-							 const camera::Camera			 &camera,
-							 const PassHandle                &pass,
-							 const PipelineHandle            &pipeline,
-							 const std::vector<ImageHandle>  &renderTargets);
+		void recordDrawcalls(CommandStreamHandle       		  &cmdStream,
+							 const camera::Camera			  &camera,
+							 const PassHandle                 &pass,
+							 const PipelineHandle             &pipeline,
+							 size_t							  pushConstantsSizePerDrawcall,
+							 const RecordMeshDrawcallFunction &record,
+							 const std::vector<ImageHandle>   &renderTargets);
 		
 		static Scene create(Core& core);
 		
diff --git a/modules/scene/src/vkcv/scene/Mesh.cpp b/modules/scene/src/vkcv/scene/Mesh.cpp
index ff93029e7883aaa2103a28415c0caeead4c3ad57..53fb81713ed7e14049a21cb91c771d67f2f7086c 100644
--- a/modules/scene/src/vkcv/scene/Mesh.cpp
+++ b/modules/scene/src/vkcv/scene/Mesh.cpp
@@ -89,8 +89,9 @@ namespace vkcv::scene {
 	}
 	
 	void Mesh::recordDrawcalls(const glm::mat4& viewProjection,
-							   std::vector<glm::mat4>& matrices,
-							   std::vector<DrawcallInfo>& drawcalls) {
+							   PushConstants& pushConstants,
+							   std::vector<DrawcallInfo>& drawcalls,
+							   const RecordMeshDrawcallFunction& record) {
 		const glm::mat4 transform = viewProjection * m_transform;
 		
 		if (!checkFrustum(viewProjection, m_bounds)) {
@@ -98,8 +99,11 @@ namespace vkcv::scene {
 		}
 		
 		if (m_drawcalls.size() == 1) {
-			matrices.push_back(transform);
 			drawcalls.push_back(m_drawcalls[0]);
+			
+			if (record) {
+				record(transform, m_transform, pushConstants, drawcalls.back());
+			}
 		} else {
 			for (size_t i = 0; i < m_parts.size(); i++) {
 				const MeshPart& part = m_parts[i];
@@ -108,8 +112,11 @@ namespace vkcv::scene {
 					continue;
 				}
 				
-				matrices.push_back(transform);
 				drawcalls.push_back(m_drawcalls[i]);
+				
+				if (record) {
+					record(transform, m_transform, pushConstants, drawcalls.back());
+				}
 			}
 		}
 	}
diff --git a/modules/scene/src/vkcv/scene/Node.cpp b/modules/scene/src/vkcv/scene/Node.cpp
index 906e00e8a96504849f9b8baaf82d630e4450ed56..32230099b2f693362bab69d8172a4dee56c4e304 100644
--- a/modules/scene/src/vkcv/scene/Node.cpp
+++ b/modules/scene/src/vkcv/scene/Node.cpp
@@ -91,18 +91,19 @@ namespace vkcv::scene {
 	}
 	
 	void Node::recordDrawcalls(const glm::mat4& viewProjection,
-							   std::vector<glm::mat4>& matrices,
-							   std::vector<DrawcallInfo>& drawcalls) {
+							   PushConstants& pushConstants,
+							   std::vector<DrawcallInfo>& drawcalls,
+							   const RecordMeshDrawcallFunction& record) {
 		if (!checkFrustum(viewProjection, m_bounds)) {
 			return;
 		}
 		
 		for (auto& mesh : m_meshes) {
-			mesh.recordDrawcalls(viewProjection, matrices, drawcalls);
+			mesh.recordDrawcalls(viewProjection, pushConstants, drawcalls, record);
 		}
 		
 		for (auto& node : m_nodes) {
-			node.recordDrawcalls(viewProjection, matrices, drawcalls);
+			node.recordDrawcalls(viewProjection, pushConstants, drawcalls, record);
 		}
 	}
 	
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index 1a5f76e5d0bb4d64db11791b7e8d84ba8d0a764a..b050f67ff352296db6155247beebbb5682e542c2 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -109,12 +109,14 @@ namespace vkcv::scene {
 		return m_materials[index].m_data;
 	}
 	
-	void Scene::recordDrawcalls(CommandStreamHandle       		&cmdStream,
-								const camera::Camera			&camera,
-								const PassHandle                &pass,
-								const PipelineHandle            &pipeline,
-								const std::vector<ImageHandle>  &renderTargets) {
-		std::vector<glm::mat4> matrices;
+	void Scene::recordDrawcalls(CommandStreamHandle       		 &cmdStream,
+								const camera::Camera			 &camera,
+								const PassHandle                 &pass,
+								const PipelineHandle             &pipeline,
+								size_t							 pushConstantsSizePerDrawcall,
+								const RecordMeshDrawcallFunction &record,
+								const std::vector<ImageHandle>   &renderTargets) {
+		PushConstants pushConstants (pushConstantsSizePerDrawcall);
 		std::vector<DrawcallInfo> drawcalls;
 		size_t count = 0;
 		
@@ -122,18 +124,16 @@ namespace vkcv::scene {
 		
 		for (auto& node : m_nodes) {
 			count += node.getDrawcallCount();
-			node.recordDrawcalls(viewProjection, matrices, drawcalls);
+			node.recordDrawcalls(viewProjection, pushConstants, drawcalls, record);
 		}
 		
 		vkcv_log(LogLevel::RAW_INFO, "Frustum culling: %lu / %lu", drawcalls.size(), count);
 		
-		PushConstantData pushConstantData (matrices.data(), sizeof(glm::mat4));
-		
 		m_core->recordDrawcallsToCmdStream(
 				cmdStream,
 				pass,
 				pipeline,
-				pushConstantData,
+				pushConstants,
 				drawcalls,
 				renderTargets
 		);
diff --git a/projects/bloom/src/BloomAndFlares.cpp b/projects/bloom/src/BloomAndFlares.cpp
index 6f26db9de0f2c8334b6dd7e5dd6cf4b6f48baedc..7529dcf0ca1e1d164c33658d7e90c105b88edc49 100644
--- a/projects/bloom/src/BloomAndFlares.cpp
+++ b/projects/bloom/src/BloomAndFlares.cpp
@@ -104,7 +104,7 @@ void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStre
             m_DownsamplePipe,
             initialDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[0]).vulkanHandle)},
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 
     // downsample dispatches of blur buffer's mip maps
     float mipDispatchCountX = dispatchCountX;
@@ -139,7 +139,7 @@ void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStre
                 m_DownsamplePipe,
                 mipDispatchCount,
                 {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[mipLevel]).vulkanHandle)},
-                vkcv::PushConstantData(nullptr, 0));
+                vkcv::PushConstants(0));
 
         // image barrier between mips
         p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle());
@@ -184,7 +184,7 @@ void BloomAndFlares::execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream
                 m_UpsamplePipe,
                 upsampleDispatchCount,
                 {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_UpsampleDescSets[mipLevel]).vulkanHandle)},
-                vkcv::PushConstantData(nullptr, 0)
+                vkcv::PushConstants(0)
         );
         // image barrier between mips
         p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle());
@@ -216,7 +216,7 @@ void BloomAndFlares::execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStr
             m_LensFlarePipe,
             lensFeatureDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_LensFlareDescSet).vulkanHandle)},
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 }
 
 void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStream,
@@ -249,7 +249,7 @@ void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStrea
             m_CompositePipe,
             compositeDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_CompositeDescSet).vulkanHandle)},
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 }
 
 void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStream,
diff --git a/projects/bloom/src/main.cpp b/projects/bloom/src/main.cpp
index 7a17a51f1c7d638575c0b5aafcdca49b589533ef..95435a69032bf64b4aa704830162ae1e096849f5 100644
--- a/projects/bloom/src/main.cpp
+++ b/projects/bloom/src/main.cpp
@@ -252,9 +252,6 @@ int main(int argc, const char** argv) {
 	};
 	const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig);
 
-	std::vector<std::array<glm::mat4, 2>> mainPassMatrices;
-	std::vector<glm::mat4> mvpLight;
-
 	// gamma correction compute shader
 	vkcv::ShaderProgram gammaCorrectionProgram;
 	compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/gammaCorrection.comp",
@@ -348,18 +345,19 @@ int main(int argc, const char** argv) {
 		lightBuffer.fill({ lightInfo });
 
 		const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP();
-
-		mainPassMatrices.clear();
-		mvpLight.clear();
+		
+		vkcv::PushConstants pushConstants (2 * sizeof(glm::mat4));
+		vkcv::PushConstants shadowPushConstants (sizeof(glm::mat4));
+		
 		for (const auto& m : modelMatrices) {
-			mainPassMatrices.push_back({ viewProjectionCamera * m, m });
-			mvpLight.push_back(lightInfo.lightMatrix * m);
+			pushConstants.appendDrawcall(std::array<glm::mat4, 2>{ viewProjectionCamera * m, m });
+			shadowPushConstants.appendDrawcall(lightInfo.lightMatrix * m);
 		}
 
-		vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4));
+		
 		const std::vector<vkcv::ImageHandle> renderTargets = { colorBuffer, depthBuffer };
 
-		const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4));
+		const
 
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
@@ -368,7 +366,7 @@ int main(int argc, const char** argv) {
 			cmdStream,
 			shadowPass,
 			shadowPipe,
-			shadowPushConstantData,
+			shadowPushConstants,
 			shadowDrawcalls,
 			{ shadowMap.getHandle() });
 		core.prepareImageForSampling(cmdStream, shadowMap.getHandle());
@@ -378,7 +376,7 @@ int main(int argc, const char** argv) {
 			cmdStream,
             forwardPass,
             forwardPipeline,
-			pushConstantData,
+			pushConstants,
 			drawcalls,
 			renderTargets);
 
@@ -406,7 +404,7 @@ int main(int argc, const char** argv) {
 			gammaCorrectionPipeline, 
 			gammaCorrectionDispatchCount,
 			{ vkcv::DescriptorSetUsage(0, core.getDescriptorSet(gammaCorrectionDescriptorSet).vulkanHandle) },
-			vkcv::PushConstantData(nullptr, 0));
+			vkcv::PushConstants(0));
 
 		// present and end
 		core.prepareSwapchainImageForPresent(cmdStream);
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index e7546fc3a143b3638cceb36869c519336ebec751..6eaf429c9f769792ccf2435ee41bcb52973701e3 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -185,7 +185,8 @@ int main(int argc, const char** argv) {
 		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
         glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
 
-		vkcv::PushConstantData pushConstantData((void*)&mvp, sizeof(glm::mat4));
+		vkcv::PushConstants pushConstants (sizeof(glm::mat4));
+		pushConstants.appendDrawcall(mvp);
 
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
@@ -194,7 +195,7 @@ int main(int argc, const char** argv) {
 			cmdStream,
 			firstMeshPass,
 			firstMeshPipeline,
-			pushConstantData,
+			pushConstants,
 			{ drawcall },
 			renderTargets);
 		core.prepareSwapchainImageForPresent(cmdStream);
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 0f81990bdddfffb4c6a59541311a7ee5e19837e9..486b21343926e1b922b73ba8ffb29428de6ea9a7 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -125,10 +125,18 @@ int main(int argc, const char** argv) {
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
+		auto recordMesh = [](const glm::mat4& MVP, const glm::mat4& M,
+							 vkcv::PushConstants &pushConstants,
+							 vkcv::DrawcallInfo& drawcallInfo) {
+			pushConstants.appendDrawcall(MVP);
+		};
+		
 		scene.recordDrawcalls(cmdStream,
 							  cameraManager.getActiveCamera(),
 							  scenePass,
 							  scenePipeline,
+							  sizeof(glm::mat4),
+							  recordMesh,
 							  renderTargets);
 		
 		core.prepareSwapchainImageForPresent(cmdStream);
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 5bdd55a263f4d81d8f424c056d7d6c0b54ccb1ca..db175a801b75535343cfa468af90ce490a670938 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -195,26 +195,31 @@ int main(int argc, const char** argv) {
 		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
         glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
 
-		vkcv::PushConstantData pushConstantData((void*)&mvp, sizeof(glm::mat4));
+		vkcv::PushConstants pushConstants (sizeof(glm::mat4));
+		pushConstants.appendDrawcall(mvp);
+		
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
 			trianglePass,
 			trianglePipeline,
-			pushConstantData,
+			pushConstants,
 			{ drawcall },
 			{ swapchainInput });
 
 		const uint32_t dispatchSize[3] = { 2, 1, 1 };
 		const float theMeaningOfLife = 42;
+		
+		vkcv::PushConstants pushConstantsCompute (sizeof(theMeaningOfLife));
+		pushConstantsCompute.appendDrawcall(theMeaningOfLife);
 
 		core.recordComputeDispatchToCmdStream(
 			cmdStream,
 			computePipeline,
 			dispatchSize,
 			{ vkcv::DescriptorSetUsage(0, core.getDescriptorSet(computeDescriptorSet).vulkanHandle) },
-			vkcv::PushConstantData((void*)&theMeaningOfLife, sizeof(theMeaningOfLife)));
+			pushConstantsCompute);
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
diff --git a/projects/particle_simulation/src/BloomAndFlares.cpp b/projects/particle_simulation/src/BloomAndFlares.cpp
index 23ace2bc35a2e421613718c62380f9161a408f70..98d53c2a1a2c08d40473858b47aacf34da30f7ed 100644
--- a/projects/particle_simulation/src/BloomAndFlares.cpp
+++ b/projects/particle_simulation/src/BloomAndFlares.cpp
@@ -104,7 +104,7 @@ void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStre
             m_DownsamplePipe,
             initialDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[0]).vulkanHandle)},
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 
     // downsample dispatches of blur buffer's mip maps
     float mipDispatchCountX = dispatchCountX;
@@ -139,7 +139,7 @@ void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStre
                 m_DownsamplePipe,
                 mipDispatchCount,
                 {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[mipLevel]).vulkanHandle)},
-                vkcv::PushConstantData(nullptr, 0));
+                vkcv::PushConstants(0));
 
         // image barrier between mips
         p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle());
@@ -184,7 +184,7 @@ void BloomAndFlares::execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream
                 m_UpsamplePipe,
                 upsampleDispatchCount,
                 {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_UpsampleDescSets[mipLevel]).vulkanHandle)},
-                vkcv::PushConstantData(nullptr, 0)
+                vkcv::PushConstants(0)
         );
         // image barrier between mips
         p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle());
@@ -216,7 +216,7 @@ void BloomAndFlares::execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStr
             m_LensFlarePipe,
             lensFeatureDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_LensFlareDescSet).vulkanHandle)},
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 }
 
 void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStream,
@@ -249,7 +249,7 @@ void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStrea
             m_CompositePipe,
             compositeDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_CompositeDescSet).vulkanHandle)},
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 }
 
 void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStream,
diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp
index a22044f0d2588a43a5e7a0f6cba25d9c7460be9f..ad02c651486e0f1b693a348d016a6f59d0f73eb4 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -260,30 +260,38 @@ int main(int argc, const char **argv) {
         cameraManager.update(deltatime);
 
         // split view and projection to allow for easy billboarding in shader
-        glm::mat4 renderingMatrices[2];
-        renderingMatrices[0] = cameraManager.getActiveCamera().getView();
-        renderingMatrices[1] = cameraManager.getActiveCamera().getProjection();
+        struct {
+			glm::mat4 view;
+			glm::mat4 projection;
+        } renderingMatrices;
+        
+        renderingMatrices.view = cameraManager.getActiveCamera().getView();
+        renderingMatrices.projection = cameraManager.getActiveCamera().getProjection();
 
         auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
         float random = rdm(rdmEngine);
         glm::vec2 pushData = glm::vec2(deltatime, random);
 
-        vkcv::PushConstantData pushConstantDataCompute( &pushData, sizeof(glm::vec2));
+        vkcv::PushConstants pushConstantsCompute (sizeof(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,core.getDescriptorSet(computeDescriptorSet).vulkanHandle)},
-                                              pushConstantDataCompute);
+											  pushConstantsCompute);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle());
 
-        vkcv::PushConstantData pushConstantDataDraw((void *) &renderingMatrices[0], 2 * sizeof(glm::mat4));
+        vkcv::PushConstants pushConstantsDraw (sizeof(renderingMatrices));
+        pushConstantsDraw.appendDrawcall(renderingMatrices);
+        
         core.recordDrawcallsToCmdStream(
                 cmdStream,
                 particlePass,
                 particlePipeline,
-                pushConstantDataDraw,
+				pushConstantsDraw,
                 {drawcalls},
                 { colorBuffer });
 
@@ -309,7 +317,7 @@ int main(int argc, const char **argv) {
             tonemappingPipe, 
             tonemappingDispatchCount, 
             {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptor).vulkanHandle) },
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 
         core.prepareSwapchainImageForPresent(cmdStream);
         core.submitCommandStream(cmdStream);
diff --git a/projects/voxelization/src/BloomAndFlares.cpp b/projects/voxelization/src/BloomAndFlares.cpp
index fac57735a6544c197f880f78e1f512382607d048..6cb02e9035daf7abebc047d26137d0ba973bb4f1 100644
--- a/projects/voxelization/src/BloomAndFlares.cpp
+++ b/projects/voxelization/src/BloomAndFlares.cpp
@@ -128,7 +128,7 @@ void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStre
             m_DownsamplePipe,
             initialDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[0]).vulkanHandle)},
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 
     // downsample dispatches of blur buffer's mip maps
     float mipDispatchCountX = dispatchCountX;
@@ -163,7 +163,7 @@ void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStre
                 m_DownsamplePipe,
                 mipDispatchCount,
                 {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[mipLevel]).vulkanHandle)},
-                vkcv::PushConstantData(nullptr, 0));
+                vkcv::PushConstants(0));
 
         // image barrier between mips
         p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle());
@@ -208,7 +208,7 @@ void BloomAndFlares::execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream
                 m_UpsamplePipe,
                 upsampleDispatchCount,
                 {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_UpsampleDescSets[mipLevel]).vulkanHandle)},
-                vkcv::PushConstantData(nullptr, 0)
+                vkcv::PushConstants(0)
         );
         // image barrier between mips
         p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle());
@@ -243,7 +243,7 @@ void BloomAndFlares::execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStr
             m_LensFlarePipe,
             lensFeatureDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_LensFlareDescSet).vulkanHandle)},
-            vkcv::PushConstantData(nullptr, 0));
+            vkcv::PushConstants(0));
 
     // upsample dispatch
     p_Core->prepareImageForStorage(cmdStream, m_LensFeatures.getHandle());
@@ -276,7 +276,7 @@ void BloomAndFlares::execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStr
             m_UpsamplePipe,
             upsampleDispatchCount,
             { vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_UpsampleLensFlareDescSets[i]).vulkanHandle) },
-            vkcv::PushConstantData(nullptr, 0)
+            vkcv::PushConstants(0)
         );
         // image barrier between mips
         p_Core->recordImageMemoryBarrier(cmdStream, m_LensFeatures.getHandle());
@@ -309,6 +309,9 @@ void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStrea
             static_cast<uint32_t>(glm::ceil(dispatchCountY)),
             1
     };
+	
+	vkcv::PushConstants pushConstants (sizeof(cameraForward));
+	pushConstants.appendDrawcall(cameraForward);
 
     // bloom composite dispatch
     p_Core->recordComputeDispatchToCmdStream(
@@ -316,7 +319,7 @@ void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStrea
             m_CompositePipe,
             compositeDispatchCount,
             {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_CompositeDescSet).vulkanHandle)},
-            vkcv::PushConstantData((void*)&cameraForward, sizeof(cameraForward)));
+			pushConstants);
 }
 
 void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment, 
diff --git a/projects/voxelization/src/ShadowMapping.cpp b/projects/voxelization/src/ShadowMapping.cpp
index a330394b7bd7ff2a4b8c347bd79e676dbc70f846..32dd5457541f8f09f4d2711ea831e3c78de2303a 100644
--- a/projects/voxelization/src/ShadowMapping.cpp
+++ b/projects/voxelization/src/ShadowMapping.cpp
@@ -248,12 +248,13 @@ void ShadowMapping::recordShadowMapRendering(
 		voxelVolumeOffset,
 		voxelVolumeExtent);
 	m_lightInfoBuffer.fill({ lightInfo });
-
-	std::vector<glm::mat4> mvpLight;
+	
+	vkcv::PushConstants shadowPushConstants (sizeof(glm::mat4));
+	
 	for (const auto& m : modelMatrices) {
-		mvpLight.push_back(lightInfo.lightMatrix * m);
+		shadowPushConstants.appendDrawcall(lightInfo.lightMatrix * m);
 	}
-	const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4));
+	
 
 	std::vector<vkcv::DrawcallInfo> drawcalls;
 	for (const auto& mesh : meshes) {
@@ -264,7 +265,7 @@ void ShadowMapping::recordShadowMapRendering(
 		cmdStream,
 		m_shadowMapPass,
 		m_shadowMapPipe,
-		shadowPushConstantData,
+		shadowPushConstants,
 		drawcalls,
 		{ m_shadowMapDepth.getHandle() });
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapDepth.getHandle());
@@ -276,6 +277,9 @@ void ShadowMapping::recordShadowMapRendering(
 	dispatchCount[2] = 1;
 
 	const uint32_t msaaSampleCount = msaaToSampleCount(msaa);
+	
+	vkcv::PushConstants msaaPushConstants (sizeof(msaaSampleCount));
+	msaaPushConstants.appendDrawcall(msaaSampleCount);
 
 	m_corePtr->prepareImageForStorage(cmdStream, m_shadowMap.getHandle());
 	m_corePtr->recordComputeDispatchToCmdStream(
@@ -283,7 +287,7 @@ void ShadowMapping::recordShadowMapRendering(
 		m_depthToMomentsPipe,
 		dispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_depthToMomentsDescriptorSet).vulkanHandle) },
-		vkcv::PushConstantData((void*)&msaaSampleCount, sizeof(msaaSampleCount)));
+		msaaPushConstants);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMap.getHandle());
 
 	// blur X
@@ -293,7 +297,7 @@ void ShadowMapping::recordShadowMapRendering(
 		m_shadowBlurXPipe,
 		dispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_shadowBlurXDescriptorSet).vulkanHandle) },
-		vkcv::PushConstantData(nullptr, 0));
+		vkcv::PushConstants(0));
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapIntermediate.getHandle());
 
 	// blur Y
@@ -303,7 +307,7 @@ void ShadowMapping::recordShadowMapRendering(
 		m_shadowBlurYPipe,
 		dispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_shadowBlurYDescriptorSet).vulkanHandle) },
-		vkcv::PushConstantData(nullptr, 0));
+		vkcv::PushConstants(0));
 	m_shadowMap.recordMipChainGeneration(cmdStream);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMap.getHandle());
 }
diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp
index c117b4b9e6b896fbf51aae83343f30281061be9f..1f1888d5c0ed30effc4c93c02efddb370ce8ad21 100644
--- a/projects/voxelization/src/Voxelization.cpp
+++ b/projects/voxelization/src/Voxelization.cpp
@@ -232,34 +232,36 @@ void Voxelization::voxelizeMeshes(
 
 	const glm::mat4 voxelizationView = glm::translate(glm::mat4(1.f), -m_voxelInfo.offset);
 	const glm::mat4 voxelizationViewProjection = voxelizationProjection * voxelizationView;
-
-	std::vector<std::array<glm::mat4, 2>> voxelizationMatrices;
+	
+	vkcv::PushConstants voxelizationPushConstants (2 * sizeof(glm::mat4));
+	
 	for (const auto& m : modelMatrices) {
-		voxelizationMatrices.push_back({ voxelizationViewProjection * m, m });
+		voxelizationPushConstants.appendDrawcall(std::array<glm::mat4, 2>{ voxelizationViewProjection * m, m });
 	}
 
-	const vkcv::PushConstantData voxelizationPushConstantData((void*)voxelizationMatrices.data(), 2 * sizeof(glm::mat4));
-
 	// 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 (sizeof(voxelCount));
+	voxelCountPushConstants.appendDrawcall(voxelCount);
 
 	m_corePtr->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_voxelResetPipe,
 		resetVoxelDispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_voxelResetDescriptorSet).vulkanHandle) },
-		vkcv::PushConstantData(&voxelCount, sizeof(voxelCount)));
+		voxelCountPushConstants);
 	m_corePtr->recordBufferMemoryBarrier(cmdStream, m_voxelBuffer.getHandle());
 
 	// voxelization
 	std::vector<vkcv::DrawcallInfo> drawcalls;
 	for (int i = 0; i < meshes.size(); i++) {
 		drawcalls.push_back(vkcv::DrawcallInfo(
-			meshes[i], 
+			meshes[i],
 			{ 
 				vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_voxelizationDescriptorSet).vulkanHandle),
 				vkcv::DescriptorSetUsage(1, m_corePtr->getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) 
@@ -271,7 +273,7 @@ void Voxelization::voxelizeMeshes(
 		cmdStream,
 		m_voxelizationPass,
 		m_voxelizationPipe,
-		voxelizationPushConstantData,
+		voxelizationPushConstants,
 		drawcalls,
 		{ m_dummyRenderTarget.getHandle() });
 
@@ -287,7 +289,7 @@ void Voxelization::voxelizeMeshes(
 		m_bufferToImagePipe,
 		bufferToImageDispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_bufferToImageDescriptorSet).vulkanHandle) },
-		vkcv::PushConstantData(nullptr, 0));
+		vkcv::PushConstants(0));
 
 	m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImageIntermediate.getHandle());
 
@@ -303,7 +305,7 @@ void Voxelization::voxelizeMeshes(
 		m_secondaryBouncePipe,
 		bufferToImageDispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_secondaryBounceDescriptorSet).vulkanHandle) },
-		vkcv::PushConstantData(nullptr, 0));
+		vkcv::PushConstants(0));
 	m_voxelImage.recordMipChainGeneration(cmdStream);
 
 	m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImage.getHandle());
@@ -319,7 +321,8 @@ void Voxelization::renderVoxelVisualisation(
 	const std::vector<vkcv::ImageHandle>&   renderTargets,
 	uint32_t                                mipLevel) {
 
-	const vkcv::PushConstantData voxelVisualisationPushConstantData((void*)&viewProjectin, sizeof(glm::mat4));
+	vkcv::PushConstants voxelVisualisationPushConstants (sizeof(glm::mat4));
+	voxelVisualisationPushConstants.appendDrawcall(viewProjectin);
 
 	mipLevel = std::clamp(mipLevel, (uint32_t)0, m_voxelImage.getMipCount()-1);
 
@@ -342,7 +345,7 @@ void Voxelization::renderVoxelVisualisation(
 		cmdStream,
 		m_visualisationPass,
 		m_visualisationPipe,
-		voxelVisualisationPushConstantData,
+		voxelVisualisationPushConstants,
 		{ drawcall },
 		renderTargets);
 }
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index edc50c554b6c73bd2f06914eba6dd7adf9e43483..68018ef80f801b797eca30078ca6fadf897da4ad 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -618,29 +618,32 @@ int main(int argc, const char** argv) {
 
 		// depth prepass
 		const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP();
-
+		
+		vkcv::PushConstants prepassPushConstants (sizeof(glm::mat4));
+		
 		std::vector<glm::mat4> prepassMatrices;
 		for (const auto& m : modelMatrices) {
-			prepassMatrices.push_back(viewProjectionCamera * m);
+			prepassPushConstants.appendDrawcall(viewProjectionCamera * m);
 		}
 
-		const vkcv::PushConstantData            prepassPushConstantData((void*)prepassMatrices.data(), sizeof(glm::mat4));
+		
 		const std::vector<vkcv::ImageHandle>    prepassRenderTargets = { depthBuffer };
 
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
 			prepassPass,
 			prepassPipeline,
-			prepassPushConstantData,
+			prepassPushConstants,
 			prepassDrawcalls,
 			prepassRenderTargets);
 
 		core.recordImageMemoryBarrier(cmdStream, depthBuffer);
-
+		
+		vkcv::PushConstants pushConstants (2 * sizeof(glm::mat4));
+		
 		// main pass
-		std::vector<std::array<glm::mat4, 2>> mainPassMatrices;
 		for (const auto& m : modelMatrices) {
-			mainPassMatrices.push_back({ viewProjectionCamera * m, m });
+			pushConstants.appendDrawcall(std::array<glm::mat4, 2>{ viewProjectionCamera * m, m });
 		}
 
 		VolumetricSettings volumeSettings;
@@ -648,28 +651,30 @@ int main(int argc, const char** argv) {
 		volumeSettings.absorptionCoefficient    = absorptionColor * absorptionDensity;
 		volumeSettings.ambientLight             = volumetricAmbient;
 		volumetricSettingsBuffer.fill({ volumeSettings });
-
-		const vkcv::PushConstantData            pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4));
+		
 		const std::vector<vkcv::ImageHandle>    renderTargets = { colorBuffer, depthBuffer };
 
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
 			forwardPass,
 			forwardPipeline,
-			pushConstantData,
+			pushConstants,
 			drawcalls,
 			renderTargets);
 
 		if (renderVoxelVis) {
 			voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip);
 		}
+		
+		vkcv::PushConstants skySettingsPushConstants (sizeof(skySettings));
+		skySettingsPushConstants.appendDrawcall(skySettings);
 
 		// sky
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
 			skyPass,
 			skyPipe,
-			vkcv::PushConstantData((void*)&skySettings, sizeof(skySettings)),
+			skySettingsPushConstants,
 			{ vkcv::DrawcallInfo(vkcv::Mesh({}, nullptr, 3), {}) },
 			renderTargets);
 
@@ -692,7 +697,7 @@ int main(int argc, const char** argv) {
 					resolvePipeline,
 					fulsscreenDispatchCount,
 					{ vkcv::DescriptorSetUsage(0, core.getDescriptorSet(resolveDescriptorSet).vulkanHandle) },
-					vkcv::PushConstantData(nullptr, 0));
+					vkcv::PushConstants(0));
 
 				core.recordImageMemoryBarrier(cmdStream, resolvedColorBuffer);
 			}
@@ -710,12 +715,15 @@ int main(int argc, const char** argv) {
 		auto timeSinceStart = std::chrono::duration_cast<std::chrono::microseconds>(end - appStartTime);
 		float timeF         = static_cast<float>(timeSinceStart.count()) * 0.01;
 
+		vkcv::PushConstants timePushConstants (sizeof(timeF));
+		timePushConstants.appendDrawcall(timeF);
+		
 		core.recordComputeDispatchToCmdStream(
 			cmdStream, 
 			tonemappingPipeline, 
 			fulsscreenDispatchCount,
 			{ vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptorSet).vulkanHandle) },
-			vkcv::PushConstantData(&timeF, sizeof(timeF)));
+			timePushConstants);
 
 		// present and end
 		core.prepareSwapchainImageForPresent(cmdStream);
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 352a1cf62eabe55ce1bbf2f53a6b5a4bd6e91753..3af359e7fa302e01e5c42664911ab90c9e9247cc 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -232,7 +232,7 @@ namespace vkcv
 		const CommandStreamHandle       cmdStreamHandle,
 		const PassHandle                renderpassHandle, 
 		const PipelineHandle            pipelineHandle, 
-        const PushConstantData          &pushConstantData,
+        const PushConstants             &pushConstants,
         const std::vector<DrawcallInfo> &drawcalls,
 		const std::vector<ImageHandle>  &renderTargets) {
 
@@ -345,7 +345,7 @@ namespace vkcv
             }
 
             for (int i = 0; i < drawcalls.size(); i++) {
-                recordDrawcall(drawcalls[i], cmdBuffer, pipelineLayout, pushConstantData, i);
+                recordDrawcall(drawcalls[i], cmdBuffer, pipelineLayout, pushConstants, i);
             }
 
             cmdBuffer.endRenderPass();
@@ -364,7 +364,7 @@ namespace vkcv
 		PipelineHandle computePipeline,
 		const uint32_t dispatchCount[3],
 		const std::vector<DescriptorSetUsage>& descriptorSetUsages,
-		const PushConstantData& pushConstantData) {
+		const PushConstants& pushConstants) {
 
 		auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
 
@@ -379,13 +379,13 @@ namespace vkcv
 					{ usage.vulkanHandle },
 					{});
 			}
-			if (pushConstantData.sizePerDrawcall > 0) {
+			if (pushConstants.getSizePerDrawcall() > 0) {
 				cmdBuffer.pushConstants(
 					pipelineLayout,
 					vk::ShaderStageFlagBits::eCompute,
 					0,
-					pushConstantData.sizePerDrawcall,
-					pushConstantData.data);
+					pushConstants.getSizePerDrawcall(),
+					pushConstants.getData());
 			}
 			cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]);
 		};
diff --git a/src/vkcv/DrawcallRecording.cpp b/src/vkcv/DrawcallRecording.cpp
index e6ea18588c251b5e49f454618a5ac9962cc8a264..cae0b3226e5e6bc424123d5bfa21b98a73cb3951 100644
--- a/src/vkcv/DrawcallRecording.cpp
+++ b/src/vkcv/DrawcallRecording.cpp
@@ -6,7 +6,7 @@ namespace vkcv {
         const DrawcallInfo      &drawcall,
         vk::CommandBuffer       cmdBuffer,
         vk::PipelineLayout      pipelineLayout,
-        const PushConstantData  &pushConstantData,
+        const PushConstants     &pushConstants,
         const size_t            drawcallIndex) {
 
         for (uint32_t i = 0; i < drawcall.mesh.vertexBufferBindings.size(); i++) {
@@ -23,17 +23,15 @@ namespace vkcv {
                 nullptr);
         }
 
-        const size_t drawcallPushConstantOffset = drawcallIndex * pushConstantData.sizePerDrawcall;
-        // char* cast because void* does not support pointer arithmetic
-        const void* drawcallPushConstantData = drawcallPushConstantOffset + (char*)pushConstantData.data;
+        const size_t drawcallPushConstantOffset = drawcallIndex * pushConstants.getSizePerDrawcall();
 
-        if (pushConstantData.data && pushConstantData.sizePerDrawcall > 0) {
+        if (pushConstants.getSizePerDrawcall() > 0) {
             cmdBuffer.pushConstants(
                 pipelineLayout,
                 vk::ShaderStageFlagBits::eAll,
                 0,
-                pushConstantData.sizePerDrawcall,
-                drawcallPushConstantData);
+				pushConstants.getSizePerDrawcall(),
+                pushConstants.getDrawcallData(drawcallIndex));
         }
 
         if (drawcall.mesh.indexBuffer) {