diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp
index 52056d9f0441f807aa802ef3d29b31e2129b605c..40821e68569397fce497426b66991fc38bb29859 100644
--- a/include/vkcv/Buffer.hpp
+++ b/include/vkcv/Buffer.hpp
@@ -95,7 +95,31 @@ namespace vkcv {
 		 */
 		void fill(const std::vector<T>& vector,
 				  size_t offset = 0) {
-			fill( static_cast<const T*>(vector.data()), static_cast<size_t>(vector.size()), offset);
+			fill(static_cast<const T*>(vector.data()), static_cast<size_t>(vector.size()), offset);
+		}
+		
+		/**
+		 * @brief Reads the #Buffer directly into a data pointer of type T.
+		 *
+		 * @param[in] data Pointer to the array of object type T
+		 * @param[in] count The number of objects to copy from the buffer
+		 * @param[in] offset The offset into the #Buffer where the data is copied from
+		 */
+		void read(T* data,
+				  size_t count = 0,
+				  size_t offset = 0) {
+			m_manager->readBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T));
+		}
+		
+		/**
+		 * @brief Reads the #Buffer directly to a vector of type T.
+		 *
+		 * @param vector Vector of type T to be copied into from the #Buffer
+		 * @param offset The offset into the #Buffer where the data is copied from
+		 */
+		void read(std::vector<T>& vector,
+				  size_t offset = 0) {
+			read(static_cast<T*>(vector.data()), static_cast<size_t>(vector.size()), offset);
 		}
 		
 		/**
@@ -163,14 +187,16 @@ namespace vkcv {
 								BufferType type,
 								size_t count,
 								BufferMemoryType memoryType,
-								bool supportIndirect) {
+								bool supportIndirect,
+								bool readable) {
 			return Buffer<T>(
 				manager,
 				manager->createBuffer(
 					type,
 					count * sizeof(T),
 					memoryType,
-					supportIndirect
+					supportIndirect,
+					readable
 				),
 				type,
 				count,
diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp
index 3ac685b06068c4581202f5371f8dafab5445ecf1..8da11cfea885471f490836bf24c8b5169d1ce401 100644
--- a/include/vkcv/BufferManager.hpp
+++ b/include/vkcv/BufferManager.hpp
@@ -86,12 +86,14 @@ namespace vkcv
 		 * @param[in] size Size of buffer in bytes
 		 * @param[in] memoryType Type of buffers memory
 		 * @param[in] supportIndirect Support of indirect usage
+		 * @param[in] readable Support read functionality
 		 * @return New buffer handle
 		 */
 		BufferHandle createBuffer(BufferType type,
 								  size_t size,
 								  BufferMemoryType memoryType,
-								  bool supportIndirect);
+								  bool supportIndirect,
+								  bool readable);
 		
 		/**
 		 * @brief Returns the Vulkan buffer handle of a buffer
@@ -137,6 +139,20 @@ namespace vkcv
 						size_t size,
 						size_t offset);
 		
+		/**
+		 * @brief Reads from a buffer represented by a given
+		 * buffer handle to some data pointer.
+		 *
+		 * @param[in] handle Buffer handle
+		 * @param[in] data Pointer to data
+		 * @param[in] size Size of data to read in bytes
+		 * @param[in] offset Offset to read from buffer in bytes
+		 */
+		void readBuffer(const BufferHandle& handle,
+						void* data,
+						size_t size,
+						size_t offset);
+		
 		/**
 		 * @brief Maps memory to a buffer represented by a given
 		 * buffer handle and returns it.
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index a11db52fdc2c6a8bdc1668110539dffb9c23e6de..be7cb6097acf2aa163c6e1ec8218a793caa6bd73 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -206,8 +206,19 @@ namespace vkcv
             * return Buffer-Object
             */
         template<typename T>
