Vulkan: Share rendergraphs on context inside same thread

This PR will share render graphs between all contexts that run in
the same thread. This allows the draw manager commands to be added
to the same render graph as the UI.

- Fixes debug groups hiearchy. Draw manager would restart a hierarchy as
  it wasn't aware of the debug groups already added by the UI
- Removes cpu sync when switching between contexts.

In a future change this is needed to improve discarding resources.

Pull Request: https://projects.blender.org/blender/blender/pulls/124715
This commit is contained in:
Jeroen Bakker
2024-07-15 16:03:51 +02:00
parent 5f54c463c0
commit 0d71d83d47
7 changed files with 44 additions and 27 deletions

View File

@@ -17,11 +17,6 @@ VKRenderGraph::VKRenderGraph(std::unique_ptr<VKCommandBufferInterface> command_b
submission_id.reset();
}
void VKRenderGraph::free_data()
{
command_buffer_.reset();
}
void VKRenderGraph::remove_nodes(Span<NodeHandle> node_handles)
{
UNUSED_VARS_NDEBUG(node_handles);

View File

@@ -39,6 +39,7 @@
#include <mutex>
#include <optional>
#include <pthread.h>
#include "BKE_global.hh"
@@ -119,6 +120,12 @@ class VKRenderGraph : public NonCopyable {
public:
VKSubmissionID submission_id;
/**
* Thread this render graph belongs to.
*
* Contexts of the same thread will share the same render graph. See `VKDevice::render_graph()`.
*/
pthread_t thread_id;
/**
* Construct a new render graph instance.
@@ -129,17 +136,6 @@ class VKRenderGraph : public NonCopyable {
VKRenderGraph(std::unique_ptr<VKCommandBufferInterface> command_buffer,
VKResourceStateTracker &resources);
/**
* Free all resources held by the render graph. After calling this function the render graph may
* not work as expected, leading to crashes.
*
* Freeing data of context resources cannot be done inside the destructor due to an issue when
* Blender (read window manager) exits. During this phase the backend is deallocated, device is
* destroyed, but window manager requires a context so it creates new one. We work around this
* issue by ensuring the VKDevice is always in control of releasing resources.
*/
void free_data();
private:
/**
* Add a node to the render graph.

View File

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

View File

@@ -23,8 +23,8 @@ namespace blender::gpu {
VKContext::VKContext(void *ghost_window,
void *ghost_context,
render_graph::VKResourceStateTracker &resources)
: render_graph(std::make_unique<render_graph::VKCommandBufferWrapper>(), resources)
render_graph::VKRenderGraph &render_graph)
: render_graph(render_graph)
{
ghost_window_ = ghost_window;
ghost_context_ = ghost_context;
@@ -46,7 +46,6 @@ VKContext::~VKContext()
GPU_texture_free(surface_texture_);
surface_texture_ = nullptr;
}
render_graph.free_data();
VKBackend::get().device.context_unregister(*this);
delete imm;
@@ -115,9 +114,6 @@ void VKContext::activate()
void VKContext::deactivate()
{
/* Draw manager draws in a different context than the rest of the UI. Although run from the
* same thread. Commands inside the render-graph need to be submitted into the device queue. */
flush_render_graph();
immDeactivate();
is_active_ = false;
}

View File

@@ -40,11 +40,9 @@ class VKContext : public Context, NonCopyable {
bool is_init_ = false;
public:
render_graph::VKRenderGraph render_graph;
render_graph::VKRenderGraph &render_graph;
VKContext(void *ghost_window,
void *ghost_context,
render_graph::VKResourceStateTracker &resources);
VKContext(void *ghost_window, void *ghost_context, render_graph::VKRenderGraph &render_graph);
virtual ~VKContext();
void activate() override;

View File

@@ -40,6 +40,14 @@ void VKDevice::deinit()
return;
}
{
std::scoped_lock mutex(resources.mutex);
for (render_graph::VKRenderGraph *render_graph : render_graphs_) {
delete render_graph;
}
render_graphs_.clear();
}
dummy_buffer_.free();
samplers_.free();
destroy_discarded_resources();
@@ -342,6 +350,24 @@ std::string VKDevice::driver_version() const
/** \name Resource management
* \{ */
render_graph::VKRenderGraph &VKDevice::render_graph()
{
std::scoped_lock mutex(resources.mutex);
pthread_t current_thread_id = pthread_self();
for (render_graph::VKRenderGraph *render_graph : render_graphs_) {
if (pthread_equal(render_graph->thread_id, current_thread_id)) {
return *render_graph;
}
}
render_graph::VKRenderGraph *render_graph = new render_graph::VKRenderGraph(
std::make_unique<render_graph::VKCommandBufferWrapper>(), resources);
render_graph->thread_id = current_thread_id;
render_graphs_.append(render_graph);
return *render_graph;
}
void VKDevice::context_register(VKContext &context)
{
contexts_.append(std::reference_wrapper(context));

View File

@@ -11,6 +11,7 @@
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "render_graph/vk_render_graph.hh"
#include "render_graph/vk_resource_state_tracker.hh"
#include "vk_buffer.hh"
#include "vk_common.hh"
@@ -103,6 +104,7 @@ class VKDevice : public NonCopyable {
Vector<VkImageView> discarded_image_views_;
std::string glsl_patch_;
Vector<render_graph::VKRenderGraph *> render_graphs_;
public:
render_graph::VKResourceStateTracker resources;
@@ -235,6 +237,10 @@ class VKDevice : public NonCopyable {
/** \name Resource management
* \{ */
/**
* Get the render graph associated with the current thread. Create a new one when not existing.
*/
render_graph::VKRenderGraph &render_graph();
void context_register(VKContext &context);
void context_unregister(VKContext &context);
Span<std::reference_wrapper<VKContext>> contexts_get() const;