Files
test/source/blender/gpu/vulkan/vk_batch.cc
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

142 lines
4.8 KiB
C++

/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#include "vk_batch.hh"
#include "vk_context.hh"
#include "vk_framebuffer.hh"
#include "vk_index_buffer.hh"
#include "vk_state_manager.hh"
#include "vk_storage_buffer.hh"
#include "vk_vertex_attribute_object.hh"
#include "vk_vertex_buffer.hh"
namespace blender::gpu {
void VKBatch::draw(int vertex_first, int vertex_count, int instance_first, int instance_count)
{
VKContext &context = *VKContext::get();
render_graph::VKResourceAccessInfo &resource_access_info = context.reset_and_get_access_info();
VKVertexAttributeObject vao;
vao.update_bindings(context, *this);
VKIndexBuffer *index_buffer = index_buffer_get();
const bool draw_indexed = index_buffer != nullptr;
/* Upload geometry */
if (draw_indexed) {
index_buffer->upload_data();
}
context.active_framebuffer_get()->rendering_ensure(context);
if (draw_indexed) {
render_graph::VKDrawIndexedNode::CreateInfo draw_indexed(resource_access_info);
draw_indexed.node_data.index_count = vertex_count;
draw_indexed.node_data.instance_count = instance_count;
draw_indexed.node_data.first_index = vertex_first;
draw_indexed.node_data.vertex_offset = index_buffer->index_start_get();
draw_indexed.node_data.first_instance = instance_first;
draw_indexed.node_data.index_buffer.buffer = index_buffer->vk_handle();
draw_indexed.node_data.index_buffer.index_type = index_buffer->vk_index_type();
vao.bind(draw_indexed.node_data.vertex_buffers);
context.update_pipeline_data(prim_type, vao, draw_indexed.node_data.pipeline_data);
context.render_graph().add_node(draw_indexed);
}
else {
render_graph::VKDrawNode::CreateInfo draw(resource_access_info);
draw.node_data.vertex_count = vertex_count;
draw.node_data.instance_count = instance_count;
draw.node_data.first_vertex = vertex_first;
draw.node_data.first_instance = instance_first;
vao.bind(draw.node_data.vertex_buffers);
context.update_pipeline_data(prim_type, vao, draw.node_data.pipeline_data);
context.render_graph().add_node(draw);
}
}
void VKBatch::draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset)
{
multi_draw_indirect(indirect_buf, 1, offset, 0);
}
void VKBatch::multi_draw_indirect(GPUStorageBuf *indirect_buf,
const int count,
const intptr_t offset,
const intptr_t stride)
{
VKStorageBuffer &indirect_buffer = *unwrap(unwrap(indirect_buf));
multi_draw_indirect(indirect_buffer.vk_handle(), count, offset, stride);
}
void VKBatch::multi_draw_indirect(const VkBuffer indirect_buffer,
const int count,
const intptr_t offset,
const intptr_t stride)
{
VKContext &context = *VKContext::get();
render_graph::VKResourceAccessInfo &resource_access_info = context.reset_and_get_access_info();
VKVertexAttributeObject vao;
vao.update_bindings(context, *this);
VKIndexBuffer *index_buffer = index_buffer_get();
const bool draw_indexed = index_buffer != nullptr;
/* Upload geometry */
if (draw_indexed) {
index_buffer->upload_data();
}
context.active_framebuffer_get()->rendering_ensure(context);
if (draw_indexed) {
render_graph::VKDrawIndexedIndirectNode::CreateInfo draw_indexed_indirect(
resource_access_info);
draw_indexed_indirect.node_data.indirect_buffer = indirect_buffer;
draw_indexed_indirect.node_data.offset = offset;
draw_indexed_indirect.node_data.draw_count = count;
draw_indexed_indirect.node_data.stride = stride;
draw_indexed_indirect.node_data.index_buffer.buffer = index_buffer->vk_handle();
draw_indexed_indirect.node_data.index_buffer.index_type = index_buffer->vk_index_type();
vao.bind(draw_indexed_indirect.node_data.vertex_buffers);
context.update_pipeline_data(prim_type, vao, draw_indexed_indirect.node_data.pipeline_data);
context.render_graph().add_node(draw_indexed_indirect);
}
else {
render_graph::VKDrawIndirectNode::CreateInfo draw(resource_access_info);
draw.node_data.indirect_buffer = indirect_buffer;
draw.node_data.offset = offset;
draw.node_data.draw_count = count;
draw.node_data.stride = stride;
vao.bind(draw.node_data.vertex_buffers);
context.update_pipeline_data(prim_type, vao, draw.node_data.pipeline_data);
context.render_graph().add_node(draw);
}
}
VKVertexBuffer *VKBatch::vertex_buffer_get(int index)
{
return unwrap(verts_(index));
}
VKVertexBuffer *VKBatch::instance_buffer_get(int index)
{
return unwrap(inst_(index));
}
VKIndexBuffer *VKBatch::index_buffer_get()
{
return unwrap(unwrap(elem));
}
} // namespace blender::gpu