Skip to content

Commit edad871

Browse files
committed
Merge pull request #98425 from darksylinc/matias-breadcrumbs-race-fix
Fix race conditions in breadcrumbs
2 parents 15bec19 + 668c9b7 commit edad871

File tree

9 files changed

+236
-59
lines changed

9 files changed

+236
-59
lines changed

core/config/engine.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@ bool Engine::is_extra_gpu_memory_tracking_enabled() const {
267267
return extra_gpu_memory_tracking;
268268
}
269269

270+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
271+
bool Engine::is_accurate_breadcrumbs_enabled() const {
272+
return accurate_breadcrumbs;
273+
}
274+
#endif
275+
270276
void Engine::set_print_to_stdout(bool p_enabled) {
271277
CoreGlobals::print_line_enabled = p_enabled;
272278
}

core/config/engine.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ class Engine {
7373
bool use_validation_layers = false;
7474
bool generate_spirv_debug_info = false;
7575
bool extra_gpu_memory_tracking = false;
76+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
77+
bool accurate_breadcrumbs = false;
78+
#endif
7679
int32_t gpu_idx = -1;
7780

7881
uint64_t _process_frames = 0;
@@ -186,6 +189,9 @@ class Engine {
186189
bool is_validation_layers_enabled() const;
187190
bool is_generate_spirv_debug_info_enabled() const;
188191
bool is_extra_gpu_memory_tracking_enabled() const;
192+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
193+
bool is_accurate_breadcrumbs_enabled() const;
194+
#endif
189195
int32_t get_gpu_index() const;
190196

191197
void increment_frames_drawn();

core/error/error_macros.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,28 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
107107
_global_unlock();
108108
}
109109

110+
// For printing errors when we may crash at any point, so we must flush ASAP a lot of lines
111+
// but we don't want to make it noisy by printing lots of file & line info (because it's already
112+
// been printing by a preceding _err_print_error).
113+
void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) {
114+
if (OS::get_singleton()) {
115+
OS::get_singleton()->printerr("ERROR: %s\n", p_error.utf8().get_data());
116+
} else {
117+
// Fallback if errors happen before OS init or after it's destroyed.
118+
const char *err_details = p_error.utf8().get_data();
119+
fprintf(stderr, "ERROR: %s\n", err_details);
120+
}
121+
122+
_global_lock();
123+
ErrorHandlerList *l = error_handler_list;
124+
while (l) {
125+
l->errfunc(l->userdata, "", "", 0, p_error.utf8().get_data(), "", false, p_type);
126+
l = l->next;
127+
}
128+
129+
_global_unlock();
130+
}
131+
110132
// Errors with message. (All combinations of p_error and p_message as String or char*.)
111133
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) {
112134
_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type);

core/error/error_macros.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
6868
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
6969
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
7070
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
71+
void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
7172
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false);
7273
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false);
7374
void _err_flush_stdout();

drivers/vulkan/rendering_device_driver_vulkan.cpp

Lines changed: 178 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@
4343
/**** GENERIC ****/
4444
/*****************/
4545

