Vulkan: Render graph storage buffers

A developer can switch `vk_common.hh#use_render_graph` to enable render graph.
When enabled the buffers and images are tracked by the device resource state
tracker. The storage buffer commands are recorded to the context render graph.

The next unit tests will pass:
- GPUVulkanTest.storage_buffer_create_update_read
- GPUVulkanTest.storage_buffer_clear_zero
- GPUVulkanTest.storage_buffer_clear
- GPUVulkanTest.storage_buffer_copy_from_vertex_buffer

The pattern to migrate to render graph is:
- always construct CreateInfo for class.
- based on `use_render_graph` call `context.command_buffers.something`
  or `context.render_graph.add_node`.
- Hide calls to `context.flush` when `use_render_graph` is true.

Pull Request: https://projects.blender.org/blender/blender/pulls/120812
This commit is contained in:
Jeroen Bakker
2024-04-19 12:08:57 +02:00
parent 8b1154a854
commit ed9dea08b2
13 changed files with 113 additions and 36 deletions

View File

@@ -169,7 +169,7 @@ Context *VKBackend::context_alloc(void *ghost_window, void *ghost_context)
device_.init(ghost_context);
}
VKContext *context = new VKContext(ghost_window, ghost_context);
VKContext *context = new VKContext(ghost_window, ghost_context, device_.resources);
device_.context_register(*context);
GHOST_SetVulkanSwapBuffersCallbacks((GHOST_ContextHandle)ghost_context,
VKContext::swap_buffers_pre_callback,

View File

@@ -61,7 +61,7 @@ bool VKBuffer::create(int64_t size_in_bytes,
BLI_assert(mapped_memory_ == nullptr);
size_in_bytes_ = size_in_bytes;
const VKDevice &device = VKBackend::get().device_get();
VKDevice &device = VKBackend::get().device_get();
VmaAllocator allocator = device.mem_allocator_get();
VkBufferCreateInfo create_info = {};
@@ -92,6 +92,10 @@ bool VKBuffer::create(int64_t size_in_bytes,
return false;
}
if (use_render_graph) {
device.resources.add_buffer(vk_buffer_);
}
if (is_host_visible) {
return map();
}
@@ -114,13 +118,26 @@ void VKBuffer::flush() const
void VKBuffer::clear(VKContext &context, uint32_t clear_value)
{
VKCommandBuffers &command_buffers = context.command_buffers_get();
command_buffers.fill(*this, clear_value);
render_graph::VKFillBufferNode::CreateInfo fill_buffer = {};
fill_buffer.vk_buffer = vk_buffer_;
fill_buffer.data = clear_value;
fill_buffer.size = size_in_bytes_;
if (use_render_graph) {
context.render_graph.add_node(fill_buffer);
}
else {
VKCommandBuffers &command_buffers = context.command_buffers_get();
command_buffers.fill(*this, fill_buffer.data);
}
}
void VKBuffer::read(void *data) const
void VKBuffer::read(VKContext &context, void *data) const
{
BLI_assert_msg(is_mapped(), "Cannot read a non-mapped buffer.");
if (use_render_graph) {
context.render_graph.submit_buffer_for_read(vk_buffer_);
}
memcpy(data, mapped_memory_, size_in_bytes_);
}

View File

@@ -38,7 +38,7 @@ class VKBuffer {
void clear(VKContext &context, uint32_t clear_value);
void update(const void *data) const;
void flush() const;
void read(void *data) const;
void read(VKContext &context, void *data) const;
bool free();
int64_t size_in_bytes() const

View File

@@ -24,6 +24,15 @@
namespace blender::gpu {
/**
* The Vulkan backend is currently migrating to a render graph approach. This requires commands to
* be recorded in a different way. During the migration the backend will mist likely crash. With
* the `use_render_graph` constant we can switch back to the not render graph implementation.
* During development of the render graph this is set to true. But when committing to main this
* must be set to false.
*/
static constexpr bool use_render_graph = false;
/**
* Based on the usage of an Image View a different image view type should be created.
*

View File

@@ -21,7 +21,10 @@
namespace blender::gpu {
VKContext::VKContext(void *ghost_window, void *ghost_context)
VKContext::VKContext(void *ghost_window,
void *ghost_context,
render_graph::VKResourceStateTracker &resources)
: render_graph(std::make_unique<render_graph::VKCommandBufferWrapper>(), resources)
{
ghost_window_ = ghost_window;
ghost_context_ = ghost_context;

View File

@@ -12,6 +12,7 @@
#include "GHOST_Types.h"
#include "render_graph/vk_render_graph.hh"
#include "vk_command_buffers.hh"
#include "vk_common.hh"
#include "vk_debug.hh"
@@ -35,7 +36,11 @@ class VKContext : public Context, NonCopyable {
void *ghost_context_;
public:
VKContext(void *ghost_window, void *ghost_context);
render_graph::VKRenderGraph render_graph;
VKContext(void *ghost_window,
void *ghost_context,
render_graph::VKResourceStateTracker &resources);
virtual ~VKContext();
void activate() override;

View File

@@ -368,11 +368,17 @@ void VKDevice::destroy_discarded_resources()
while (!discarded_images_.is_empty()) {
std::pair<VkImage, VmaAllocation> image_allocation = discarded_images_.pop_last();
if (use_render_graph) {
resources.remove_image(image_allocation.first);
}
vmaDestroyImage(mem_allocator_get(), image_allocation.first, image_allocation.second);
}
while (!discarded_buffers_.is_empty()) {
std::pair<VkBuffer, VmaAllocation> buffer_allocation = discarded_buffers_.pop_last();
if (use_render_graph) {
resources.remove_buffer(buffer_allocation.first);
}
vmaDestroyBuffer(mem_allocator_get(), buffer_allocation.first, buffer_allocation.second);
}

View File

@@ -11,6 +11,7 @@
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "render_graph/vk_resource_state_tracker.hh"
#include "vk_buffer.hh"
#include "vk_common.hh"
#include "vk_debug.hh"
@@ -109,6 +110,8 @@ class VKDevice : public NonCopyable {
std::string glsl_patch_;
public:
render_graph::VKResourceStateTracker resources;
VkPhysicalDevice physical_device_get() const
{
return vk_physical_device_;

View File

@@ -73,7 +73,7 @@ void VKIndexBuffer::read(uint32_t *data) const
VKContext &context = *VKContext::get();
VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::DeviceToHost);
staging_buffer.copy_from_device(context);
staging_buffer.host_buffer_get().read(data);
staging_buffer.host_buffer_get().read(context, data);
}
void VKIndexBuffer::update_sub(uint /*start*/, uint /*len*/, const void * /*data*/)

View File

@@ -30,23 +30,39 @@ VKStagingBuffer::VKStagingBuffer(const VKBuffer &device_buffer, Direction direct
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<VkBufferCopy>(&buffer_copy, 1));
command_buffers.submit();
render_graph::VKCopyBufferNode::CreateInfo copy_buffer = {};
copy_buffer.src_buffer = host_buffer_.vk_handle();
copy_buffer.dst_buffer = device_buffer_.vk_handle();
copy_buffer.region.size = device_buffer_.size_in_bytes();
if (use_render_graph) {
context.render_graph.add_node(copy_buffer);
}
else {
VKCommandBuffers &command_buffers = context.command_buffers_get();
command_buffers.copy(
device_buffer_, copy_buffer.src_buffer, Span<VkBufferCopy>(&copy_buffer.region, 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<VkBufferCopy>(&buffer_copy, 1));
command_buffers.submit();
render_graph::VKCopyBufferNode::CreateInfo copy_buffer = {};
copy_buffer.src_buffer = device_buffer_.vk_handle();
copy_buffer.dst_buffer = host_buffer_.vk_handle();
copy_buffer.region.size = device_buffer_.size_in_bytes();
if (use_render_graph) {
context.render_graph.add_node(copy_buffer);
}
else {
VKCommandBuffers &command_buffers = context.command_buffers_get();
command_buffers.copy(
host_buffer_, copy_buffer.src_buffer, Span<VkBufferCopy>(&copy_buffer.region, 1));
command_buffers.submit();
}
}
void VKStagingBuffer::free()

View File

@@ -88,15 +88,23 @@ void VKStorageBuffer::copy_sub(VertBuf *src, uint dst_offset, uint src_offset, u
VKVertexBuffer &src_vertex_buffer = *unwrap(src);
src_vertex_buffer.upload();
VkBufferCopy region = {};
region.srcOffset = src_offset;
region.dstOffset = dst_offset;
region.size = copy_size;
render_graph::VKCopyBufferNode::CreateInfo copy_buffer = {};
copy_buffer.src_buffer = src_vertex_buffer.vk_handle();
copy_buffer.dst_buffer = vk_handle();
copy_buffer.region.srcOffset = src_offset;
copy_buffer.region.dstOffset = dst_offset;
copy_buffer.region.size = copy_size;
VKContext &context = *VKContext::get();
VKCommandBuffers &command_buffers = context.command_buffers_get();
command_buffers.copy(buffer_, src_vertex_buffer.vk_handle(), Span<VkBufferCopy>(&region, 1));
context.flush();
if (use_render_graph) {
context.render_graph.add_node(copy_buffer);
}
else {
VKCommandBuffers &command_buffers = context.command_buffers_get();
command_buffers.copy(
buffer_, copy_buffer.src_buffer, Span<VkBufferCopy>(&copy_buffer.region, 1));
context.flush();
}
}
void VKStorageBuffer::async_flush_to_host()
@@ -108,11 +116,13 @@ void VKStorageBuffer::read(void *data)
{
ensure_allocated();
VKContext &context = *VKContext::get();
context.flush();
if (!use_render_graph) {
context.flush();
}
VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::DeviceToHost);
staging_buffer.copy_from_device(context);
staging_buffer.host_buffer_get().read(data);
staging_buffer.host_buffer_get().read(context, data);
}
} // namespace blender::gpu

View File

@@ -514,7 +514,7 @@ bool VKTexture::allocate()
BLI_assert(!is_texture_view());
VKContext &context = *VKContext::get();
const VKDevice &device = VKBackend::get().device_get();
VKDevice &device = VKBackend::get().device_get();
VkImageCreateInfo image_info = {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.flags = to_vk_image_create(type_, format_flag_, usage_get());
@@ -563,8 +563,14 @@ bool VKTexture::allocate()
}
debug::object_label(vk_image_, name_);
/* Promote image to the correct layout. */
layout_ensure(context, VK_IMAGE_LAYOUT_GENERAL);
if (use_render_graph) {
device.resources.add_image(
vk_image_, VK_IMAGE_LAYOUT_UNDEFINED, render_graph::ResourceOwner::APPLICATION);
}
else {
/* Promote image to the correct layout. */
layout_ensure(context, VK_IMAGE_LAYOUT_GENERAL);
}
return result == VK_SUCCESS;
}

View File

@@ -92,15 +92,17 @@ void VKVertexBuffer::update_sub(uint /*start*/, uint /*len*/, const void * /*dat
void VKVertexBuffer::read(void *data) const
{
VKContext &context = *VKContext::get();
context.flush();
if (!use_render_graph) {
context.flush();
}
if (buffer_.is_mapped()) {
buffer_.read(data);
buffer_.read(context, data);
return;
}
VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::DeviceToHost);
staging_buffer.copy_from_device(context);
staging_buffer.host_buffer_get().read(data);
staging_buffer.host_buffer_get().read(context, data);
}
void VKVertexBuffer::acquire_data()