Files
test2/source/blender/gpu/vulkan/vk_resource_pool.hh
Jeroen Bakker e6b3cc8983 Vulkan: Device command builder
This PR implements a new the threading model for building render graphs
based on tests performed last month. For out workload multithreaded
command building will block in the driver or device. So better to use a
single thread for command building.

Details of the internal working is documented at https://developer.blender.org/docs/features/gpu/vulkan/render_graph/

- When a context is activated on a thread the context asks for a
  render graph it can use by calling `VKDevice::render_graph_new`.
- Parts of the GPU backend that requires GPU commands will add a
  specific render graph node to the render graph. The nodes also
  contains a reference to all resources it needs including the
  access it needs and the image layout.
- When the context is flushed the render graph is submitted to the
  device by calling `VKDevice::render_graph_submit`.
- The device puts the render graph in `VKDevice::submission_pool`.
- There is a single background thread that gets the next render
  graph to send to the GPU (`VKDevice::submission_runner`).
  - Reorder the commands of the render graph to comply with Vulkan
    specific command order rules and reducing possible bottlenecks.
    (`VKScheduler`)
  - Generate the required barriers `VKCommandBuilder::groups_extract_barriers`.
    This is a separate step to reduce resource locking giving other
    threads access to the resource states when they are building
    the render graph nodes.
  - GPU commands and pipeline barriers are recorded to a VkCommandBuffer.
    (`VKCommandBuilder::record_commands`)
  - When completed the command buffer can be submitted to the device
    queue. `vkQueueSubmit`
  - Render graphs that have been submitted can be reused by a next
    thread. This is done by pushing the render graph to the
    `VKDevice::unused_render_graphs` queue.

Pull Request: https://projects.blender.org/blender/blender/pulls/132681
2025-01-27 08:55:23 +01:00

137 lines
4.0 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#pragma once
#include "vk_common.hh"
#include "vk_descriptor_pools.hh"
#include "vk_immediate.hh"
namespace blender::gpu {
class VKDevice;
class VKDiscardPool;
template<typename Item> class TimelineResources : Vector<std::pair<TimelineValue, Item>> {
friend class VKDiscardPool;
public:
void append_timeline(TimelineValue timeline, Item item)
{
BLI_assert_msg(this->is_empty() || this->last().first <= timeline,
"Timeline must be added in order");
this->append(std::pair(timeline, item));
}
void update_timeline(TimelineValue timeline)
{
for (std::pair<TimelineValue, Item> &pair : *this) {
pair.first = timeline;
}
}
int64_t size() const
{
return static_cast<const Vector<std::pair<TimelineValue, Item>> &>(*this).size();
}
bool is_empty() const
{
return static_cast<const Vector<std::pair<TimelineValue, Item>> &>(*this).is_empty();
}
/**
* Remove all items that are used in a timeline before or equal to the current_timeline.
*/
template<typename Deleter> void remove_old(TimelineValue current_timeline, Deleter deleter)
{
int64_t first_index_to_keep = 0;
for (std::pair<TimelineValue, Item> &item : *this) {
if (item.first > current_timeline) {
break;
}
deleter(item.second);
first_index_to_keep++;
}
if (first_index_to_keep > 0) {
this->remove(0, first_index_to_keep);
}
}
};
/**
* Pool of resources that are discarded, but can still be in used and cannot be destroyed.
*
* When GPU resources are deleted (`GPU_*_delete`) the GPU handles are kept inside a discard pool.
* When we are sure that the resource isn't used on the GPU anymore we can safely destroy it.
*
* When preparing the next frame, the previous frame can still be rendered. Resources that needs to
* be destroyed can only be when the previous frame has been completed and being displayed on the
* screen.
*/
class VKDiscardPool {
friend class VKDevice;
private:
TimelineResources<std::pair<VkImage, VmaAllocation>> images_;
TimelineResources<std::pair<VkBuffer, VmaAllocation>> buffers_;
TimelineResources<VkImageView> image_views_;
TimelineResources<VkShaderModule> shader_modules_;
TimelineResources<VkPipelineLayout> pipeline_layouts_;
TimelineResources<VkRenderPass> render_passes_;
TimelineResources<VkFramebuffer> framebuffers_;
std::mutex mutex_;
TimelineValue timeline_ = UINT64_MAX;
public:
void deinit(VKDevice &device);
void discard_image(VkImage vk_image, VmaAllocation vma_allocation);
void discard_image_view(VkImageView vk_image_view);
void discard_buffer(VkBuffer vk_buffer, VmaAllocation vma_allocation);
void discard_shader_module(VkShaderModule vk_shader_module);
void discard_pipeline_layout(VkPipelineLayout vk_pipeline_layout);
void discard_framebuffer(VkFramebuffer vk_framebuffer);
void discard_render_pass(VkRenderPass vk_render_pass);
/**
* Move discarded resources from src_pool into this.
*
* GPU resources that are discarded from the dependency graph are stored in the device orphaned
* data. When a swap chain context list is made active the orphaned data can be merged into a
* swap chain discard pool.
*
* All moved items will receive a new timeline.
*/
void move_data(VKDiscardPool &src_pool, TimelineValue timeline);
void destroy_discarded_resources(VKDevice &device, bool force = false);
/**
* Returns the discard pool for the current thread.
*
* When active thread has a context it uses the context discard pool.
* Otherwise the device discard pool is used.
*/
static VKDiscardPool &discard_pool_get();
};
class VKResourcePool {
public:
VKDescriptorPools descriptor_pools;
VKDescriptorSetTracker descriptor_set;
VKImmediate immediate;
void init(VKDevice &device);
void deinit(VKDevice &device);
void reset();
};
} // namespace blender::gpu