46+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
47+
static const uint32_t BREADCRUMB_BUFFER_ENTRIES = 512u;
48+
#endif
49+
4650
static const VkFormat RD_TO_VK_FORMAT[RDD::DATA_FORMAT_MAX] = {
4751
VK_FORMAT_R4G4_UNORM_PACK8,
4852
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
@@ -1370,7 +1374,10 @@ Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t
13701374
ERR_FAIL_COND_V(err != OK, err);
13711375

13721376
max_descriptor_sets_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool");
1373-
breadcrumb_buffer = buffer_create(sizeof(uint32_t), BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
1377+
1378+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
1379+
breadcrumb_buffer = buffer_create(2u * sizeof(uint32_t) * BREADCRUMB_BUFFER_ENTRIES, BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
1380+
#endif
13741381

13751382
return OK;
13761383
}
@@ -5004,10 +5011,65 @@ void RenderingDeviceDriverVulkan::command_end_label(CommandBufferID p_cmd_buffer
50045011
/**** DEBUG *****/
50055012
/****************/
50065013
void RenderingDeviceDriverVulkan::command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) {
5014+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
50075015
if (p_data == BreadcrumbMarker::NONE) {
50085016
return;
50095017
}
5010-
vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, 0, sizeof(uint32_t), p_data);
5018+
5019+
if (Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) {
5020+
// Force a full barrier so commands are not executed in parallel.
5021+
// This will mean that the last breadcrumb to see was actually the
5022+
// last (group of) command to be executed (hence, the one causing the crash).
5023+
VkMemoryBarrier memoryBarrier;
5024+
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
5025+
memoryBarrier.pNext = nullptr;
5026+
memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
5027+
VK_ACCESS_INDEX_READ_BIT |
5028+
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
5029+
VK_ACCESS_UNIFORM_READ_BIT |
5030+
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
5031+
VK_ACCESS_SHADER_READ_BIT |
5032+
VK_ACCESS_SHADER_WRITE_BIT |
5033+
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
5034+
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
5035+
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
5036+
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
5037+
VK_ACCESS_TRANSFER_READ_BIT |
5038+
VK_ACCESS_TRANSFER_WRITE_BIT |
5039+
VK_ACCESS_HOST_READ_BIT |
5040+
VK_ACCESS_HOST_WRITE_BIT;
5041+
memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
5042+
VK_ACCESS_INDEX_READ_BIT |
5043+
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
5044+
VK_ACCESS_UNIFORM_READ_BIT |
5045+
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
5046+
VK_ACCESS_SHADER_READ_BIT |
5047+
VK_ACCESS_SHADER_WRITE_BIT |
5048+
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
5049+
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
5050+
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
5051+
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
5052+
VK_ACCESS_TRANSFER_READ_BIT |
5053+
VK_ACCESS_TRANSFER_WRITE_BIT |
5054+
VK_ACCESS_HOST_READ_BIT |
5055+
VK_ACCESS_HOST_WRITE_BIT;
5056+
5057+
vkCmdPipelineBarrier(
5058+
(VkCommandBuffer)p_cmd_buffer.id,
5059+
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
5060+
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
5061+
0, 1u, &memoryBarrier, 0u, nullptr, 0u, nullptr);
5062+
}
5063+
5064+
// We write to a circular buffer. If you're getting barrier sync errors here,
5065+
// increase the value of BREADCRUMB_BUFFER_ENTRIES.
5066+
vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset, sizeof(uint32_t), breadcrumb_id++);
5067+
vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset + sizeof(uint32_t), sizeof(uint32_t), p_data);
5068+
breadcrumb_offset += sizeof(uint32_t) * 2u;
5069+
if (breadcrumb_offset >= BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u) {
5070+
breadcrumb_offset = 0u;
5071+
}
5072+
#endif
50115073
}
50125074

50135075
void RenderingDeviceDriverVulkan::on_device_lost() const {
@@ -5089,64 +5151,121 @@ void RenderingDeviceDriverVulkan::on_device_lost() const {
50895151

50905152
void RenderingDeviceDriverVulkan::print_lost_device_info() {
50915153
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
5092-
void *breadcrumb_ptr;
5093-
vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t));
5094-
vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t));
5095-
5096-
vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &breadcrumb_ptr);
5097-
uint32_t last_breadcrumb = *(uint32_t *)breadcrumb_ptr;
5098-
vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle);
5099-
uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1));
5100-
uint32_t user_data = last_breadcrumb & ((1 << 16) - 1);
5101-
String error_msg = "Last known breadcrumb: ";
5102-
5103-
switch (phase) {
5104-
case BreadcrumbMarker::ALPHA_PASS:
5105-
error_msg += "ALPHA_PASS";
5106-
break;
5107-
case BreadcrumbMarker::BLIT_PASS:
5108-
error_msg += "BLIT_PASS";
5109-
break;
5110-
case BreadcrumbMarker::DEBUG_PASS:
5111-
error_msg += "DEBUG_PASS";
5112-
break;
5113-
case BreadcrumbMarker::LIGHTMAPPER_PASS:
5114-
error_msg += "LIGHTMAPPER_PASS";
5115-
break;
5116-
case BreadcrumbMarker::OPAQUE_PASS:
5117-
error_msg += "OPAQUE_PASS";
5118-
break;
5119-
case BreadcrumbMarker::POST_PROCESSING_PASS:
5120-
error_msg += "POST_PROCESSING_PASS";
5121-
break;
5122-
case BreadcrumbMarker::REFLECTION_PROBES:
5123-
error_msg += "REFLECTION_PROBES";
5124-
break;
5125-
case BreadcrumbMarker::SHADOW_PASS_CUBE:
5126-
error_msg += "SHADOW_PASS_CUBE";
5127-
break;
5128-
case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL:
5129-
error_msg += "SHADOW_PASS_DIRECTIONAL";
5130-
break;
5131-
case BreadcrumbMarker::SKY_PASS:
5132-
error_msg += "SKY_PASS";
5133-
break;
5134-
case BreadcrumbMarker::TRANSPARENT_PASS:
5135-
error_msg += "TRANSPARENT_PASS";
5136-
break;
5137-
case BreadcrumbMarker::UI_PASS:
5138-
error_msg += "UI_PASS";
5139-
break;
5140-
default:
5141-
error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')';
5142-
break;
5154+
{
5155+
String error_msg = "Printing last known breadcrumbs in reverse order (last executed first).";
5156+
if (!Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) {
5157+
error_msg += "\nSome of them might be inaccurate. Try running with --accurate-breadcrumbs for precise information.";
5158+
}
5159+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg);
51435160
}
51445161

