diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index d5495ff7dcf2..3c45a936ddc2 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 c1b3e20e33d1..94c771cde7bb 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -367,6 +367,12 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { 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 42cd85858192..00db344b6a9a 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 15829a55de60..b636ec4f5c88 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 eb8da7ca8e53..8d65dbd2e482 100644 --- a/editor/plugins/debugger_editor_plugin.h +++ b/editor/plugins/debugger_editor_plugin.h @@ -52,6 +52,7 @@ class DebuggerEditorPlugin : public EditorPlugin { 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 0a9ebd4c6103..72527c28dbfb 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 455a6692774f..862b941a73d4 100644 --- a/servers/rendering/dummy/rasterizer_canvas_dummy.h +++ b/servers/rendering/dummy/rasterizer_canvas_dummy.h @@ -55,6 +55,8 @@ class RasterizerCanvasDummy : public RendererCanvasRender { 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 2cb7f32cc30a..b919bf4e05d6 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 4f11d2c7b12b..f81dabc1d2ea 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -174,6 +174,10 @@ class RendererCanvasCull { 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 @@ class RendererCanvasCull { 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 c30e53c29e7e..ef4de9ce5406 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -358,6 +358,9 @@ class RendererCanvasRender { 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 @@ class RendererCanvasRender { 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 d8c035a51cc2..16e23741de28 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 4c8cbd1c9f7b..7dbcd903e64f 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 @@ class RendererCanvasRenderRD : public RendererCanvasRender { 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 9ad217533220..ad7a84dd0730 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -891,6 +891,8 @@ class RenderingServerDefault : public RenderingServer { 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 1528a957ce45..cbd54d993e2a 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1393,6 +1393,8 @@ class RenderingServer : public Object { 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;