diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 96e54108f9a..1136606fd87 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -294,6 +294,7 @@ set(VULKAN_SRC vulkan/render_graph/nodes/vk_pipeline_data.hh vulkan/render_graph/nodes/vk_reset_query_pool_node.hh vulkan/render_graph/nodes/vk_synchronization_node.hh + vulkan/render_graph/nodes/vk_update_buffer_node.hh vulkan/render_graph/nodes/vk_update_mipmaps_node.hh vulkan/render_graph/vk_command_buffer_wrapper.hh vulkan/render_graph/vk_command_builder.hh diff --git a/source/blender/gpu/vulkan/render_graph/nodes/vk_node_info.hh b/source/blender/gpu/vulkan/render_graph/nodes/vk_node_info.hh index 0ae1fdae8b2..b39a8b831c7 100644 --- a/source/blender/gpu/vulkan/render_graph/nodes/vk_node_info.hh +++ b/source/blender/gpu/vulkan/render_graph/nodes/vk_node_info.hh @@ -42,6 +42,7 @@ enum class VKNodeType { FILL_BUFFER, RESET_QUERY_POOL, SYNCHRONIZATION, + UPDATE_BUFFER, UPDATE_MIPMAPS, }; @@ -114,6 +115,9 @@ BLI_INLINE std::ostream &operator<<(std::ostream &os, const VKNodeType node_type case VKNodeType::SYNCHRONIZATION: os << "SYNCHRONIZATION"; break; + case VKNodeType::UPDATE_BUFFER: + os << "UPDATE_BUFFER"; + break; case VKNodeType::UPDATE_MIPMAPS: os << "UPDATE_MIPMAPS"; break; diff --git a/source/blender/gpu/vulkan/render_graph/nodes/vk_update_buffer_node.hh b/source/blender/gpu/vulkan/render_graph/nodes/vk_update_buffer_node.hh new file mode 100644 index 00000000000..bab3ca342a5 --- /dev/null +++ b/source/blender/gpu/vulkan/render_graph/nodes/vk_update_buffer_node.hh @@ -0,0 +1,70 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "vk_node_info.hh" + +namespace blender::gpu::render_graph { +/** + * Information stored inside the render graph node. See `VKRenderGraphNode`. + */ +struct VKUpdateBufferData { + VkBuffer dst_buffer; + VkDeviceSize dst_offset; + VkDeviceSize data_size; + void *data; +}; + +class VKUpdateBufferNode : public VKNodeInfo { + public: + /** + * Update the node data with the data inside create_info. + * + * Has been implemented as a template to ensure all node specific data + * (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The + * actual node data (`VKRenderGraphNode` includes all header files.) + */ + template static void set_node_data(Node &node, const CreateInfo &create_info) + { + node.update_buffer = create_info; + } + + /** + * Extract read/write resource dependencies from `create_info` and add them to `node_links`. + */ + void build_links(VKResourceStateTracker &resources, + VKRenderGraphNodeLinks &node_links, + const CreateInfo &create_info) override + { + ResourceWithStamp dst_resource = resources.get_buffer_and_increase_stamp( + create_info.dst_buffer); + node_links.outputs.append({dst_resource, VK_ACCESS_TRANSFER_WRITE_BIT}); + } + + /** + * Build the commands and add them to the command_buffer. + */ + void build_commands(VKCommandBufferInterface &command_buffer, + Data &data, + VKBoundPipelines & /*r_bound_pipelines*/) override + { + command_buffer.update_buffer(data.dst_buffer, data.dst_offset, data.data_size, data.data); + } + + void free_data(Data &data) + { + MEM_freeN(data.data); + data.data = nullptr; + } +}; +} // namespace blender::gpu::render_graph diff --git a/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh b/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh index 3b2eb5b0921..3cd6bc9ff23 100644 --- a/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh +++ b/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh @@ -189,6 +189,21 @@ class CommandBufferLog : public VKCommandBufferInterface { log_.append(ss.str()); } + void update_buffer(VkBuffer dst_buffer, + VkDeviceSize dst_offset, + VkDeviceSize data_size, + const void * /*p_data*/) override + { + EXPECT_TRUE(is_recording_); + std::stringstream ss; + ss << "update_buffer("; + ss << "dst_buffer=" << to_string(dst_buffer); + ss << ", dst_offset=" << dst_offset; + ss << ", data_size=" << data_size; + ss << ")"; + ss << std::endl; + log_.append(ss.str()); + } void copy_buffer(VkBuffer src_buffer, VkBuffer dst_buffer, uint32_t region_count, diff --git a/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.cc b/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.cc index 2b5372afa0d..e15cd65f0ce 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.cc +++ b/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.cc @@ -189,6 +189,14 @@ void VKCommandBufferWrapper::dispatch_indirect(VkBuffer buffer, VkDeviceSize off vkCmdDispatchIndirect(vk_command_buffer_, buffer, offset); } +void VKCommandBufferWrapper::update_buffer(VkBuffer dst_buffer, + VkDeviceSize dst_offset, + VkDeviceSize data_size, + const void *p_data) +{ + vkCmdUpdateBuffer(vk_command_buffer_, dst_buffer, dst_offset, data_size, p_data); +} + void VKCommandBufferWrapper::copy_buffer(VkBuffer src_buffer, VkBuffer dst_buffer, uint32_t region_count, diff --git a/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.hh b/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.hh index 712fe99d65c..18c6eea8b75 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.hh +++ b/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.hh @@ -55,6 +55,10 @@ class VKCommandBufferInterface { uint32_t group_count_y, uint32_t group_count_z) = 0; virtual void dispatch_indirect(VkBuffer buffer, VkDeviceSize offset) = 0; + virtual void update_buffer(VkBuffer dst_buffer, + VkDeviceSize dst_offset, + VkDeviceSize data_size, + const void *p_data) = 0; virtual void copy_buffer(VkBuffer src_buffer, VkBuffer dst_buffer, uint32_t region_count, @@ -183,6 +187,10 @@ class VKCommandBufferWrapper : public VKCommandBufferInterface { uint32_t stride) override; void dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) override; void dispatch_indirect(VkBuffer buffer, VkDeviceSize offset) override; + void update_buffer(VkBuffer dst_buffer, + VkDeviceSize dst_offset, + VkDeviceSize data_size, + const void *p_data) override; void copy_buffer(VkBuffer src_buffer, VkBuffer dst_buffer, uint32_t region_count, diff --git a/source/blender/gpu/vulkan/render_graph/vk_render_graph.hh b/source/blender/gpu/vulkan/render_graph/vk_render_graph.hh index d2b29cc06b0..36fb66d0be9 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_render_graph.hh +++ b/source/blender/gpu/vulkan/render_graph/vk_render_graph.hh @@ -197,6 +197,7 @@ class VKRenderGraph : public NonCopyable { ADD_NODE(VKDrawIndexedIndirectNode) ADD_NODE(VKDrawIndirectNode) ADD_NODE(VKResetQueryPoolNode) + ADD_NODE(VKUpdateBufferNode) ADD_NODE(VKUpdateMipmapsNode) #undef ADD_NODE diff --git a/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh b/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh index 9d7eed35049..b021c299fb4 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh +++ b/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh @@ -29,6 +29,7 @@ #include "nodes/vk_fill_buffer_node.hh" #include "nodes/vk_reset_query_pool_node.hh" #include "nodes/vk_synchronization_node.hh" +#include "nodes/vk_update_buffer_node.hh" #include "nodes/vk_update_mipmaps_node.hh" namespace blender::gpu::render_graph { @@ -69,6 +70,7 @@ struct VKRenderGraphNode { VKFillBufferNode::Data fill_buffer; VKResetQueryPoolNode::Data reset_query_pool; VKSynchronizationNode::Data synchronization; + VKUpdateBufferNode::Data update_buffer; VKUpdateMipmapsNode::Data update_mipmaps; }; @@ -158,6 +160,8 @@ struct VKRenderGraphNode { return VKResetQueryPoolNode::pipeline_stage; case VKNodeType::SYNCHRONIZATION: return VKSynchronizationNode::pipeline_stage; + case VKNodeType::UPDATE_BUFFER: + return VKUpdateBufferNode::pipeline_stage; case VKNodeType::UPDATE_MIPMAPS: return VKUpdateMipmapsNode::pipeline_stage; } @@ -195,6 +199,7 @@ struct VKRenderGraphNode { BUILD_COMMANDS(VKNodeType::END_QUERY, VKEndQueryNode, end_query) BUILD_COMMANDS(VKNodeType::END_RENDERING, VKEndRenderingNode, end_rendering) BUILD_COMMANDS(VKNodeType::FILL_BUFFER, VKFillBufferNode, fill_buffer) + BUILD_COMMANDS(VKNodeType::UPDATE_BUFFER, VKUpdateBufferNode, update_buffer) BUILD_COMMANDS(VKNodeType::COPY_BUFFER, VKCopyBufferNode, copy_buffer) BUILD_COMMANDS( VKNodeType::COPY_BUFFER_TO_IMAGE, VKCopyBufferToImageNode, copy_buffer_to_image) @@ -237,6 +242,7 @@ struct VKRenderGraphNode { FREE_DATA( VKNodeType::DRAW_INDEXED_INDIRECT, VKDrawIndexedIndirectNode, draw_indexed_indirect) FREE_DATA(VKNodeType::DRAW_INDIRECT, VKDrawIndirectNode, draw_indirect) + FREE_DATA(VKNodeType::UPDATE_BUFFER, VKUpdateBufferNode, update_buffer) #undef FREE_DATA case VKNodeType::UNUSED: diff --git a/source/blender/gpu/vulkan/vk_buffer.cc b/source/blender/gpu/vulkan/vk_buffer.cc index fcb652f2b9c..f6921955a3e 100644 --- a/source/blender/gpu/vulkan/vk_buffer.cc +++ b/source/blender/gpu/vulkan/vk_buffer.cc @@ -100,13 +100,23 @@ bool VKBuffer::create(size_t size_in_bytes, return true; } -void VKBuffer::update(const void *data) const +void VKBuffer::update_immediately(const void *data) const { BLI_assert_msg(is_mapped(), "Cannot update a non-mapped buffer."); memcpy(mapped_memory_, data, size_in_bytes_); flush(); } +void VKBuffer::update_render_graph(VKContext &context, void *data) const +{ + BLI_assert(size_in_bytes_ <= 65536 && size_in_bytes_ % 4 == 0); + render_graph::VKUpdateBufferNode::CreateInfo update_buffer = {}; + update_buffer.dst_buffer = vk_buffer_; + update_buffer.data_size = size_in_bytes_; + update_buffer.data = data; + context.render_graph.add_node(update_buffer); +} + void VKBuffer::flush() const { const VKDevice &device = VKBackend::get().device; diff --git a/source/blender/gpu/vulkan/vk_buffer.hh b/source/blender/gpu/vulkan/vk_buffer.hh index 9aff1c2da84..fb3265e5eec 100644 --- a/source/blender/gpu/vulkan/vk_buffer.hh +++ b/source/blender/gpu/vulkan/vk_buffer.hh @@ -38,7 +38,13 @@ class VKBuffer : public NonCopyable { VkBufferUsageFlags buffer_usage, bool is_host_visible = true); void clear(VKContext &context, uint32_t clear_value); - void update(const void *data) const; + void update_immediately(const void *data) const; + + /** + * Update the buffer as part of the render graph evaluation. The ownership of data will be + * transferred to the render graph and should have been allocated using guarded alloc. + */ + void update_render_graph(VKContext &context, void *data) const; void flush() const; void read(VKContext &context, void *data) const; diff --git a/source/blender/gpu/vulkan/vk_device.cc b/source/blender/gpu/vulkan/vk_device.cc index 7269aed1f5c..96501ea4298 100644 --- a/source/blender/gpu/vulkan/vk_device.cc +++ b/source/blender/gpu/vulkan/vk_device.cc @@ -201,7 +201,7 @@ void VKDevice::init_dummy_buffer() /* Default dummy buffer. Set the 4th element to 1 to fix missing orcos. */ float data[16] = { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - dummy_buffer.update(static_cast(data)); + dummy_buffer.update_immediately(static_cast(data)); } void VKDevice::init_glsl_patch() diff --git a/source/blender/gpu/vulkan/vk_index_buffer.cc b/source/blender/gpu/vulkan/vk_index_buffer.cc index ebf2e27efdf..8b498d33562 100644 --- a/source/blender/gpu/vulkan/vk_index_buffer.cc +++ b/source/blender/gpu/vulkan/vk_index_buffer.cc @@ -31,7 +31,7 @@ void VKIndexBuffer::ensure_updated() VKContext &context = *VKContext::get(); VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::HostToDevice); - staging_buffer.host_buffer_get().update(data_); + staging_buffer.host_buffer_get().update_immediately(data_); staging_buffer.copy_to_device(context); MEM_SAFE_FREE(data_); } diff --git a/source/blender/gpu/vulkan/vk_storage_buffer.cc b/source/blender/gpu/vulkan/vk_storage_buffer.cc index 80db6cf2101..610737544e7 100644 --- a/source/blender/gpu/vulkan/vk_storage_buffer.cc +++ b/source/blender/gpu/vulkan/vk_storage_buffer.cc @@ -25,7 +25,7 @@ void VKStorageBuffer::update(const void *data) VKContext &context = *VKContext::get(); ensure_allocated(); VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::HostToDevice); - staging_buffer.host_buffer_get().update(data); + staging_buffer.host_buffer_get().update_immediately(data); staging_buffer.copy_to_device(context); } diff --git a/source/blender/gpu/vulkan/vk_uniform_buffer.cc b/source/blender/gpu/vulkan/vk_uniform_buffer.cc index bfcd759fba2..7198796ffb7 100644 --- a/source/blender/gpu/vulkan/vk_uniform_buffer.cc +++ b/source/blender/gpu/vulkan/vk_uniform_buffer.cc @@ -20,15 +20,12 @@ void VKUniformBuffer::update(const void *data) if (!buffer_.is_allocated()) { allocate(); } + + /* TODO: when buffer is mapped and newly created we should use `buffer_.update_immediately`. */ + void *data_copy = MEM_mallocN(size_in_bytes_, __func__); + memcpy(data_copy, data, size_in_bytes_); 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); - } + buffer_.update_render_graph(context, data_copy); } void VKUniformBuffer::allocate() @@ -58,8 +55,10 @@ void VKUniformBuffer::ensure_updated() /* Upload attached data, during bind time. */ if (data_) { - update(data_); - MEM_SAFE_FREE(data_); + /* TODO: when buffer is mapped and newly created we should use `buffer_.update_immediately`. */ + VKContext &context = *VKContext::get(); + buffer_.update_render_graph(context, std::move(data_)); + data_ = nullptr; } } diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.cc b/source/blender/gpu/vulkan/vk_vertex_buffer.cc index 0a27caebb2f..0af48e4aa5e 100644 --- a/source/blender/gpu/vulkan/vk_vertex_buffer.cc +++ b/source/blender/gpu/vulkan/vk_vertex_buffer.cc @@ -130,7 +130,7 @@ void VKVertexBuffer::upload_data_direct(const VKBuffer &host_buffer) host_buffer.flush(); } else { - host_buffer.update(data_); + host_buffer.update_immediately(data_); } }