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
240 lines
7.0 KiB
C++
240 lines
7.0 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#include <sstream>
|
|
|
|
#include "BKE_global.hh"
|
|
#include "CLG_log.h"
|
|
|
|
#include "vk_backend.hh"
|
|
#include "vk_context.hh"
|
|
#include "vk_debug.hh"
|
|
#include "vk_to_string.hh"
|
|
|
|
static CLG_LogRef LOG = {"gpu.vulkan"};
|
|
|
|
namespace blender::gpu {
|
|
void VKContext::debug_group_begin(const char *name, int)
|
|
{
|
|
render_graph().debug_group_begin(name, debug::get_debug_group_color(name));
|
|
}
|
|
|
|
void VKContext::debug_group_end()
|
|
{
|
|
render_graph().debug_group_end();
|
|
}
|
|
|
|
bool VKContext::debug_capture_begin(const char *title)
|
|
{
|
|
flush_render_graph(RenderGraphFlushFlags::SUBMIT | RenderGraphFlushFlags::WAIT_FOR_COMPLETION |
|
|
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
|
return VKBackend::get().debug_capture_begin(title);
|
|
}
|
|
|
|
bool VKBackend::debug_capture_begin(const char *title)
|
|
{
|
|
#ifdef WITH_RENDERDOC
|
|
bool result = renderdoc_api_.start_frame_capture(device.instance_get(), nullptr);
|
|
if (result && title) {
|
|
renderdoc_api_.set_frame_capture_title(title);
|
|
}
|
|
return result;
|
|
#else
|
|
UNUSED_VARS(title);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void VKContext::debug_capture_end()
|
|
{
|
|
flush_render_graph(RenderGraphFlushFlags::SUBMIT | RenderGraphFlushFlags::WAIT_FOR_COMPLETION |
|
|
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
|
VKBackend::get().debug_capture_end();
|
|
}
|
|
|
|
void VKBackend::debug_capture_end()
|
|
{
|
|
#ifdef WITH_RENDERDOC
|
|
renderdoc_api_.end_frame_capture(device.instance_get(), nullptr);
|
|
#endif
|
|
}
|
|
|
|
void *VKContext::debug_capture_scope_create(const char *name)
|
|
{
|
|
return (void *)name;
|
|
}
|
|
|
|
bool VKContext::debug_capture_scope_begin(void *scope)
|
|
{
|
|
#ifdef WITH_RENDERDOC
|
|
const char *title = (const char *)scope;
|
|
if (StringRefNull(title) != StringRefNull(G.gpu_debug_scope_name)) {
|
|
return false;
|
|
}
|
|
VKBackend::get().debug_capture_begin(title);
|
|
#else
|
|
UNUSED_VARS(scope);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void VKContext::debug_capture_scope_end(void *scope)
|
|
{
|
|
#ifdef WITH_RENDERDOC
|
|
const char *title = (const char *)scope;
|
|
if (StringRefNull(title) == StringRefNull(G.gpu_debug_scope_name)) {
|
|
VKBackend::get().debug_capture_end();
|
|
}
|
|
#else
|
|
UNUSED_VARS(scope);
|
|
#endif
|
|
}
|
|
|
|
} // namespace blender::gpu
|
|
|
|
namespace blender::gpu::debug {
|
|
|
|
void VKDebuggingTools::init(VkInstance vk_instance)
|
|
{
|
|
CLG_logref_init(&LOG);
|
|
init_messenger(vk_instance);
|
|
}
|
|
|
|
void VKDebuggingTools::deinit(VkInstance vk_instance)
|
|
{
|
|
destroy_messenger(vk_instance);
|
|
}
|
|
|
|
void object_label(VkObjectType vk_object_type, uint64_t object_handle, const char *name)
|
|
{
|
|
const VKDevice &device = VKBackend::get().device;
|
|
if (G.debug & G_DEBUG_GPU && device.functions.vkSetDebugUtilsObjectName) {
|
|
VkDebugUtilsObjectNameInfoEXT info = {};
|
|
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
|
info.objectType = vk_object_type;
|
|
info.objectHandle = object_handle;
|
|
info.pObjectName = name;
|
|
device.functions.vkSetDebugUtilsObjectName(device.vk_handle(), &info);
|
|
}
|
|
}
|
|
|
|
} // namespace blender::gpu::debug
|
|
|
|
namespace blender::gpu::debug {
|
|
|
|
void VKDebuggingTools::print_labels(const VkDebugUtilsMessengerCallbackDataEXT *callback_data)
|
|
{
|
|
std::stringstream ss;
|
|
for (uint32_t object = 0; object < callback_data->objectCount; ++object) {
|
|
ss << " - ObjectType[" << to_string(callback_data->pObjects[object].objectType) << "],";
|
|
ss << "Handle[0x" << std::hex << uintptr_t(callback_data->pObjects[object].objectHandle)
|
|
<< "]";
|
|
if (callback_data->pObjects[object].pObjectName) {
|
|
ss << ",Name[" << callback_data->pObjects[object].pObjectName << "]";
|
|
}
|
|
ss << std::endl;
|
|
}
|
|
for (uint32_t label = 0; label < callback_data->cmdBufLabelCount; ++label) {
|
|
if (callback_data->pCmdBufLabels[label].pLabelName) {
|
|
ss << " - CommandBuffer : " << callback_data->pCmdBufLabels[label].pLabelName << std::endl;
|
|
}
|
|
}
|
|
for (uint32_t label = 0; label < callback_data->queueLabelCount; ++label) {
|
|
if (callback_data->pQueueLabels[label].pLabelName) {
|
|
ss << " - Queue : " << callback_data->pQueueLabels[label].pLabelName << std::endl;
|
|
}
|
|
}
|
|
ss << std::endl;
|
|
printf("%s", ss.str().c_str());
|
|
}
|
|
|
|
static VKAPI_ATTR VkBool32 VKAPI_CALL
|
|
messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
|
VkDebugUtilsMessageTypeFlagsEXT /*message_type*/,
|
|
const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
|
|
void *user_data)
|
|
{
|
|
|
|
CLG_Severity severity = CLG_SEVERITY_INFO;
|
|
if (message_severity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT))
|
|
{
|
|
severity = CLG_SEVERITY_INFO;
|
|
}
|
|
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
|
severity = CLG_SEVERITY_WARN;
|
|
}
|
|
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
|
severity = CLG_SEVERITY_ERROR;
|
|
}
|
|
|
|
if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level <= severity)) {
|
|
const char *format = "{0x%x}% s\n %s ";
|
|
CLG_logf(LOG.type,
|
|
severity,
|
|
"",
|
|
"",
|
|
format,
|
|
callback_data->messageIdNumber,
|
|
callback_data->pMessageIdName,
|
|
callback_data->pMessage);
|
|
}
|
|
|
|
const bool do_labels = (callback_data->objectCount + callback_data->cmdBufLabelCount +
|
|
callback_data->queueLabelCount) > 0;
|
|
if (do_labels) {
|
|
VKDebuggingTools &debugging_tools = *reinterpret_cast<VKDebuggingTools *>(user_data);
|
|
debugging_tools.print_labels(callback_data);
|
|
}
|
|
|
|
return VK_FALSE;
|
|
};
|
|
|
|
void VKDebuggingTools::init_messenger(VkInstance vk_instance)
|
|
{
|
|
if (vk_debug_utils_messenger) {
|
|
return;
|
|
}
|
|
|
|
VKDevice &device = VKBackend::get().device;
|
|
if (!device.functions.vkCreateDebugUtilsMessenger) {
|
|
return;
|
|
}
|
|
|
|
VkDebugUtilsMessengerCreateInfoEXT create_info;
|
|
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
|
create_info.pNext = nullptr;
|
|
create_info.flags = 0;
|
|
create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
|
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
|
create_info.pfnUserCallback = messenger_callback;
|
|
create_info.pUserData = this;
|
|
device.functions.vkCreateDebugUtilsMessenger(
|
|
vk_instance, &create_info, nullptr, &vk_debug_utils_messenger);
|
|
return;
|
|
}
|
|
|
|
void VKDebuggingTools::destroy_messenger(VkInstance vk_instance)
|
|
{
|
|
if (vk_debug_utils_messenger == nullptr) {
|
|
return;
|
|
}
|
|
|
|
VKDevice &device = VKBackend::get().device;
|
|
device.functions.vkDestroyDebugUtilsMessenger(vk_instance, vk_debug_utils_messenger, nullptr);
|
|
vk_debug_utils_messenger = nullptr;
|
|
return;
|
|
}
|
|
|
|
}; // namespace blender::gpu::debug
|