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