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:
@@ -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,
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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*/)
|
||||
|
||||
@@ -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>(©_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>(©_buffer.region, 1));
|
||||
command_buffers.submit();
|
||||
}
|
||||
}
|
||||
|
||||
void VKStagingBuffer::free()
|
||||
|
||||
@@ -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>(®ion, 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>(©_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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user