diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp
index a6926e69eaff75ade2a820d254dd81bc7a1c6059..5c21b1ce1d2d84afad6816fd8bf5f66a82b80941 100644
--- a/include/vkcv/Buffer.hpp
+++ b/include/vkcv/Buffer.hpp
@@ -26,11 +26,11 @@ namespace vkcv {
 			return m_count * sizeof(T);
 		}
 		
-		void fill(T* data, size_t count = SIZE_MAX, size_t offset = 0) {
+		void fill(T* data, size_t count = 0, size_t offset = 0) {
 			 m_manager->fillBuffer(m_handle_id, data, count * sizeof(T), offset * sizeof(T));
 		}
 		
-		T* map(size_t offset = 0, size_t count = SIZE_MAX) {
+		T* map(size_t offset = 0, size_t count = 0) {
 			return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle_id, offset * sizeof(T), count * sizeof(T)));
 		}
 
diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp
index 0f6be9a5257dfa69acc106beb90bc1f36b04667f..eb8cb178d1b844fff57892c7312ed71d325900cc 100644
--- a/include/vkcv/BufferManager.hpp
+++ b/include/vkcv/BufferManager.hpp
@@ -5,13 +5,14 @@
 
 namespace vkcv
 {
-	enum BufferType {
+	enum class BufferType {
 		VERTEX,
 		UNIFORM,
-		STORAGE
+		STORAGE,
+		STAGING
 	};
 	
-	enum BufferMemoryType {
+	enum class BufferMemoryType {
 		DEVICE_LOCAL,
 		HOST_VISIBLE
 	};
@@ -27,14 +28,19 @@ namespace vkcv
 		{
 			vk::Buffer m_handle;
 			vk::DeviceMemory m_memory;
+			size_t m_size;
 			void* m_mapped = nullptr;
+			bool m_mappable;
 		};
 		
 		Core* m_core;
 		std::vector<Buffer> m_buffers;
+		uint64_t m_stagingBuffer;
 		
 		BufferManager() noexcept;
 		
+		void init();
+		
 	public:
 		~BufferManager() noexcept;
 		
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index fbb6484c989a8b22e04ad2d27d6acf623472105b..dd144d5cd0a05e4a581af6c576680d29ea2b0a9d 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -36,13 +36,22 @@ int main(int argc, const char** argv) {
 		float x, y, z;
 	};
 	
-	auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, 3, vkcv::BufferMemoryType::HOST_VISIBLE);
+	const size_t n = 5027;
 	
-	vec3* m = buffer.map();
+	auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL);
+	vec3 vec_data [n];
+	
+	for (size_t i = 0; i < n; i++) {
+		vec_data[i] = { 42, static_cast<float>(i), 7 };
+	}
+	
+	buffer.fill(vec_data);
+	
+	/*vec3* m = buffer.map();
 	m[0] = { 0, 0, 0 };
 	m[1] = { 0, 0, 0 };
 	m[2] = { 0, 0, 0 };
-	buffer.unmap();
+	buffer.unmap();*/
 
 	std::cout << "Physical device: " << physicalDevice.getProperties().deviceName << std::endl;
 
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index 4e3bc245371d107dd188792d08886d9b80fa41ee..cb2425a51ee177d062120b0eb61ae089f6f1692f 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -9,8 +9,17 @@
 namespace vkcv {
 	
 	BufferManager::BufferManager() noexcept :
-		m_core(nullptr), m_buffers()
-	{}
+		m_core(nullptr), m_buffers(), m_stagingBuffer(UINT64_MAX)
+	{
+	}
+	
+	void BufferManager::init() {
+		if (!m_core) {
+			return;
+		}
+		
+		m_stagingBuffer = createBuffer(BufferType::STAGING, 1024 * 1024, BufferMemoryType::HOST_VISIBLE);
+	}
 	
 	BufferManager::~BufferManager() noexcept {
 		for (size_t id = 0; id < m_buffers.size(); id++) {
@@ -48,20 +57,27 @@ namespace vkcv {
 		vk::BufferUsageFlags usageFlags;
 		
 		switch (type) {
-			case VERTEX:
+			case BufferType::VERTEX:
 				usageFlags = vk::BufferUsageFlagBits::eVertexBuffer;
 				break;
-			case UNIFORM:
+			case BufferType::UNIFORM:
 				usageFlags = vk::BufferUsageFlagBits::eUniformBuffer;
 				break;
-			case STORAGE:
+			case BufferType::STORAGE:
 				usageFlags = vk::BufferUsageFlagBits::eStorageBuffer;
 				break;
+			case BufferType::STAGING:
+				usageFlags = vk::BufferUsageFlagBits::eTransferSrc;
+				break;
 			default:
 				// TODO: maybe an issue
 				break;
 		}
 		
+		if (memoryType == BufferMemoryType::DEVICE_LOCAL) {
+			usageFlags |= vk::BufferUsageFlagBits::eTransferDst;
+		}
+		
 		const vk::Device& device = m_core->getContext().getDevice();
 		
 		vk::Buffer buffer = device.createBuffer(
@@ -72,13 +88,15 @@ namespace vkcv {
 		const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice();
 		
 		vk::MemoryPropertyFlags memoryTypeFlags;
+		bool mappable = false;
 		
 		switch (memoryType) {
-			case DEVICE_LOCAL:
+			case BufferMemoryType::DEVICE_LOCAL:
 				memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal;
 				break;
-			case HOST_VISIBLE:
+			case BufferMemoryType::HOST_VISIBLE:
 				memoryTypeFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
+				mappable = true;
 				break;
 			default:
 				// TODO: maybe an issue
@@ -91,14 +109,80 @@ namespace vkcv {
 				memoryTypeFlags
 		);
 		
-		vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryType));
+		vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex));
+		
+		device.bindBufferMemory(buffer, memory, 0);
 		
 		const uint64_t id = m_buffers.size();
-		m_buffers.push_back({ buffer, memory, nullptr });
+		m_buffers.push_back({ buffer, memory, size, nullptr, mappable });
 		return id;
 	}
 	
+	struct StagingStepInfo {
+		void* data;
+		size_t size;
+		size_t offset;
+		
+		vk::Buffer buffer;
+		vk::Buffer stagingBuffer;
+		vk::DeviceMemory stagingMemory;
+		
+		size_t stagingLimit;
+		size_t stagingPosition;
+	};
+	
+	/**
+	 * Copies data from CPU to a staging buffer and submits the commands to copy
+	 * each part one after another into the actual target buffer.
+	 *
+	 * 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
+	 */
+	void copyFromStagingBuffer(Core* core, StagingStepInfo& info) {
+		const size_t remaining = info.size - info.stagingPosition;
+		const size_t mapped_size = std::min(remaining, info.stagingLimit);
+		
+		const vk::Device& device = core->getContext().getDevice();
+		
+		void* mapped = device.mapMemory(info.stagingMemory, 0, mapped_size);
+		memcpy(mapped, reinterpret_cast<char*>(info.data) + info.stagingPosition, mapped_size);
+		device.unmapMemory(info.stagingMemory);
+		
+		SubmitInfo submitInfo;
+		submitInfo.queueType = QueueType::Transfer;
+		
+		core->submitCommands(
+				submitInfo,
+				[&info, &mapped_size](const vk::CommandBuffer& commandBuffer) {
+					const vk::BufferCopy region (
+							0,
+							info.offset + info.stagingPosition,
+							mapped_size
+					);
+					
+					commandBuffer.copyBuffer(info.stagingBuffer, info.buffer, 1, &region);
+				},
+				[&core, &info, &mapped_size, &remaining]() {
+					if (mapped_size < remaining) {
+						info.stagingPosition += mapped_size;
+						
+						copyFromStagingBuffer(
+								core,
+								info
+						);
+					}
+				}
+		);
+	}
+	
 	void BufferManager::fillBuffer(uint64_t id, void *data, size_t size, size_t offset) {
+		if (size == 0) {
+			size = SIZE_MAX;
+		}
+		
 		if (id >= m_buffers.size()) {
 			return;
 		}
@@ -111,19 +195,42 @@ namespace vkcv {
 		
 		const vk::Device& device = m_core->getContext().getDevice();
 		
-		const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer.m_handle);
-		
-		if (offset > requirements.size) {
+		if (offset > buffer.m_size) {
 			return;
 		}
 		
-		const size_t mapped_size = std::min(size, requirements.size - offset);
-		void* mapped = device.mapMemory(buffer.m_memory, offset, mapped_size);
-		memcpy(mapped, data, mapped_size);
-		device.unmapMemory(buffer.m_memory);
+		const size_t max_size = std::min(size, buffer.m_size - offset);
+		
+		if (buffer.m_mappable) {
+			void* mapped = device.mapMemory(buffer.m_memory, offset, max_size);
+			memcpy(mapped, data, max_size);
+			device.unmapMemory(buffer.m_memory);
+		} else {
+			auto& stagingBuffer = m_buffers[m_stagingBuffer];
+			
+			StagingStepInfo info;
+			info.data = data;
+			info.size = std::min(size, max_size - offset);
+			info.offset = offset;
+			
+			info.buffer = buffer.m_handle;
+			info.stagingBuffer = stagingBuffer.m_handle;
+			info.stagingMemory = stagingBuffer.m_memory;
+			
+			const vk::MemoryRequirements stagingRequirements = device.getBufferMemoryRequirements(stagingBuffer.m_handle);
+			
+			info.stagingLimit = stagingRequirements.size;
+			info.stagingPosition = 0;
+			
+			copyFromStagingBuffer(m_core, info);
+		}
 	}
 	
 	void* BufferManager::mapBuffer(uint64_t id, size_t offset, size_t size) {
+		if (size == 0) {
+			size = SIZE_MAX;
+		}
+		
 		if (id >= m_buffers.size()) {
 			return nullptr;
 		}
@@ -136,14 +243,12 @@ namespace vkcv {
 		
 		const vk::Device& device = m_core->getContext().getDevice();
 		
-		const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer.m_handle);
-		
-		if (offset > requirements.size) {
+		if (offset > buffer.m_size) {
 			return nullptr;
 		}
 		
-		const size_t mapped_size = std::min(size, requirements.size - offset);
-		buffer.m_mapped = device.mapMemory(buffer.m_memory, offset, mapped_size);
+		const size_t max_size = std::min(size, buffer.m_size - offset);
+		buffer.m_mapped = device.mapMemory(buffer.m_memory, offset, max_size);
 		return buffer.m_mapped;
 	}
 	
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 4c4293cb4e2e293c70608ff4c3280f9ea358f61c..788891703505b1c802fbc37a77e42423804e7fa6 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -97,6 +97,7 @@ namespace vkcv
             m_SyncResources(syncResources)
 	{
     	m_BufferManager->m_core = this;
+    	m_BufferManager->init();
 	}
 
 	Core::~Core() noexcept {