You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-05 12:10:55 +00:00
Fix race conditions in breadcrumbs
Adds "--accurate-breadcrumbs" CLI command Additionally, leave out breadcrumbs code in non-debug, non-dev builds. Fix regression introduced in #98388 where command_insert_breadcrumb() is called even in non-debug builds. Fixes #98338
This commit is contained in:
@@ -43,6 +43,10 @@
|
||||
/**** GENERIC ****/
|
||||
/*****************/
|
||||
|
||||
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
|
||||
static const uint32_t BREADCRUMB_BUFFER_ENTRIES = 512u;
|
||||
#endif
|
||||
|
||||
static const VkFormat RD_TO_VK_FORMAT[RDD::DATA_FORMAT_MAX] = {
|
||||
VK_FORMAT_R4G4_UNORM_PACK8,
|
||||
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
|
||||
@@ -1370,7 +1374,10 @@ Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
|
||||
max_descriptor_sets_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool");
|
||||
breadcrumb_buffer = buffer_create(sizeof(uint32_t), BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
|
||||
|
||||
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
|
||||
breadcrumb_buffer = buffer_create(2u * sizeof(uint32_t) * BREADCRUMB_BUFFER_ENTRIES, BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -5004,10 +5011,65 @@ void RenderingDeviceDriverVulkan::command_end_label(CommandBufferID p_cmd_buffer
|
||||
/**** DEBUG *****/
|
||||
/****************/
|
||||
void RenderingDeviceDriverVulkan::command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) {
|
||||
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
|
||||
if (p_data == BreadcrumbMarker::NONE) {
|
||||
return;
|
||||
}
|
||||
vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, 0, sizeof(uint32_t), p_data);
|
||||
|
||||
if (Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) {
|
||||
// Force a full barrier so commands are not executed in parallel.
|
||||
// This will mean that the last breadcrumb to see was actually the
|
||||
// last (group of) command to be executed (hence, the one causing the crash).
|
||||
VkMemoryBarrier memoryBarrier;
|
||||
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
||||
memoryBarrier.pNext = nullptr;
|
||||
memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
|
||||
VK_ACCESS_INDEX_READ_BIT |
|
||||
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
|
||||
VK_ACCESS_UNIFORM_READ_BIT |
|
||||
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_SHADER_READ_BIT |
|
||||
VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT |
|
||||
VK_ACCESS_HOST_READ_BIT |
|
||||
VK_ACCESS_HOST_WRITE_BIT;
|
||||
memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
|
||||
VK_ACCESS_INDEX_READ_BIT |
|
||||
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
|
||||
VK_ACCESS_UNIFORM_READ_BIT |
|
||||
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_SHADER_READ_BIT |
|
||||
VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT |
|
||||
VK_ACCESS_HOST_READ_BIT |
|
||||
VK_ACCESS_HOST_WRITE_BIT;
|
||||
|
||||
vkCmdPipelineBarrier(
|
||||
(VkCommandBuffer)p_cmd_buffer.id,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
0, 1u, &memoryBarrier, 0u, nullptr, 0u, nullptr);
|
||||
}
|
||||
|
||||
// We write to a circular buffer. If you're getting barrier sync errors here,
|
||||
// increase the value of BREADCRUMB_BUFFER_ENTRIES.
|
||||
vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset, sizeof(uint32_t), breadcrumb_id++);
|
||||
vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset + sizeof(uint32_t), sizeof(uint32_t), p_data);
|
||||
breadcrumb_offset += sizeof(uint32_t) * 2u;
|
||||
if (breadcrumb_offset >= BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u) {
|
||||
breadcrumb_offset = 0u;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverVulkan::on_device_lost() const {
|
||||
@@ -5089,64 +5151,121 @@ void RenderingDeviceDriverVulkan::on_device_lost() const {
|
||||
|
||||
void RenderingDeviceDriverVulkan::print_lost_device_info() {
|
||||
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
|
||||
void *breadcrumb_ptr;
|
||||
vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t));
|
||||
vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t));
|
||||
|
||||
vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &breadcrumb_ptr);
|
||||
uint32_t last_breadcrumb = *(uint32_t *)breadcrumb_ptr;
|
||||
vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle);
|
||||
uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1));
|
||||
uint32_t user_data = last_breadcrumb & ((1 << 16) - 1);
|
||||
String error_msg = "Last known breadcrumb: ";
|
||||
|
||||
switch (phase) {
|
||||
case BreadcrumbMarker::ALPHA_PASS:
|
||||
error_msg += "ALPHA_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::BLIT_PASS:
|
||||
error_msg += "BLIT_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::DEBUG_PASS:
|
||||
error_msg += "DEBUG_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::LIGHTMAPPER_PASS:
|
||||
error_msg += "LIGHTMAPPER_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::OPAQUE_PASS:
|
||||
error_msg += "OPAQUE_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::POST_PROCESSING_PASS:
|
||||
error_msg += "POST_PROCESSING_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::REFLECTION_PROBES:
|
||||
error_msg += "REFLECTION_PROBES";
|
||||
break;
|
||||
case BreadcrumbMarker::SHADOW_PASS_CUBE:
|
||||
error_msg += "SHADOW_PASS_CUBE";
|
||||
break;
|
||||
case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL:
|
||||
error_msg += "SHADOW_PASS_DIRECTIONAL";
|
||||
break;
|
||||
case BreadcrumbMarker::SKY_PASS:
|
||||
error_msg += "SKY_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::TRANSPARENT_PASS:
|
||||
error_msg += "TRANSPARENT_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::UI_PASS:
|
||||
error_msg += "UI_PASS";
|
||||
break;
|
||||
default:
|
||||
error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')';
|
||||
break;
|
||||
{
|
||||
String error_msg = "Printing last known breadcrumbs in reverse order (last executed first).";
|
||||
if (!Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) {
|
||||
error_msg += "\nSome of them might be inaccurate. Try running with --accurate-breadcrumbs for precise information.";
|
||||
}
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg);
|
||||
}
|
||||
|
||||
if (user_data != 0) {
|
||||
error_msg += " | User data: " + itos(user_data);
|
||||
uint8_t *breadcrumb_ptr = nullptr;
|
||||
VkResult map_result = VK_SUCCESS;
|
||||
|
||||
vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u);
|
||||
vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u);
|
||||
{
|
||||
void *ptr = nullptr;
|
||||
map_result = vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &ptr);
|
||||
breadcrumb_ptr = reinterpret_cast<uint8_t *>(ptr);
|
||||
}
|
||||
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg);
|
||||
if (breadcrumb_ptr && map_result == VK_SUCCESS) {
|
||||
uint32_t last_breadcrumb_offset = 0;
|
||||
{
|
||||
_err_print_error_asap("Searching last breadcrumb. We've sent up to ID: " + itos(breadcrumb_id - 1u));
|
||||
|
||||
// Scan the whole buffer to find the offset with the highest ID.
|
||||
// That means that was the last one to be written.
|
||||
//
|
||||
// We use "breadcrumb_id - id" to account for wraparound.
|
||||
// e.g. breadcrumb_id = 2 and id = 4294967294; then 2 - 4294967294 = 4.
|
||||
// The one with the smallest difference is the closest to breadcrumb_id, which means it's
|
||||
// the last written command.
|
||||
uint32_t biggest_id = 0u;
|
||||
uint32_t smallest_id_diff = std::numeric_limits<uint32_t>::max();
|
||||
const uint32_t *breadcrumb_ptr32 = reinterpret_cast<const uint32_t *>(breadcrumb_ptr);
|
||||
for (size_t i = 0u; i < BREADCRUMB_BUFFER_ENTRIES; ++i) {
|
||||
const uint32_t id = breadcrumb_ptr32[i * 2u];
|
||||
const uint32_t id_diff = breadcrumb_id - id;
|
||||
if (id_diff < smallest_id_diff) {
|
||||
biggest_id = i;
|
||||
smallest_id_diff = id_diff;
|
||||
}
|
||||
}
|
||||
|
||||
_err_print_error_asap("Last breadcrumb ID found: " + itos(breadcrumb_ptr32[biggest_id * 2u]));
|
||||
|
||||
last_breadcrumb_offset = biggest_id * sizeof(uint32_t) * 2u;
|
||||
}
|
||||
|
||||
const size_t entries_to_print = 8u; // Note: The value is arbitrary.
|
||||
for (size_t i = 0u; i < entries_to_print; ++i) {
|
||||
const uint32_t last_breadcrumb = *reinterpret_cast<uint32_t *>(breadcrumb_ptr + last_breadcrumb_offset + sizeof(uint32_t));
|
||||
const uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1));
|
||||
const uint32_t user_data = last_breadcrumb & ((1 << 16) - 1);
|
||||
String error_msg = "Last known breadcrumb: ";
|
||||
|
||||
switch (phase) {
|
||||
case BreadcrumbMarker::ALPHA_PASS:
|
||||
error_msg += "ALPHA_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::BLIT_PASS:
|
||||
error_msg += "BLIT_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::DEBUG_PASS:
|
||||
error_msg += "DEBUG_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::LIGHTMAPPER_PASS:
|
||||
error_msg += "LIGHTMAPPER_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::OPAQUE_PASS:
|
||||
error_msg += "OPAQUE_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::POST_PROCESSING_PASS:
|
||||
error_msg += "POST_PROCESSING_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::REFLECTION_PROBES:
|
||||
error_msg += "REFLECTION_PROBES";
|
||||
break;
|
||||
case BreadcrumbMarker::SHADOW_PASS_CUBE:
|
||||
error_msg += "SHADOW_PASS_CUBE";
|
||||
break;
|
||||
case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL:
|
||||
error_msg += "SHADOW_PASS_DIRECTIONAL";
|
||||
break;
|
||||
case BreadcrumbMarker::SKY_PASS:
|
||||
error_msg += "SKY_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::TRANSPARENT_PASS:
|
||||
error_msg += "TRANSPARENT_PASS";
|
||||
break;
|
||||
case BreadcrumbMarker::UI_PASS:
|
||||
error_msg += "UI_PASS";
|
||||
break;
|
||||
default:
|
||||
error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')';
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_data != 0) {
|
||||
error_msg += " | User data: " + itos(user_data);
|
||||
}
|
||||
|
||||
_err_print_error_asap(error_msg);
|
||||
|
||||
if (last_breadcrumb_offset == 0u) {
|
||||
// Decrement last_breadcrumb_idx, wrapping underflow.
|
||||
last_breadcrumb_offset = BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u;
|
||||
}
|
||||
last_breadcrumb_offset -= sizeof(uint32_t) * 2u;
|
||||
}
|
||||
|
||||
vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle);
|
||||
breadcrumb_ptr = nullptr;
|
||||
} else {
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Couldn't map breadcrumb buffer. VkResult = " + itos(map_result));
|
||||
}
|
||||
#endif
|
||||
on_device_lost();
|
||||
}
|
||||
@@ -5417,7 +5536,9 @@ RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverV
|
||||
}
|
||||
|
||||
RenderingDeviceDriverVulkan::~RenderingDeviceDriverVulkan() {
|
||||
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
|
||||
buffer_free(breadcrumb_buffer);
|
||||
#endif
|
||||
|
||||
while (small_allocs_pools.size()) {
|
||||
HashMap<uint32_t, VmaPool>::Iterator E = small_allocs_pools.begin();
|
||||
|
||||
Reference in New Issue
Block a user