-        Buffer<T> createBuffer(vkcv::BufferType type, size_t count, BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL, bool supportIndirect = false) {
-        	return Buffer<T>::create(m_BufferManager.get(), type, count, memoryType, supportIndirect);
+        Buffer<T> createBuffer(vkcv::BufferType type,
+							   size_t count,
+							   BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL,
+							   bool supportIndirect = false,
+							   bool readable = false) {
+        	return Buffer<T>::create(
+					m_BufferManager.get(),
+					type,
+					count,
+					memoryType,
+					supportIndirect,
+					readable
+			);
         }
         
         /**
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index fe096114ead0b66a6d919a4d49baa791debc4897..2868a4cc8901a346e5ee723444ace1ec83ae0b25 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -259,7 +259,6 @@ int main(int argc, const char** argv) {
 	std::vector<vkcv::Image> sceneImages;
 	
 	vkcv::algorithm::SinglePassDownsampler spdDownsampler (core, colorSampler);
-	vkcv::Downsampler &downsampler = core.getDownsampler();
 	
 	auto mipStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index 2d29719f4a34d305f90c1b76db6b56277eb3def5..e1b1a5097be2bd088d1ec1bd39b78695331899f3 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -19,7 +19,13 @@ namespace vkcv {
 			return;
 		}
 		
-		m_stagingBuffer = createBuffer(BufferType::STAGING, 1024 * 1024, BufferMemoryType::HOST_VISIBLE, false);
+		m_stagingBuffer = createBuffer(
+				BufferType::STAGING,
+				1024 * 1024,
+				BufferMemoryType::HOST_VISIBLE,
+				false,
+				false
+		);
 	}
 	
 	BufferManager::~BufferManager() noexcept {
@@ -28,7 +34,11 @@ namespace vkcv {
 		}
 	}
 	
-	BufferHandle BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType, bool supportIndirect) {
+	BufferHandle BufferManager::createBuffer(BufferType type,
+											 size_t size,
+											 BufferMemoryType memoryType,
+											 bool supportIndirect,
+											 bool readable) {
 		vk::BufferCreateFlags createFlags;
 		vk::BufferUsageFlags usageFlags;
 		
@@ -43,7 +53,8 @@ namespace vkcv {
 				usageFlags = vk::BufferUsageFlagBits::eStorageBuffer;
 				break;
 			case BufferType::STAGING:
-				usageFlags = vk::BufferUsageFlagBits::eTransferSrc;
+				usageFlags = vk::BufferUsageFlagBits::eTransferSrc |
+							 vk::BufferUsageFlagBits::eTransferDst;
 				break;
 			case BufferType::INDEX:
 				usageFlags = vk::BufferUsageFlagBits::eIndexBuffer;
@@ -59,8 +70,14 @@ namespace vkcv {
 		if (memoryType == BufferMemoryType::DEVICE_LOCAL) {
 			usageFlags |= vk::BufferUsageFlagBits::eTransferDst;
 		}
-		if (supportIndirect)
+		
+		if (supportIndirect) {
 			usageFlags |= vk::BufferUsageFlagBits::eIndirectBuffer;
+		}
+		
+		if (readable) {
+			usageFlags |= vk::BufferUsageFlagBits::eTransferSrc;
+		}
 		
 		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 		
@@ -112,9 +129,9 @@ namespace vkcv {
 	}
 	
 	/**
-	 * @brief Structure to store details required for a staging process.
+	 * @brief Structure to store details required for a write staging process.
 	 */
-	struct StagingStepInfo {
+	struct StagingWriteInfo {
 		const void* data;
 		size_t size;
 		size_t offset;
@@ -137,7 +154,7 @@ namespace vkcv {
 	 * @param core Core instance
 	 * @param info Staging-info structure
 	 */
-	void copyFromStagingBuffer(Core* core, StagingStepInfo& info) {
+	static void fillFromStagingBuffer(Core* core, StagingWriteInfo& info) {
 		const size_t remaining = info.size - info.stagingPosition;
 		const size_t mapped_size = std::min(remaining, info.stagingLimit);
 		
@@ -165,7 +182,70 @@ namespace vkcv {
 					if (mapped_size < remaining) {
 						info.stagingPosition += mapped_size;
 						
-						copyFromStagingBuffer(
+						fillFromStagingBuffer(
+								core,
+								info
+						);
+					}
+				}
+		);
+	}
+	
+	/**
+	 * @brief Structure to store details required for a read staging process.
+	 */
+	struct StagingReadInfo {
+		void* data;
+		size_t size;
+		size_t offset;
+		
+		vk::Buffer buffer;
+		vk::Buffer stagingBuffer;
+		vma::Allocation stagingAllocation;
+		
+		size_t stagingLimit;
+		size_t stagingPosition;
+	};
+	
+	/**
+	 * Copies data from a staging buffer to CPU and submits the commands to copy
+	 * each part one after another into the actual target data pointer.
+	 *
+	 * The function can be used fully asynchronously!
+	 * Just be careful to not use the staging buffer in parallel!
+	 *
+	 * @param core Core instance
+	 * @param info Staging-info structure
+	 */
+	static void readToStagingBuffer(Core* core, StagingReadInfo& info) {
+		const size_t remaining = info.size - info.stagingPosition;
+		const size_t mapped_size = std::min(remaining, info.stagingLimit);
+		
+		SubmitInfo submitInfo;
+		submitInfo.queueType = QueueType::Transfer;
+		
+		core->recordAndSubmitCommandsImmediate(
+				submitInfo,
+				[&info, &mapped_size](const vk::CommandBuffer& commandBuffer) {
+					const vk::BufferCopy region (
+							info.offset + info.stagingPosition,
+							0,
+							mapped_size
+					);
+					
+					commandBuffer.copyBuffer(info.buffer, info.stagingBuffer, 1, &region);
+				},
+				[&core, &info, &mapped_size, &remaining]() {
+					const vma::Allocator& allocator = core->getContext().getAllocator();
+					
+					const void* mapped = allocator.mapMemory(info.stagingAllocation);
+					memcpy(reinterpret_cast<char*>(info.data) + info.stagingPosition, mapped, mapped_size);
+					allocator.unmapMemory(info.stagingAllocation);
+					
+					if (mapped_size < remaining) {
+						info.stagingPosition += mapped_size;
+						
+						readToStagingBuffer(
 								core,
 								info
 						);
@@ -216,7 +296,10 @@ namespace vkcv {
 		return info.deviceMemory;
 	}
 	
-	void BufferManager::fillBuffer(const BufferHandle& handle, const void *data, size_t size, size_t offset) {
+	void BufferManager::fillBuffer(const BufferHandle& handle,
+								   const void *data,
+								   size_t size,
+								   size_t offset) {
 		const uint64_t id = handle.getId();
 		
 		if (size == 0) {
@@ -244,9 +327,56 @@ namespace vkcv {
 		} else {
 			auto& stagingBuffer = m_buffers[ m_stagingBuffer.getId() ];
 			
-			StagingStepInfo info;
+			StagingWriteInfo info;
+			info.data = data;
+			info.size = max_size;
+			info.offset = offset;
+			
+			info.buffer = buffer.m_handle;
+			info.stagingBuffer = stagingBuffer.m_handle;
+			info.stagingAllocation = stagingBuffer.m_allocation;
+			
+			info.stagingLimit = stagingBuffer.m_size;
+			info.stagingPosition = 0;
+			
+			fillFromStagingBuffer(m_core, info);
+		}
+	}
+	
+	void BufferManager::readBuffer(const BufferHandle &handle,
+								   void *data,
+								   size_t size,
+								   size_t offset) {
+		const uint64_t id = handle.getId();
+		
+		if (size == 0) {
+			size = SIZE_MAX;
+		}
+		
+		if (id >= m_buffers.size()) {
+			return;
+		}
+		
+		auto& buffer = m_buffers[id];
+		
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		
+		if (offset > buffer.m_size) {
+			return;
+		}
+		
+		const size_t max_size = std::min(size, buffer.m_size - offset);
+		
+		if (buffer.m_mappable) {
+			const void* mapped = allocator.mapMemory(buffer.m_allocation);
+			memcpy(data, reinterpret_cast<const char*>(mapped) + offset, max_size);
+			allocator.unmapMemory(buffer.m_allocation);
+		} else {
+			auto& stagingBuffer = m_buffers[ m_stagingBuffer.getId() ];
+			
+			StagingReadInfo info;
 			info.data = data;
-			info.size = std::min(size, max_size - offset);
+			info.size = max_size;
 			info.offset = offset;
 			
 			info.buffer = buffer.m_handle;
@@ -256,7 +386,7 @@ namespace vkcv {
 			info.stagingLimit = stagingBuffer.m_size;
 			info.stagingPosition = 0;
 			
-			copyFromStagingBuffer(m_core, info);
+			readToStagingBuffer(m_core, info);
 		}
 	}
 	
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index d37e293b48c2c6fc39cab6afd9cfc5054aebcb73..585c1faf554060ccd0cdc2e6158ddffb2a11951e 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -512,7 +512,11 @@ namespace vkcv {
 		const size_t max_size = std::min(size, image_size);
 		
 		BufferHandle bufferHandle = m_bufferManager.createBuffer(
-				BufferType::STAGING, max_size, BufferMemoryType::DEVICE_LOCAL, false
+				BufferType::STAGING,
+				max_size,
+				BufferMemoryType::DEVICE_LOCAL,
+				false,
+				false
 		);
 		
 		m_bufferManager.fillBuffer(bufferHandle, data, max_size, 0);