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.
This commit is contained in:
Juan Linietsky 2023-07-07 19:35:54 +02:00 committed by Rémi Verschelde
parent 713bfaf5ea
commit 407b16ab00
No known key found for this signature in database
GPG key ID: C3336907360768E1
14 changed files with 124 additions and 0 deletions

View file

@ -414,6 +414,12 @@
<member name="compression/formats/zstd/window_log_size" type="int" setter="" getter="" default="27">
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.
</member>
<member name="debug/canvas_items/debug_redraw_color" type="Color" setter="" getter="" default="Color(1, 0.2, 0.2, 0.5)">
If canvas item redraw debugging is active, this color will be flashed on canvas items when they redraw.
</member>
<member name="debug/canvas_items/debug_redraw_time" type="float" setter="" getter="" default="1.0">
If canvas item redraw debugging is active, this will be the time the flash will last each time they redraw.
</member>
<member name="debug/file_logging/enable_file_logging" type="bool" setter="" getter="" default="false">
If [code]true[/code], logs all output to files.
</member>

View file

@ -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();

View file

@ -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);

View file

@ -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);
}

View file

@ -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,

View file

@ -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 <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 <ms> Simulate high CPU load (delay each frame by <ms> 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) {

View file

@ -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() {}
};

View file

@ -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() {

View file

@ -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<Item::VisibilityNotifierData> visibility_notifier_allocator;
SelfList<Item::VisibilityNotifierData>::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);

View file

@ -358,6 +358,9 @@ public:
Command *last_command = nullptr;
Vector<CommandBlock> blocks;
uint32_t current_block;
#ifdef DEBUG_ENABLED
mutable double debug_redraw_time = 0;
#endif
template <class T>
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() {}
};

View file

@ -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

View file

@ -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 *&current_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);

View file

@ -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)

View file

@ -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;