From 2e5597442fc4bc7ac156b36fea6677e1dccc79d4 Mon Sep 17 00:00:00 2001 From: Johannes Unterguggenberger Date: Fri, 31 May 2024 14:03:25 +0200 Subject: [PATCH] Added proper documentation to buffer_t::read_into and to buffer_t::read --- include/avk/buffer.hpp | 58 ++++++++++++++++++---------------- src/avk.cpp | 70 ++++++++++-------------------------------- 2 files changed, 48 insertions(+), 80 deletions(-) diff --git a/include/avk/buffer.hpp b/include/avk/buffer.hpp index c6f5a8b..ddb4722 100644 --- a/include/avk/buffer.hpp +++ b/include/avk/buffer.hpp @@ -226,19 +226,6 @@ namespace avk */ command::action_type_command fill(const void* aDataPtr, size_t aMetaDataIndex) const; - // TODO: Maybe the following overload could be re-enabled after command/commands refactoring?! - ///** Fill buffer with data according to the meta data of the given type Meta. - // * The buffer's size is determined from its metadata - // * @param aDataPtr Pointer to the data to copy to the buffer. MUST point to at least enough data to fill the buffer entirely. - // * @param aMetaDataSkip How often a meta data of type Meta shall be skipped. I.e. values != 0 only make sense if there ar multiple meta data entries of type Meta. - // */ - //template - //std::optional fill(const void* aDataPtr, size_t aMetaDataSkip, old_sync aSyncHandler) - //{ - // assert(has_meta(aMetaDataSkip)); - // return fill(aDataPtr, index_of_meta(aMetaDataSkip), std::move(aSyncHandler)); - //} - /** Fill buffer partially with data. * * @param aDataPtr Pointer to the data to copy to the buffer @@ -248,23 +235,42 @@ namespace avk */ command::action_type_command fill(const void* aDataPtr, size_t aMetaDataIndex, size_t aOffsetInBytes, size_t aDataSizeInBytes) const; - /** Read data from buffer back to the CPU-side, into some given memory. - * @param aDataPtr Target memory where to write read-back data into - * @param aMetaDataIndex Index of the meta data index which is used for the buffer's read-back data (size and stuff) - */ + /** Reads values from a buffer back into some host-side memory. + * @param aDataPtr Where to store the read-back memory into. + * @param aMetaDataIndex Which meta data index shall be used to determine the data size to be read back. + * @return An avk::command is returned which you, generally, must send to a queue to be executed. + * It could be that the returned command is empty. This will happen if the buffer's memory + * is stored in a host visible memory region. + * + * @example Read an uint64_t back to host memory from a buffer that is backed by device-local memory, + * and wait with a fence until the operation has completed: + * + * avk::buffer mMyBuffer = ...; + * uint32_t mMyReadBackData; + * context().record_and_submit_with_fence({ + * mMyBuffer->read_into(&mMyReadBackData, 0) + * }, *mQueue)->wait_until_signalled(); + */ avk::command::action_type_command read_into(void* aDataPtr, size_t aMetaDataIndex) const; /** - * Read back data from a buffer. - * - * This is a convenience overload to avk::read. - * - * Example usage: - * uint32_t readData = avk::read(mMySsbo, avk::old_sync::not_required()); - * // ^ given that mMySsbo is a host-coherent buffer. If it is not, sync is required. + * Read back data from a buffer that is backed by host-visible memory. + * This is a convenience overload to avk::read, and is mostly intended to be used for small amounts of data, + * because the data container is allocated on the stack and returned to the caller. + * Technically, every data type that can be copy-constructed is fine. + * Attention: This does not support read backs from buffers backed by device-local memory, since the + * result of avk::read_into is discarded. * - * @tparam Ret Specify the type of data that shall be read from the buffer (this is `uint32_t` in the example above). - * @returns A value of type `Ret` which is returned by value. + * @tparam Ret Specify the type of data that shall be read from the buffer + * @param aMetaDataIndex Which meta data index shall be used to determine the data size to be read back. + * @return The value that has been read back from the buffer's host visible memory. + * The value's size is of size `Ret` and is returned by value. + * + * @example Read back one uint32_t value: + * + * avk::buffer mMyBuffer = ...; + * uint32_t myData = mMyBuffer->read(0); + * // ^ given that mMyBuffer is a buffer backed by host visible memory and hence, does not require commands to be executed. */ template [[nodiscard]] Ret read(size_t aMetaDataIndex) { diff --git a/src/avk.cpp b/src/avk.cpp index 58b0757..32cdcd9 100644 --- a/src/avk.cpp +++ b/src/avk.cpp @@ -2683,60 +2683,22 @@ namespace avk } } - //std::optional buffer_t::fill(const void* aDataPtr, size_t aMetaDataIndex) - //{ - // return commands{ pipeline_stage::transfer, memory_access::transfer_read_access, [this](command_buffer_t& aCmdBfr) { - // auto metaData = meta_at_index(aMetaDataIndex); - // auto bufferSize = static_cast(metaData.total_size()); - // auto memProps = memory_properties(); - - // // #1: Is our memory accessible from the CPU-SIDE? - // if (avk::has_flag(memProps, vk::MemoryPropertyFlagBits::eHostVisible)) { - // auto mapped = scoped_mapping{mBuffer, mapping_access::write}; - // memcpy(mapped.get(), aDataPtr, bufferSize); - // return {}; - // } - - // // #2: Otherwise, it must be on the GPU-SIDE! - // else { - // assert(avk::has_flag(memProps, vk::MemoryPropertyFlagBits::eDeviceLocal)); - - // // We have to create a (somewhat temporary) staging buffer and transfer it to the GPU - // // "somewhat temporary" means that it can not be deleted in this function, but only - // // after the transfer operation has completed => handle via sync - // auto stagingBuffer = root::create_buffer( - // mPhysicalDevice, mDevice, mBuffer.allocator(), - // AVK_STAGING_BUFFER_MEMORY_USAGE, - // vk::BufferUsageFlagBits::eTransferSrc, - // generic_buffer_meta::create_from_size(bufferSize) - // ); - // stagingBuffer->fill(aDataPtr, 0, old_sync::wait_idle()); // Recurse into the other if-branch - - // auto& commandBuffer = aSyncHandler.get_or_create_command_buffer(); - // // Sync before: - // aSyncHandler.establish_barrier_before_the_operation(pipeline_stage::transfer, read_memory_access{memory_access::transfer_read_access}); - - // // Operation: - // auto copyRegion = vk::BufferCopy{} - // .setSrcOffset(0u) // TODO: Support different offsets or whatever?! - // .setDstOffset(0u) - // .setSize(bufferSize); - // commandBuffer.handle().copyBuffer(stagingBuffer->handle(), handle(), { copyRegion }); - - // // Sync after: - // aSyncHandler.establish_barrier_after_the_operation(pipeline_stage::transfer, write_memory_access{memory_access::transfer_write_access}); - - // // Take care of the lifetime handling of the stagingBuffer, it might still be in use: - // commandBuffer.set_custom_deleter([ - // lOwnedStagingBuffer{ std::move(stagingBuffer) } - // ]() { /* Nothing to do here, the buffers' destructors will do the cleanup, the lambda is just storing it. */ }); - // - // // Finish him: - // return aSyncHandler.submit_and_sync(); - // } - // }, pipeline_stage::transfer, memory_access::transfer_write_access); - //} - + /* Reads values from a buffer back into some host-side memory. + * @param aDataPtr Where to store the read-back memory into. + * @param aMetaDataIndex Which meta data index shall be used to determine the data size to be read back. + * @return An avk::command is returned which you, generally, must send to a queue to be executed. + * It could be that the returned command is empty. This will happen if the buffer's memory + * is stored in a host visible memory region. + * + * @example Read an uint64_t back to host memory from a buffer that is backed by device-local memory, + * and wait with a fence until the operation has completed: + * + * avk::buffer mMyBuffer = ...; + * uint32_t mMyReadBackData; + * context().record_and_submit_with_fence({ + * mMyBuffer->read_into(&mMyReadBackData, 0) + * }, *mQueue)->wait_until_signalled(); + */ avk::command::action_type_command buffer_t::read_into(void* aDataPtr, size_t aMetaDataIndex) const { auto metaData = meta_at_index(aMetaDataIndex);