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, ®ion); + }, + [&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 {