From 407b16ab000b340006b1b2b2c4c574f1b634d3a9 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Fri, 7 Jul 2023 19:35:54 +0200 Subject: [PATCH] Debug CanvasItem redraw I wanted to add this tool for years and always forget. This command line option: ``` $ godot.exe -e --debug-canvas-item-redraw ``` Allows to see when a canvas item is redrawn. This helps find out if something in the UI is refreshing in a way it should not. Examples as such: * Signals causing more of the UI to redraw. * Container resizing causes more UI elements to redraw. * Something using a timer is redrawing all time time, which can go unnoticed. To my surprise, the editor UI is redrawing very efficiently. There is some weird stuff with the scene tabs, redrawing when the inspector changes but most things for the most part are fine. --- doc/classes/ProjectSettings.xml | 6 +++ drivers/gles3/rasterizer_canvas_gles3.h | 6 +++ editor/editor_run.cpp | 6 +++ editor/plugins/debugger_editor_plugin.cpp | 14 ++++++ editor/plugins/debugger_editor_plugin.h | 1 + main/main.cpp | 7 +++ .../rendering/dummy/rasterizer_canvas_dummy.h | 2 + servers/rendering/renderer_canvas_cull.cpp | 14 ++++++ servers/rendering/renderer_canvas_cull.h | 6 +++ servers/rendering/renderer_canvas_render.h | 5 ++ .../renderer_rd/renderer_canvas_render_rd.cpp | 47 +++++++++++++++++++ .../renderer_rd/renderer_canvas_render_rd.h | 6 +++ servers/rendering/rendering_server_default.h | 2 + servers/rendering_server.h | 2 + 14 files changed, 124 insertions(+) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index d5495ff7dcf..3c45a936ddc 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -414,6 +414,12 @@ Largest size limit (in power of 2) allowed when compressing using long-distance matching with Zstandard. Higher values can result in better compression, but will require more memory when compressing and decompressing. + + If canvas item redraw debugging is active, this color will be flashed on canvas items when they redraw. + + + If canvas item redraw debugging is active, this will be the time the flash will last each time they redraw. + If [code]true[/code], logs all output to files. diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index c1b3e20e33d..94c771cde7b 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -367,6 +367,12 @@ public: void set_time(double p_time); + virtual void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) override { + if (p_enabled) { + WARN_PRINT_ONCE("Debug CanvasItem Redraw is not available yet when using the GL Compatibility backend."); + } + } + static RasterizerCanvasGLES3 *get_singleton(); RasterizerCanvasGLES3(); ~RasterizerCanvasGLES3(); diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 42cd8585819..00db344b6a9 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -110,6 +110,8 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { bool debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false); bool debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); bool debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false); + bool debug_canvas_redraw = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_canvas_redraw", false); + if (debug_collisions) { args.push_back("--debug-collisions"); } @@ -126,6 +128,10 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { args.push_back("--debug-avoidance"); } + if (debug_canvas_redraw) { + args.push_back("--debug-canvas-item-redraw"); + } + if (p_write_movie != "") { args.push_back("--write-movie"); args.push_back(p_write_movie); diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp index 15829a55de6..b636ec4f5c8 100644 --- a/editor/plugins/debugger_editor_plugin.cpp +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -79,6 +79,10 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) { debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, avoidance objects shapes, radius and velocities will be visible in the running project.")); debug_menu->add_separator(); + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_canvas_redraw", TTR("Debug CanvasItem Redraws")), RUN_DEBUG_CANVAS_REDRAW); + debug_menu->set_item_tooltip(-1, + TTR("When this option is enabled, redraw requests of 2D objects will become visible (as a short flash) in the running project.\nThis is useful to troubleshoot low processor mode.")); + debug_menu->add_separator(); debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Synchronize Scene Changes")), RUN_LIVE_DEBUG); debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, any changes made to the scene in the editor will be replicated in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled.")); @@ -174,6 +178,12 @@ void DebuggerEditorPlugin::_menu_option(int p_option) { debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_AVOIDANCE), !ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_avoidance", !ischecked); + } break; + case RUN_DEBUG_CANVAS_REDRAW: { + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_CANVAS_REDRAW)); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_CANVAS_REDRAW), !ischecked); + EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_canvas_redraw", !ischecked); + } break; case RUN_RELOAD_SCRIPTS: { bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS)); @@ -213,6 +223,7 @@ void DebuggerEditorPlugin::_update_debug_options() { bool check_debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false); bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); bool check_debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false); + bool check_debug_canvas_redraw = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_canvas_redraw", false); bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true); bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true); bool check_server_keep_open = EditorSettings::get_singleton()->get_project_metadata("debug_options", "server_keep_open", false); @@ -236,6 +247,9 @@ void DebuggerEditorPlugin::_update_debug_options() { if (check_debug_avoidance) { _menu_option(RUN_DEBUG_AVOIDANCE); } + if (check_debug_canvas_redraw) { + _menu_option(RUN_DEBUG_CANVAS_REDRAW); + } if (check_live_debug) { _menu_option(RUN_LIVE_DEBUG); } diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h index eb8da7ca8e5..8d65dbd2e48 100644 --- a/editor/plugins/debugger_editor_plugin.h +++ b/editor/plugins/debugger_editor_plugin.h @@ -52,6 +52,7 @@ private: RUN_DEBUG_PATHS, RUN_DEBUG_NAVIGATION, RUN_DEBUG_AVOIDANCE, + RUN_DEBUG_CANVAS_REDRAW, RUN_DEPLOY_REMOTE_DEBUG, RUN_RELOAD_SCRIPTS, SERVER_KEEP_OPEN, diff --git a/main/main.cpp b/main/main.cpp index 0a9ebd4c610..72527c28dbf 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -209,6 +209,7 @@ static bool debug_collisions = false; static bool debug_paths = false; static bool debug_navigation = false; static bool debug_avoidance = false; +static bool debug_canvas_item_redraw = false; #endif static int max_fps = -1; static int frame_delay = 0; @@ -471,6 +472,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n"); OS::get_singleton()->print(" --debug-avoidance Show navigation avoidance debug visuals when running the scene.\n"); OS::get_singleton()->print(" --debug-stringnames Print all StringName allocations to stdout when the engine quits.\n"); + OS::get_singleton()->print(" --debug-canvas-item-redraw Display a rectangle each time a canvas item requests a redraw (useful to troubleshoot low processor mode).\n"); #endif OS::get_singleton()->print(" --max-fps Set a maximum number of frames per second rendered (can be used to limit power usage). A value of 0 results in unlimited framerate.\n"); OS::get_singleton()->print(" --frame-delay Simulate high CPU load (delay each frame by milliseconds). Do not use as a FPS limiter; use --max-fps instead.\n"); @@ -1414,6 +1416,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph debug_navigation = true; } else if (I->get() == "--debug-avoidance") { debug_avoidance = true; + } else if (I->get() == "--debug-canvas-item-redraw") { + debug_canvas_item_redraw = true; } else if (I->get() == "--debug-stringnames") { StringName::set_debug_stringnames(true); #endif @@ -3050,6 +3054,9 @@ bool Main::start() { NavigationServer3D::get_singleton()->set_active(true); NavigationServer3D::get_singleton()->set_debug_enabled(true); } + if (debug_canvas_item_redraw) { + RenderingServer::get_singleton()->canvas_item_set_debug_redraw(true); + } #endif if (single_threaded_scene) { diff --git a/servers/rendering/dummy/rasterizer_canvas_dummy.h b/servers/rendering/dummy/rasterizer_canvas_dummy.h index 455a6692774..862b941a73d 100644 --- a/servers/rendering/dummy/rasterizer_canvas_dummy.h +++ b/servers/rendering/dummy/rasterizer_canvas_dummy.h @@ -55,6 +55,8 @@ public: bool free(RID p_rid) override { return true; } void update() override {} + virtual void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) override {} + RasterizerCanvasDummy() {} ~RasterizerCanvasDummy() {} }; diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 2cb7f32cc30..b919bf4e05d 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -30,6 +30,7 @@ #include "renderer_canvas_cull.h" +#include "core/config/project_settings.h" #include "core/math/geometry_2d.h" #include "renderer_viewport.h" #include "rendering_server_default.h" @@ -1557,6 +1558,11 @@ void RendererCanvasCull::canvas_item_clear(RID p_item) { ERR_FAIL_COND(!canvas_item); canvas_item->clear(); +#ifdef DEBUG_ENABLED + if (debug_redraw) { + canvas_item->debug_redraw_time = debug_redraw_time; + } +#endif } void RendererCanvasCull::canvas_item_set_draw_index(RID p_item, int p_index) { @@ -1612,6 +1618,11 @@ void RendererCanvasCull::canvas_item_set_visibility_notifier(RID p_item, bool p_ } } +void RendererCanvasCull::canvas_item_set_debug_redraw(bool p_enabled) { + debug_redraw = p_enabled; + RSG::canvas_render->set_debug_redraw(p_enabled, debug_redraw_time, debug_redraw_color); +} + void RendererCanvasCull::canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin, bool p_fit_empty, float p_fit_margin, bool p_blur_mipmaps) { Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_COND(!canvas_item); @@ -2155,6 +2166,9 @@ RendererCanvasCull::RendererCanvasCull() { z_last_list = (RendererCanvasRender::Item **)memalloc(z_range * sizeof(RendererCanvasRender::Item *)); disable_scale = false; + + debug_redraw_time = GLOBAL_DEF("debug/canvas_items/debug_redraw_time", 1.0); + debug_redraw_color = GLOBAL_DEF("debug/canvas_items/debug_redraw_color", Color(1.0, 0.2, 0.2, 0.5)); } RendererCanvasCull::~RendererCanvasCull() { diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index 4f11d2c7b12..f81dabc1d2e 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -174,6 +174,10 @@ public: bool sdf_used = false; bool snapping_2d_transforms_to_pixel = false; + bool debug_redraw = false; + double debug_redraw_time = 0; + Color debug_redraw_color; + PagedAllocator visibility_notifier_allocator; SelfList::List visibility_notifier_list; @@ -260,6 +264,8 @@ public: void canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false); + void canvas_item_set_debug_redraw(bool p_enabled); + RID canvas_light_allocate(); void canvas_light_initialize(RID p_rid); diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index c30e53c29e7..ef4de9ce540 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -358,6 +358,9 @@ public: Command *last_command = nullptr; Vector blocks; uint32_t current_block; +#ifdef DEBUG_ENABLED + mutable double debug_redraw_time = 0; +#endif template T *alloc_command() { @@ -517,6 +520,8 @@ public: virtual bool free(RID p_rid) = 0; virtual void update() = 0; + virtual void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) = 0; + RendererCanvasRender() { singleton = this; } virtual ~RendererCanvasRender() {} }; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index d8c035a51cc..16e23741de2 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -957,7 +957,48 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend c = c->next; } +#ifdef DEBUG_ENABLED + if (debug_redraw && p_item->debug_redraw_time > 0.0) { + Color dc = debug_redraw_color; + dc.a *= p_item->debug_redraw_time / debug_redraw_time; + RID pipeline = pipeline_variants->variants[PIPELINE_LIGHT_MODE_DISABLED][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + + //bind textures + + _bind_canvas_texture(p_draw_list, RID(), current_filter, current_repeat, last_texture, push_constant, texpixel_size); + + Rect2 src_rect; + Rect2 dst_rect; + + dst_rect = Rect2(Vector2(), p_item->rect.size); + src_rect = Rect2(0, 0, 1, 1); + + push_constant.modulation[0] = dc.r; + push_constant.modulation[1] = dc.g; + push_constant.modulation[2] = dc.b; + push_constant.modulation[3] = dc.a; + + push_constant.src_rect[0] = src_rect.position.x; + push_constant.src_rect[1] = src_rect.position.y; + push_constant.src_rect[2] = src_rect.size.width; + push_constant.src_rect[3] = src_rect.size.height; + + push_constant.dst_rect[0] = dst_rect.position.x; + push_constant.dst_rect[1] = dst_rect.position.y; + push_constant.dst_rect[2] = dst_rect.size.width; + push_constant.dst_rect[3] = dst_rect.size.height; + + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant)); + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); + RD::get_singleton()->draw_list_draw(p_draw_list, true); + + p_item->debug_redraw_time -= RSG::rasterizer->get_frame_delta_time(); + + RenderingServerDefault::redraw_request(); + } +#endif if (current_clip && reclip) { //will make it re-enable clipping if needed afterwards current_clip = nullptr; @@ -2741,6 +2782,12 @@ void RendererCanvasRenderRD::set_shadow_texture_size(int p_size) { } } +void RendererCanvasRenderRD::set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) { + debug_redraw = p_enabled; + debug_redraw_time = p_time; + debug_redraw_color = p_color; +} + RendererCanvasRenderRD::~RendererCanvasRenderRD() { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); //canvas state diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 4c8cbd1c9f7..7dbcd903e64 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -418,6 +418,10 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer); + bool debug_redraw = false; + Color debug_redraw_color; + double debug_redraw_time = 1.0; + inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead. void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used); void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false); @@ -450,6 +454,8 @@ public: virtual void set_shadow_texture_size(int p_size); + void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color); + void set_time(double p_time); void update(); bool free(RID p_rid); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 9ad21753322..ad7a84dd073 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -891,6 +891,8 @@ public: FUNC6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool) + FUNC1(canvas_item_set_debug_redraw, bool) + FUNCRIDSPLIT(canvas_light) FUNC2(canvas_light_set_mode, RID, CanvasLightMode) diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 1528a957ce4..cbd54d993e2 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1393,6 +1393,8 @@ public: virtual void canvas_item_set_canvas_group_mode(RID p_item, CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false) = 0; + virtual void canvas_item_set_debug_redraw(bool p_enabled) = 0; + /* CANVAS LIGHT */ virtual RID canvas_light_create() = 0;