5145-
if (user_data != 0) {
5146-
error_msg += " | User data: " + itos(user_data);
5147-
}
5162+
uint8_t *breadcrumb_ptr = nullptr;
5163+
VkResult map_result = VK_SUCCESS;
5164+
5165+
vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u);
5166+
vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u);
5167+
{
5168+
void *ptr = nullptr;
5169+
map_result = vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &ptr);
5170+
breadcrumb_ptr = reinterpret_cast<uint8_t *>(ptr);
5171+
}
5172+
5173+
if (breadcrumb_ptr && map_result == VK_SUCCESS) {
5174+
uint32_t last_breadcrumb_offset = 0;
5175+
{
5176+
_err_print_error_asap("Searching last breadcrumb. We've sent up to ID: " + itos(breadcrumb_id - 1u));
5177+
5178+
// Scan the whole buffer to find the offset with the highest ID.
5179+
// That means that was the last one to be written.
5180+
//
5181+
// We use "breadcrumb_id - id" to account for wraparound.
5182+
// e.g. breadcrumb_id = 2 and id = 4294967294; then 2 - 4294967294 = 4.
5183+
// The one with the smallest difference is the closest to breadcrumb_id, which means it's
5184+
// the last written command.
5185+
uint32_t biggest_id = 0u;
5186+
uint32_t smallest_id_diff = std::numeric_limits<uint32_t>::max();
5187+
const uint32_t *breadcrumb_ptr32 = reinterpret_cast<const uint32_t *>(breadcrumb_ptr);
5188+
for (size_t i = 0u; i < BREADCRUMB_BUFFER_ENTRIES; ++i) {
5189+
const uint32_t id = breadcrumb_ptr32[i * 2u];
5190+
const uint32_t id_diff = breadcrumb_id - id;
5191+
if (id_diff < smallest_id_diff) {
5192+
biggest_id = i;
5193+
smallest_id_diff = id_diff;
5194+
}
5195+
}
5196+
5197+
_err_print_error_asap("Last breadcrumb ID found: " + itos(breadcrumb_ptr32[biggest_id * 2u]));
5198+
5199+
last_breadcrumb_offset = biggest_id * sizeof(uint32_t) * 2u;
5200+
}
5201+
5202+
const size_t entries_to_print = 8u; // Note: The value is arbitrary.
5203+
for (size_t i = 0u; i < entries_to_print; ++i) {
5204+
const uint32_t last_breadcrumb = *reinterpret_cast<uint32_t *>(breadcrumb_ptr + last_breadcrumb_offset + sizeof(uint32_t));
5205+
const uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1));
5206+
const uint32_t user_data = last_breadcrumb & ((1 << 16) - 1);
5207+
String error_msg = "Last known breadcrumb: ";
5208+
5209+
switch (phase) {
5210+
case BreadcrumbMarker::ALPHA_PASS:
5211+
error_msg += "ALPHA_PASS";
5212+
break;
5213+
case BreadcrumbMarker::BLIT_PASS:
5214+
error_msg += "BLIT_PASS";
5215+
break;
5216+
case BreadcrumbMarker::DEBUG_PASS:
5217+
error_msg += "DEBUG_PASS";
5218+
break;
5219+
case BreadcrumbMarker::LIGHTMAPPER_PASS:
5220+
error_msg += "LIGHTMAPPER_PASS";
5221+
break;
5222+
case BreadcrumbMarker::OPAQUE_PASS:
5223+
error_msg += "OPAQUE_PASS";
5224+
break;
5225+
case BreadcrumbMarker::POST_PROCESSING_PASS:
5226+
error_msg += "POST_PROCESSING_PASS";
5227+
break;
5228+
case BreadcrumbMarker::REFLECTION_PROBES:
5229+
error_msg += "REFLECTION_PROBES";
5230+
break;
5231+
case BreadcrumbMarker::SHADOW_PASS_CUBE:
5232+
error_msg += "SHADOW_PASS_CUBE";
5233+
break;
5234+
case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL:
5235+
error_msg += "SHADOW_PASS_DIRECTIONAL";
5236+
break;
5237+
case BreadcrumbMarker::SKY_PASS:
5238+
error_msg += "SKY_PASS";
5239+
break;
5240+
case BreadcrumbMarker::TRANSPARENT_PASS:
5241+
error_msg += "TRANSPARENT_PASS";
5242+
break;
5243+
case BreadcrumbMarker::UI_PASS:
5244+
error_msg += "UI_PASS";
5245+
break;
5246+
default:
5247+
error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')';
5248+
break;
5249+
}
5250+
5251+
if (user_data != 0) {
5252+
error_msg += " | User data: " + itos(user_data);
5253+
}
5254+
5255+
_err_print_error_asap(error_msg);
51485256

