From d09d93febff10a112ae5e30150cca11501ebc604 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 24 Nov 2023 13:52:48 +0100 Subject: [PATCH] Vulkan: Store Vertex, Index and Storage Buffers on Device Memory Currently all buffer types were stored in host memory, which is visible to the GPU as well. This is typically slow as the data would be transferred over the PCI bus when used. Most of the time Index and Vertex buffers are written once and read many times so it makes more sense to locate them on the GPU. Storage buffers typically require quick access as they are created for shading/compute purposes. This PR will try to store vertex buffers, index buffers and storage buffers on device memory to improve the performance. Uniform buffers are still located on host memory as they can be uploaded during binding process. This can (will) reset the graphics pipeline triggering draw calls using unattached resources. In future this could be optimized further as in: * using different pools for allocating specific buffers, with a fallback when buffers cannot be stored on the GPU anymore. * store uniform buffers in device memory Pull Request: https://projects.blender.org/blender/blender/pulls/115343 --- intern/ghost/intern/GHOST_ContextVK.cc | 1 - source/blender/gpu/CMakeLists.txt | 2 + source/blender/gpu/vulkan/vk_batch.cc | 20 +++-- source/blender/gpu/vulkan/vk_buffer.cc | 28 +++++-- source/blender/gpu/vulkan/vk_buffer.hh | 12 ++- .../blender/gpu/vulkan/vk_command_buffers.cc | 4 +- .../blender/gpu/vulkan/vk_command_buffers.hh | 2 +- source/blender/gpu/vulkan/vk_index_buffer.cc | 25 ++++-- .../blender/gpu/vulkan/vk_staging_buffer.cc | 57 ++++++++++++++ .../blender/gpu/vulkan/vk_staging_buffer.hh | 76 +++++++++++++++++++ .../blender/gpu/vulkan/vk_storage_buffer.cc | 14 +++- .../blender/gpu/vulkan/vk_uniform_buffer.cc | 20 ++++- .../gpu/vulkan/vk_vertex_attribute_object.cc | 9 +++ .../gpu/vulkan/vk_vertex_attribute_object.hh | 8 ++ source/blender/gpu/vulkan/vk_vertex_buffer.cc | 50 +++++++++--- source/blender/gpu/vulkan/vk_vertex_buffer.hh | 3 + 16 files changed, 289 insertions(+), 42 deletions(-) create mode 100644 source/blender/gpu/vulkan/vk_staging_buffer.cc create mode 100644 source/blender/gpu/vulkan/vk_staging_buffer.hh diff --git a/intern/ghost/intern/GHOST_ContextVK.cc b/intern/ghost/intern/GHOST_ContextVK.cc index 3b3095eb98d..b39295802d2 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cc +++ b/intern/ghost/intern/GHOST_ContextVK.cc @@ -999,7 +999,6 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() } extensions_device.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); extensions_device.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); - requireExtension(extensions_available, extensions_enabled, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME); /* Enable MoltenVK required instance extensions. */ #ifdef VK_MVK_MOLTENVK_EXTENSION_NAME diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 2c62739d2a6..633066904f8 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -227,6 +227,7 @@ set(VULKAN_SRC vulkan/vk_shader.cc vulkan/vk_shader_interface.cc vulkan/vk_shader_log.cc + vulkan/vk_staging_buffer.cc vulkan/vk_state_manager.cc vulkan/vk_storage_buffer.cc vulkan/vk_texture.cc @@ -266,6 +267,7 @@ set(VULKAN_SRC vulkan/vk_shader.hh vulkan/vk_shader_interface.hh vulkan/vk_shader_log.hh + vulkan/vk_staging_buffer.hh vulkan/vk_state_manager.hh vulkan/vk_storage_buffer.hh vulkan/vk_texture.hh diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index a1767724d65..1e816df82fa 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -26,20 +26,24 @@ void VKBatch::draw_setup() /* Finalize graphics pipeline */ VKContext &context = *VKContext::get(); VKStateManager &state_manager = context.state_manager_get(); - state_manager.apply_state(); - state_manager.apply_bindings(); - VKVertexAttributeObject vao; - vao.update_bindings(context, *this); - context.bind_graphics_pipeline(prim_type, vao); - - /* Bind geometry resources. */ - vao.bind(context); VKIndexBuffer *index_buffer = index_buffer_get(); const bool draw_indexed = index_buffer != nullptr; + state_manager.apply_state(); + state_manager.apply_bindings(); + /* + * The next statements are order dependent. VBOs and IBOs must be uploaded, before resources can + * be bound. Uploading device located buffers flush the graphics pipeline and already bound + * resources will be unbound. + */ + VKVertexAttributeObject vao; + vao.update_bindings(context, *this); + vao.ensure_vbos_uploaded(); if (draw_indexed) { index_buffer->upload_data(); index_buffer->bind(context); } + vao.bind(context); + context.bind_graphics_pipeline(prim_type, vao); } void VKBatch::draw(int vertex_first, int vertex_count, int instance_first, int instance_count) diff --git a/source/blender/gpu/vulkan/vk_buffer.cc b/source/blender/gpu/vulkan/vk_buffer.cc index ce8fc69c0c1..e446707dc57 100644 --- a/source/blender/gpu/vulkan/vk_buffer.cc +++ b/source/blender/gpu/vulkan/vk_buffer.cc @@ -28,12 +28,11 @@ static VmaAllocationCreateFlags vma_allocation_flags(GPUUsageType usage) { switch (usage) { case GPU_USAGE_STATIC: + case GPU_USAGE_DEVICE_ONLY: + return 0; case GPU_USAGE_DYNAMIC: case GPU_USAGE_STREAM: return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; - case GPU_USAGE_DEVICE_ONLY: - return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | - VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; case GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY: break; } @@ -41,7 +40,21 @@ static VmaAllocationCreateFlags vma_allocation_flags(GPUUsageType usage) return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; } -bool VKBuffer::create(int64_t size_in_bytes, GPUUsageType usage, VkBufferUsageFlags buffer_usage) +static VkMemoryPropertyFlags vma_preferred_flags(const bool is_host_visible) +{ + return is_host_visible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT : + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; +} + +/* + * TODO: Check which memory is selected and adjust the creation flag to add mapping. This way the + * staging buffer can be skipped, or in case of a vertex buffer an intermediate buffer can be + * removed. + */ +bool VKBuffer::create(int64_t size_in_bytes, + GPUUsageType usage, + VkBufferUsageFlags buffer_usage, + const bool is_host_visible) { BLI_assert(!is_allocated()); BLI_assert(vk_buffer_ == VK_NULL_HANDLE); @@ -70,6 +83,7 @@ bool VKBuffer::create(int64_t size_in_bytes, GPUUsageType usage, VkBufferUsageFl VmaAllocationCreateInfo vma_create_info = {}; vma_create_info.flags = vma_allocation_flags(usage); vma_create_info.priority = 1.0f; + vma_create_info.preferredFlags = vma_preferred_flags(is_host_visible); vma_create_info.usage = VMA_MEMORY_USAGE_AUTO; VkResult result = vmaCreateBuffer( @@ -78,8 +92,10 @@ bool VKBuffer::create(int64_t size_in_bytes, GPUUsageType usage, VkBufferUsageFl return false; } - /* All buffers are mapped to virtual memory. */ - return map(); + if (is_host_visible) { + return map(); + } + return true; } void VKBuffer::update(const void *data) const diff --git a/source/blender/gpu/vulkan/vk_buffer.hh b/source/blender/gpu/vulkan/vk_buffer.hh index b4191f4258d..fdbe4d649e4 100644 --- a/source/blender/gpu/vulkan/vk_buffer.hh +++ b/source/blender/gpu/vulkan/vk_buffer.hh @@ -31,8 +31,10 @@ class VKBuffer { /** Has this buffer been allocated? */ bool is_allocated() const; - - bool create(int64_t size, GPUUsageType usage, VkBufferUsageFlags buffer_usage); + bool create(int64_t size, + GPUUsageType usage, + VkBufferUsageFlags buffer_usage, + bool is_host_visible = true); void clear(VKContext &context, uint32_t clear_value); void update(const void *data) const; void flush() const; @@ -56,9 +58,13 @@ class VKBuffer { */ void *mapped_memory_get() const; + /** + * Is this buffer mapped (visible on host) + */ + bool is_mapped() const; + private: /** Check if this buffer is mapped. */ - bool is_mapped() const; bool map(); void unmap(); }; diff --git a/source/blender/gpu/vulkan/vk_command_buffers.cc b/source/blender/gpu/vulkan/vk_command_buffers.cc index 44c63055bf6..f9b5b53b36f 100644 --- a/source/blender/gpu/vulkan/vk_command_buffers.cc +++ b/source/blender/gpu/vulkan/vk_command_buffers.cc @@ -415,7 +415,9 @@ void VKCommandBuffers::copy(VKTexture &dst_texture, command_buffer.command_recorded(); } -void VKCommandBuffers::copy(VKBuffer &dst_buffer, VkBuffer src_buffer, Span regions) +void VKCommandBuffers::copy(const VKBuffer &dst_buffer, + VkBuffer src_buffer, + Span regions) { VKCommandBuffer &command_buffer = command_buffer_get(Type::DataTransferCompute); vkCmdCopyBuffer(command_buffer.vk_command_buffer(), diff --git a/source/blender/gpu/vulkan/vk_command_buffers.hh b/source/blender/gpu/vulkan/vk_command_buffers.hh index 4c3f19f44dd..2d08bc37493 100644 --- a/source/blender/gpu/vulkan/vk_command_buffers.hh +++ b/source/blender/gpu/vulkan/vk_command_buffers.hh @@ -95,7 +95,7 @@ class VKCommandBuffers : public NonCopyable, NonMovable { void copy(VKBuffer &dst_buffer, VKTexture &src_texture, Span regions); void copy(VKTexture &dst_texture, VKBuffer &src_buffer, Span regions); void copy(VKTexture &dst_texture, VKTexture &src_texture, Span regions); - void copy(VKBuffer &dst_buffer, VkBuffer src_buffer, Span regions); + void copy(const VKBuffer &dst_buffer, VkBuffer src_buffer, Span regions); void blit(VKTexture &dst_texture, VKTexture &src_texture, Span regions); void blit(VKTexture &dst_texture, VkImageLayout dst_layout, diff --git a/source/blender/gpu/vulkan/vk_index_buffer.cc b/source/blender/gpu/vulkan/vk_index_buffer.cc index cf02dec94cc..697e827491c 100644 --- a/source/blender/gpu/vulkan/vk_index_buffer.cc +++ b/source/blender/gpu/vulkan/vk_index_buffer.cc @@ -9,6 +9,7 @@ #include "vk_index_buffer.hh" #include "vk_shader.hh" #include "vk_shader_interface.hh" +#include "vk_staging_buffer.hh" #include "vk_state_manager.hh" namespace blender::gpu { @@ -24,10 +25,15 @@ void VKIndexBuffer::ensure_updated() allocate(); } - if (data_ != nullptr) { - buffer_.update(data_); - MEM_SAFE_FREE(data_); + if (data_ == nullptr) { + return; } + + VKContext &context = *VKContext::get(); + VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::HostToDevice); + staging_buffer.host_buffer_get().update(data_); + staging_buffer.copy_to_device(context); + MEM_SAFE_FREE(data_); } void VKIndexBuffer::upload_data() @@ -65,9 +71,9 @@ void VKIndexBuffer::bind(int binding, void VKIndexBuffer::read(uint32_t *data) const { VKContext &context = *VKContext::get(); - context.flush(); - - buffer_.read(data); + VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::DeviceToHost); + staging_buffer.copy_from_device(context); + staging_buffer.host_buffer_get().read(data); } void VKIndexBuffer::update_sub(uint /*start*/, uint /*len*/, const void * /*data*/) @@ -83,8 +89,11 @@ void VKIndexBuffer::strip_restart_indices() void VKIndexBuffer::allocate() { GPUUsageType usage = data_ == nullptr ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_STATIC; - buffer_.create( - size_get(), usage, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); + buffer_.create(size_get(), + usage, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + false); debug::object_label(buffer_.vk_handle(), "IndexBuffer"); } diff --git a/source/blender/gpu/vulkan/vk_staging_buffer.cc b/source/blender/gpu/vulkan/vk_staging_buffer.cc new file mode 100644 index 00000000000..97641701a76 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_staging_buffer.cc @@ -0,0 +1,57 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#include "vk_staging_buffer.hh" +#include "vk_command_buffers.hh" +#include "vk_context.hh" + +namespace blender::gpu { + +VKStagingBuffer::VKStagingBuffer(const VKBuffer &device_buffer, Direction direction) + : device_buffer_(device_buffer) +{ + VkBufferUsageFlags usage; + switch (direction) { + case Direction::HostToDevice: + usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + break; + case Direction::DeviceToHost: + usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + } + + host_buffer_.create(device_buffer.size_in_bytes(), GPU_USAGE_STREAM, usage, true); +} + +void VKStagingBuffer::copy_to_device(VKContext &context) +{ + BLI_assert(host_buffer_.is_allocated() && host_buffer_.is_mapped()); + VkBufferCopy buffer_copy = {}; + buffer_copy.size = device_buffer_.size_in_bytes(); + VKCommandBuffers &command_buffers = context.command_buffers_get(); + command_buffers.copy( + device_buffer_, host_buffer_.vk_handle(), Span(&buffer_copy, 1)); + command_buffers.submit(); +} + +void VKStagingBuffer::copy_from_device(VKContext &context) +{ + BLI_assert(host_buffer_.is_allocated() && host_buffer_.is_mapped()); + VkBufferCopy buffer_copy = {}; + buffer_copy.size = device_buffer_.size_in_bytes(); + VKCommandBuffers &command_buffers = context.command_buffers_get(); + command_buffers.copy( + host_buffer_, device_buffer_.vk_handle(), Span(&buffer_copy, 1)); + command_buffers.submit(); +} + +void VKStagingBuffer::free() +{ + host_buffer_.free(); +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_staging_buffer.hh b/source/blender/gpu/vulkan/vk_staging_buffer.hh new file mode 100644 index 00000000000..ef1aebce3af --- /dev/null +++ b/source/blender/gpu/vulkan/vk_staging_buffer.hh @@ -0,0 +1,76 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "vk_buffer.hh" +#include "vk_common.hh" + +namespace blender::gpu { + +/** + * Utility class to copy data from host to device and vise versa. + * + * This is a common as buffers on device are more performant than when located inside host memory. + */ +class VKStagingBuffer { + public: + /** + * Direction of the transfer. + */ + enum class Direction { + /** + * Transferring data from host to device. + */ + HostToDevice, + /** + * Transferring data from device to host. + */ + DeviceToHost, + }; + + private: + /** + * Reference to the device buffer. + */ + const VKBuffer &device_buffer_; + + /** + * The temporary buffer on host for the transfer. Also called the staging buffer. + */ + VKBuffer host_buffer_; + + public: + VKStagingBuffer(const VKBuffer &device_buffer, Direction direction); + + /** + * Copy the content of the host buffer to the device buffer. + */ + void copy_to_device(VKContext &context); + + /** + * Copy the content of the device buffer to the host buffer. + */ + void copy_from_device(VKContext &context); + + /** + * Get the reference to the host buffer to update/load the data. + */ + const VKBuffer &host_buffer_get() const + { + return host_buffer_; + } + + /** + * Free the host memory. + * + * In case a reference of the staging buffer is kept, but the host resource isn't needed anymore. + */ + void free(); +}; +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_storage_buffer.cc b/source/blender/gpu/vulkan/vk_storage_buffer.cc index e36a3a17601..b15cfe8e8c9 100644 --- a/source/blender/gpu/vulkan/vk_storage_buffer.cc +++ b/source/blender/gpu/vulkan/vk_storage_buffer.cc @@ -7,6 +7,7 @@ */ #include "vk_shader.hh" #include "vk_shader_interface.hh" +#include "vk_staging_buffer.hh" #include "vk_state_manager.hh" #include "vk_vertex_buffer.hh" @@ -21,8 +22,11 @@ VKStorageBuffer::VKStorageBuffer(int size, GPUUsageType usage, const char *name) void VKStorageBuffer::update(const void *data) { + VKContext &context = *VKContext::get(); ensure_allocated(); - buffer_.update(data); + VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::HostToDevice); + staging_buffer.host_buffer_get().update(data); + staging_buffer.copy_to_device(context); } void VKStorageBuffer::ensure_allocated() @@ -34,10 +38,12 @@ void VKStorageBuffer::ensure_allocated() void VKStorageBuffer::allocate() { + const bool is_host_visible = false; buffer_.create(size_in_bytes_, usage_, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT); + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + is_host_visible); debug::object_label(buffer_.vk_handle(), name_); } @@ -104,7 +110,9 @@ void VKStorageBuffer::read(void *data) VKContext &context = *VKContext::get(); context.flush(); - buffer_.read(data); + VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::DeviceToHost); + staging_buffer.copy_from_device(context); + staging_buffer.host_buffer_get().read(data); } } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_uniform_buffer.cc b/source/blender/gpu/vulkan/vk_uniform_buffer.cc index f8c4107a2c9..076b4612c4e 100644 --- a/source/blender/gpu/vulkan/vk_uniform_buffer.cc +++ b/source/blender/gpu/vulkan/vk_uniform_buffer.cc @@ -10,6 +10,7 @@ #include "vk_context.hh" #include "vk_shader.hh" #include "vk_shader_interface.hh" +#include "vk_staging_buffer.hh" #include "vk_state_manager.hh" namespace blender::gpu { @@ -19,15 +20,30 @@ void VKUniformBuffer::update(const void *data) if (!buffer_.is_allocated()) { allocate(); } - buffer_.update(data); + VKContext &context = *VKContext::get(); + if (buffer_.is_mapped()) { + buffer_.update(data); + } + else { + VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::HostToDevice); + staging_buffer.host_buffer_get().update(data); + staging_buffer.copy_to_device(context); + } } void VKUniformBuffer::allocate() { + /* + * TODO: make uniform buffers device local. In order to do that we should remove the upload + * during binding, as that will reset the graphics pipeline and already attached resources would + * not be bound anymore. + */ + const bool is_host_visible = true; buffer_.create(size_in_bytes_, GPU_USAGE_STATIC, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT); + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + is_host_visible); debug::object_label(buffer_.vk_handle(), name_); } diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc index ac46c8eb89f..d24cea9848e 100644 --- a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc @@ -114,6 +114,15 @@ void VKVertexAttributeObject::bind_buffers(VKContext &context) } } +void VKVertexAttributeObject::ensure_vbos_uploaded() const +{ + for (VKVertexBuffer *vbo : vbos) { + if (vbo) { + vbo->upload(); + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh index 369fee5dc87..ec662773529 100644 --- a/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh @@ -47,6 +47,14 @@ class VKVertexAttributeObject { void update_bindings(const VKContext &context, VKBatch &batch); void update_bindings(VKImmediate &immediate); + /** + * Ensure that all Vertex Buffers are uploaded to the GPU. + * + * This is a separate step as uploading could flush the graphics pipeline making the state + * inconsistent. + */ + void ensure_vbos_uploaded() const; + void debug_print() const; private: diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.cc b/source/blender/gpu/vulkan/vk_vertex_buffer.cc index e7914adbd16..a0bc7dbc2c3 100644 --- a/source/blender/gpu/vulkan/vk_vertex_buffer.cc +++ b/source/blender/gpu/vulkan/vk_vertex_buffer.cc @@ -12,6 +12,7 @@ #include "vk_memory.hh" #include "vk_shader.hh" #include "vk_shader_interface.hh" +#include "vk_staging_buffer.hh" #include "vk_state_manager.hh" #include "vk_vertex_buffer.hh" @@ -92,7 +93,14 @@ void VKVertexBuffer::read(void *data) const { VKContext &context = *VKContext::get(); context.flush(); - buffer_.read(data); + if (buffer_.is_mapped()) { + buffer_.read(data); + return; + } + + VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::DeviceToHost); + staging_buffer.copy_from_device(context); + staging_buffer.host_buffer_get().read(data); } void VKVertexBuffer::acquire_data() @@ -128,6 +136,25 @@ void VKVertexBuffer::release_data() MEM_SAFE_FREE(data); } +void VKVertexBuffer::upload_data_direct(const VKBuffer &host_buffer) +{ + device_format_ensure(); + if (vertex_format_converter.needs_conversion()) { + vertex_format_converter.convert(host_buffer.mapped_memory_get(), data, vertex_len); + host_buffer.flush(); + } + else { + host_buffer.update(data); + } +} + +void VKVertexBuffer::upload_data_via_staging_buffer(VKContext &context) +{ + VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::HostToDevice); + upload_data_direct(staging_buffer.host_buffer_get()); + staging_buffer.copy_to_device(context); +} + void VKVertexBuffer::upload_data() { if (!buffer_.is_allocated()) { @@ -139,12 +166,12 @@ void VKVertexBuffer::upload_data() if (flag & GPU_VERTBUF_DATA_DIRTY) { device_format_ensure(); - if (vertex_format_converter.needs_conversion()) { - vertex_format_converter.convert(buffer_.mapped_memory_get(), data, vertex_len); - buffer_.flush(); + if (buffer_.is_mapped()) { + upload_data_direct(buffer_); } else { - buffer_.update(data); + VKContext &context = *VKContext::get(); + upload_data_via_staging_buffer(context); } if (usage_ == GPU_USAGE_STATIC) { MEM_SAFE_FREE(data); @@ -175,10 +202,15 @@ const GPUVertFormat &VKVertexBuffer::device_format_get() const void VKVertexBuffer::allocate() { - buffer_.create(size_alloc_get(), - usage_, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT); + const bool is_host_visible = ELEM(usage_, GPU_USAGE_DYNAMIC, GPU_USAGE_STREAM); + VkBufferUsageFlags vk_buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; + if (!is_host_visible) { + vk_buffer_usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + } + buffer_.create(size_alloc_get(), usage_, vk_buffer_usage, is_host_visible); debug::object_label(buffer_.vk_handle(), "VertexBuffer"); } diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.hh b/source/blender/gpu/vulkan/vk_vertex_buffer.hh index 0f02a5171f3..0fcfecdfb31 100644 --- a/source/blender/gpu/vulkan/vk_vertex_buffer.hh +++ b/source/blender/gpu/vulkan/vk_vertex_buffer.hh @@ -61,6 +61,9 @@ class VKVertexBuffer : public VertBuf, public VKBindableResource { private: void allocate(); + void upload_data_direct(const VKBuffer &host_buffer); + void upload_data_via_staging_buffer(VKContext &context); + /* VKTexture requires access to `buffer_` to convert a vertex buffer to a texture. */ friend class VKTexture; };