5149-
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg);
5257+
if (last_breadcrumb_offset == 0u) {
5258+
// Decrement last_breadcrumb_idx, wrapping underflow.
5259+
last_breadcrumb_offset = BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u;
5260+
}
5261+
last_breadcrumb_offset -= sizeof(uint32_t) * 2u;
5262+
}
5263+
5264+
vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle);
5265+
breadcrumb_ptr = nullptr;
5266+
} else {
5267+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Couldn't map breadcrumb buffer. VkResult = " + itos(map_result));
5268+
}
51505269
#endif
51515270
on_device_lost();
51525271
}
@@ -5417,7 +5536,9 @@ RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverV
54175536
}
54185537

54195538
RenderingDeviceDriverVulkan::~RenderingDeviceDriverVulkan() {
5539+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
54205540
buffer_free(breadcrumb_buffer);
5541+
#endif
54215542

54225543
while (small_allocs_pools.size()) {
54235544
HashMap<uint32_t, VmaPool>::Iterator E = small_allocs_pools.begin();

drivers/vulkan/rendering_device_driver_vulkan.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,12 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
172172
VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index);
173173

174174
private:
175+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
176+
// It's a circular buffer.
175177
BufferID breadcrumb_buffer;
178+
uint32_t breadcrumb_offset = 0u;
179+
uint32_t breadcrumb_id = 0u;
180+
#endif
176181

177182
public:
178183
/*****************/

main/main.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ void Main::print_help(const char *p_binary) {
630630
print_help_option("--generate-spirv-debug-info", "Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
631631
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
632632
print_help_option("--extra-gpu-memory-tracking", "Enables additional memory tracking (see class reference for `RenderingDevice.get_driver_and_device_memory_report()` and linked methods). Currently only implemented for Vulkan. Enabling this feature may cause crashes on some systems due to buggy drivers or bugs in the Vulkan Loader. See https://github.com/godotengine/godot/issues/95967\n");
633+
print_help_option("--accurate-breadcrumbs", "Force barriers between breadcrumbs. Useful for narrowing down a command causing GPU resets. Currently only implemented for Vulkan.\n");
633634
#endif
634635
print_help_option("--remote-debug <uri>", "Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
635636
print_help_option("--single-threaded-scene", "Force scene tree to run in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
@@ -1236,8 +1237,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
12361237
#endif
12371238
} else if (arg == "--generate-spirv-debug-info") {
12381239
Engine::singleton->generate_spirv_debug_info = true;
1240+
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
12391241
} else if (arg == "--extra-gpu-memory-tracking") {
12401242
Engine::singleton->extra_gpu_memory_tracking = true;
1243+
} else if (arg == "--accurate-breadcrumbs") {
1244+
Engine::singleton->accurate_breadcrumbs = true;
1245+
#endif
12411246
} else if (arg == "--tablet-driver") {
12421247
if (N) {
12431248
tablet_driver = N->get();

0 commit comments

Comments
 (0)