└── addons └── debug_menu ├── LICENSE.md ├── debug_menu.gd ├── debug_menu.tscn ├── plugin.cfg └── plugin.gd /addons/debug_menu/LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright © 2023-present Hugo Locurcio and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/debug_menu/debug_menu.gd: -------------------------------------------------------------------------------- 1 | extends CanvasLayer 2 | 3 | @export var fps: Label 4 | @export var frame_time: Label 5 | @export var frame_number: Label 6 | @export var frame_history_total_avg: Label 7 | @export var frame_history_total_min: Label 8 | @export var frame_history_total_max: Label 9 | @export var frame_history_total_last: Label 10 | @export var frame_history_cpu_avg: Label 11 | @export var frame_history_cpu_min: Label 12 | @export var frame_history_cpu_max: Label 13 | @export var frame_history_cpu_last: Label 14 | @export var frame_history_gpu_avg: Label 15 | @export var frame_history_gpu_min: Label 16 | @export var frame_history_gpu_max: Label 17 | @export var frame_history_gpu_last: Label 18 | @export var fps_graph: Panel 19 | @export var total_graph: Panel 20 | @export var cpu_graph: Panel 21 | @export var gpu_graph: Panel 22 | @export var information: Label 23 | @export var settings: Label 24 | 25 | ## The number of frames to keep in history for graph drawing and best/worst calculations. 26 | ## Currently, this also affects how FPS is measured. 27 | const HISTORY_NUM_FRAMES = 150 28 | 29 | const GRAPH_SIZE = Vector2(150, 25) 30 | const GRAPH_MIN_FPS = 10 31 | const GRAPH_MAX_FPS = 160 32 | const GRAPH_MIN_FRAMETIME = 1.0 / GRAPH_MIN_FPS 33 | const GRAPH_MAX_FRAMETIME = 1.0 / GRAPH_MAX_FPS 34 | 35 | ## Debug menu display style. 36 | enum Style { 37 | HIDDEN, ## Debug menu is hidden. 38 | VISIBLE_COMPACT, ## Debug menu is visible, with only the FPS, FPS cap (if any) and time taken to render the last frame. 39 | VISIBLE_DETAILED, ## Debug menu is visible with full information, including graphs. 40 | MAX, ## Represents the size of the Style enum. 41 | } 42 | 43 | ## The style to use when drawing the debug menu. 44 | var style := Style.HIDDEN: 45 | set(value): 46 | style = value 47 | match style: 48 | Style.HIDDEN: 49 | visible = false 50 | Style.VISIBLE_COMPACT, Style.VISIBLE_DETAILED: 51 | visible = true 52 | frame_number.visible = style == Style.VISIBLE_DETAILED 53 | $DebugMenu/VBoxContainer/FrameTimeHistory.visible = style == Style.VISIBLE_DETAILED 54 | $DebugMenu/VBoxContainer/FPSGraph.visible = style == Style.VISIBLE_DETAILED 55 | $DebugMenu/VBoxContainer/TotalGraph.visible = style == Style.VISIBLE_DETAILED 56 | $DebugMenu/VBoxContainer/CPUGraph.visible = style == Style.VISIBLE_DETAILED 57 | $DebugMenu/VBoxContainer/GPUGraph.visible = style == Style.VISIBLE_DETAILED 58 | information.visible = style == Style.VISIBLE_DETAILED 59 | settings.visible = style == Style.VISIBLE_DETAILED 60 | 61 | # Value of `Time.get_ticks_usec()` on the previous frame. 62 | var last_tick := 0 63 | 64 | var thread := Thread.new() 65 | 66 | ## Returns the sum of all values of an array (use as a parameter to `Array.reduce()`). 67 | var sum_func := func avg(accum: float, number: float) -> float: return accum + number 68 | 69 | # History of the last `HISTORY_NUM_FRAMES` rendered frames. 70 | var frame_history_total: Array[float] = [] 71 | var frame_history_cpu: Array[float] = [] 72 | var frame_history_gpu: Array[float] = [] 73 | var fps_history: Array[float] = [] # Only used for graphs. 74 | 75 | var frametime_avg := GRAPH_MIN_FRAMETIME 76 | var frametime_cpu_avg := GRAPH_MAX_FRAMETIME 77 | var frametime_gpu_avg := GRAPH_MIN_FRAMETIME 78 | var frames_per_second := float(GRAPH_MIN_FPS) 79 | var frame_time_gradient := Gradient.new() 80 | 81 | func _init() -> void: 82 | # This must be done here instead of `_ready()` to avoid having `visibility_changed` be emitted immediately. 83 | visible = false 84 | 85 | if not InputMap.has_action("cycle_debug_menu"): 86 | # Create default input action if no user-defined override exists. 87 | # We can't do it in the editor plugin's activation code as it doesn't seem to work there. 88 | InputMap.add_action("cycle_debug_menu") 89 | var event := InputEventKey.new() 90 | event.keycode = KEY_F3 91 | InputMap.action_add_event("cycle_debug_menu", event) 92 | 93 | 94 | func _ready() -> void: 95 | fps_graph.draw.connect(_fps_graph_draw) 96 | total_graph.draw.connect(_total_graph_draw) 97 | cpu_graph.draw.connect(_cpu_graph_draw) 98 | gpu_graph.draw.connect(_gpu_graph_draw) 99 | 100 | fps_history.resize(HISTORY_NUM_FRAMES) 101 | frame_history_total.resize(HISTORY_NUM_FRAMES) 102 | frame_history_cpu.resize(HISTORY_NUM_FRAMES) 103 | frame_history_gpu.resize(HISTORY_NUM_FRAMES) 104 | 105 | # NOTE: Both FPS and frametimes are colored following FPS logic 106 | # (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS). 107 | # This makes the color gradient non-linear. 108 | # Colors are taken from . 109 | frame_time_gradient.set_color(0, Color8(239, 68, 68)) # red-500 110 | frame_time_gradient.set_color(1, Color8(56, 189, 248)) # light-blue-400 111 | frame_time_gradient.add_point(0.3333, Color8(250, 204, 21)) # yellow-400 112 | frame_time_gradient.add_point(0.6667, Color8(128, 226, 95)) # 50-50 mix of lime-400 and green-400 113 | 114 | get_viewport().size_changed.connect(update_settings_label) 115 | 116 | # Display loading text while information is being queried, 117 | # in case the user toggles the full debug menu just after starting the project. 118 | information.text = "Loading hardware information...\n\n " 119 | settings.text = "Loading project information..." 120 | thread.start( 121 | func(): 122 | # Disable thread safety checks as they interfere with this add-on. 123 | # This only affects this particular thread, not other thread instances in the project. 124 | # See for details. 125 | # Use a Callable so that this can be ignored on Godot 4.0 without causing a script error 126 | # (thread safety checks were added in Godot 4.1). 127 | if Engine.get_version_info()["hex"] >= 0x040100: 128 | Callable(Thread, "set_thread_safety_checks_enabled").call(false) 129 | 130 | # Enable required time measurements to display CPU/GPU frame time information. 131 | # These lines are time-consuming operations, so run them in a separate thread. 132 | RenderingServer.viewport_set_measure_render_time(get_viewport().get_viewport_rid(), true) 133 | update_information_label() 134 | update_settings_label() 135 | ) 136 | 137 | 138 | func _input(event: InputEvent) -> void: 139 | if event.is_action_pressed("cycle_debug_menu"): 140 | style = wrapi(style + 1, 0, Style.MAX) as Style 141 | 142 | 143 | func _exit_tree() -> void: 144 | thread.wait_to_finish() 145 | 146 | 147 | ## Update hardware information label (this can change at runtime based on window 148 | ## size and graphics settings). This is only called when the window is resized. 149 | ## To update when graphics settings are changed, the function must be called manually 150 | ## using `DebugMenu.update_settings_label()`. 151 | func update_settings_label() -> void: 152 | settings.text = "" 153 | if ProjectSettings.has_setting("application/config/version"): 154 | settings.text += "Project Version: %s\n" % ProjectSettings.get_setting("application/config/version") 155 | 156 | var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method")) 157 | var rendering_method_string := rendering_method 158 | match rendering_method: 159 | "forward_plus": 160 | rendering_method_string = "Forward+" 161 | "mobile": 162 | rendering_method_string = "Forward Mobile" 163 | "gl_compatibility": 164 | rendering_method_string = "Compatibility" 165 | settings.text += "Rendering Method: %s\n" % rendering_method_string 166 | 167 | var viewport := get_viewport() 168 | 169 | # The size of the viewport rendering, which determines which resolution 3D is rendered at. 170 | var viewport_render_size := Vector2i() 171 | 172 | if viewport.content_scale_mode == Window.CONTENT_SCALE_MODE_VIEWPORT: 173 | viewport_render_size = viewport.get_visible_rect().size 174 | settings.text += "Viewport: %d×%d, Window: %d×%d\n" % [viewport.get_visible_rect().size.x, viewport.get_visible_rect().size.y, viewport.size.x, viewport.size.y] 175 | else: 176 | # Window size matches viewport size. 177 | viewport_render_size = viewport.size 178 | settings.text += "Viewport: %d×%d\n" % [viewport.size.x, viewport.size.y] 179 | 180 | # Display 3D settings only if relevant. 181 | if viewport.get_camera_3d(): 182 | var scaling_3d_mode_string := "(unknown)" 183 | match viewport.scaling_3d_mode: 184 | Viewport.SCALING_3D_MODE_BILINEAR: 185 | scaling_3d_mode_string = "Bilinear" 186 | Viewport.SCALING_3D_MODE_FSR: 187 | scaling_3d_mode_string = "FSR 1.0" 188 | Viewport.SCALING_3D_MODE_FSR2: 189 | scaling_3d_mode_string = "FSR 2.2" 190 | 191 | var antialiasing_3d_string := "" 192 | if viewport.scaling_3d_mode == Viewport.SCALING_3D_MODE_FSR2: 193 | # The FSR2 scaling mode includes its own temporal antialiasing implementation. 194 | antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FSR 2.2" 195 | if viewport.scaling_3d_mode != Viewport.SCALING_3D_MODE_FSR2 and viewport.use_taa: 196 | # Godot's own TAA is ignored when using FSR2 scaling mode, as FSR2 provides its own TAA implementation. 197 | antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "TAA" 198 | if viewport.msaa_3d >= Viewport.MSAA_2X: 199 | antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "%d× MSAA" % pow(2, viewport.msaa_3d) 200 | if viewport.screen_space_aa == Viewport.SCREEN_SPACE_AA_FXAA: 201 | antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FXAA" 202 | 203 | settings.text += "3D scale (%s): %d%% = %d×%d" % [ 204 | scaling_3d_mode_string, 205 | viewport.scaling_3d_scale * 100, 206 | viewport_render_size.x * viewport.scaling_3d_scale, 207 | viewport_render_size.y * viewport.scaling_3d_scale, 208 | ] 209 | 210 | if not antialiasing_3d_string.is_empty(): 211 | settings.text += "\n3D Antialiasing: %s" % antialiasing_3d_string 212 | 213 | var environment := viewport.get_camera_3d().get_world_3d().environment 214 | if environment: 215 | if environment.ssr_enabled: 216 | settings.text += "\nSSR: %d Steps" % environment.ssr_max_steps 217 | 218 | if environment.ssao_enabled: 219 | settings.text += "\nSSAO: On" 220 | if environment.ssil_enabled: 221 | settings.text += "\nSSIL: On" 222 | 223 | if environment.sdfgi_enabled: 224 | settings.text += "\nSDFGI: %d Cascades" % environment.sdfgi_cascades 225 | 226 | if environment.glow_enabled: 227 | settings.text += "\nGlow: On" 228 | 229 | if environment.volumetric_fog_enabled: 230 | settings.text += "\nVolumetric Fog: On" 231 | var antialiasing_2d_string := "" 232 | if viewport.msaa_2d >= Viewport.MSAA_2X: 233 | antialiasing_2d_string = "%d× MSAA" % pow(2, viewport.msaa_2d) 234 | 235 | if not antialiasing_2d_string.is_empty(): 236 | settings.text += "\n2D Antialiasing: %s" % antialiasing_2d_string 237 | 238 | 239 | ## Update hardware/software information label (this never changes at runtime). 240 | func update_information_label() -> void: 241 | var adapter_string := "" 242 | # Make "NVIDIA Corporation" and "NVIDIA" be considered identical (required when using OpenGL to avoid redundancy). 243 | if RenderingServer.get_video_adapter_vendor().trim_suffix(" Corporation") in RenderingServer.get_video_adapter_name(): 244 | # Avoid repeating vendor name before adapter name. 245 | # Trim redundant suffix sometimes reported by NVIDIA graphics cards when using OpenGL. 246 | adapter_string = RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2") 247 | else: 248 | adapter_string = RenderingServer.get_video_adapter_vendor() + " - " + RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2") 249 | 250 | # Graphics driver version information isn't always availble. 251 | var driver_info := OS.get_video_adapter_driver_info() 252 | var driver_info_string := "" 253 | if driver_info.size() >= 2: 254 | driver_info_string = driver_info[1] 255 | else: 256 | driver_info_string = "(unknown)" 257 | 258 | var release_string := "" 259 | if OS.has_feature("editor"): 260 | # Editor build (implies `debug`). 261 | release_string = "editor" 262 | elif OS.has_feature("debug"): 263 | # Debug export template build. 264 | release_string = "debug" 265 | else: 266 | # Release export template build. 267 | release_string = "release" 268 | 269 | var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method")) 270 | var rendering_driver := str(ProjectSettings.get_setting_with_override("rendering/rendering_device/driver")) 271 | var graphics_api_string := rendering_driver 272 | if rendering_method != "gl_compatibility": 273 | if rendering_driver == "d3d12": 274 | graphics_api_string = "Direct3D 12" 275 | elif rendering_driver == "metal": 276 | graphics_api_string = "Metal" 277 | elif rendering_driver == "vulkan": 278 | if OS.has_feature("macos") or OS.has_feature("ios"): 279 | graphics_api_string = "Vulkan via MoltenVK" 280 | else: 281 | graphics_api_string = "Vulkan" 282 | else: 283 | if rendering_driver == "opengl3_angle": 284 | graphics_api_string = "OpenGL via ANGLE" 285 | elif OS.has_feature("mobile") or rendering_driver == "opengl3_es": 286 | graphics_api_string = "OpenGL ES" 287 | elif OS.has_feature("web"): 288 | graphics_api_string = "WebGL" 289 | elif rendering_driver == "opengl3": 290 | graphics_api_string = "OpenGL" 291 | 292 | information.text = ( 293 | "%s, %d threads\n" % [OS.get_processor_name().replace("(R)", "").replace("(TM)", ""), OS.get_processor_count()] 294 | + "%s %s (%s %s), %s %s\n" % [OS.get_name(), "64-bit" if OS.has_feature("64") else "32-bit", release_string, "double" if OS.has_feature("double") else "single", graphics_api_string, RenderingServer.get_video_adapter_api_version()] 295 | + "%s, %s" % [adapter_string, driver_info_string] 296 | ) 297 | 298 | 299 | func _fps_graph_draw() -> void: 300 | var fps_polyline := PackedVector2Array() 301 | fps_polyline.resize(HISTORY_NUM_FRAMES) 302 | for fps_index in fps_history.size(): 303 | fps_polyline[fps_index] = Vector2( 304 | remap(fps_index, 0, fps_history.size(), 0, GRAPH_SIZE.x), 305 | remap(clampf(fps_history[fps_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0) 306 | ) 307 | # Don't use antialiasing to speed up line drawing, but use a width that scales with 308 | # viewport scale to keep the line easily readable on hiDPI displays. 309 | fps_graph.draw_polyline(fps_polyline, frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0) 310 | 311 | 312 | func _total_graph_draw() -> void: 313 | var total_polyline := PackedVector2Array() 314 | total_polyline.resize(HISTORY_NUM_FRAMES) 315 | for total_index in frame_history_total.size(): 316 | total_polyline[total_index] = Vector2( 317 | remap(total_index, 0, frame_history_total.size(), 0, GRAPH_SIZE.x), 318 | remap(clampf(frame_history_total[total_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0) 319 | ) 320 | # Don't use antialiasing to speed up line drawing, but use a width that scales with 321 | # viewport scale to keep the line easily readable on hiDPI displays. 322 | total_graph.draw_polyline(total_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0) 323 | 324 | 325 | func _cpu_graph_draw() -> void: 326 | var cpu_polyline := PackedVector2Array() 327 | cpu_polyline.resize(HISTORY_NUM_FRAMES) 328 | for cpu_index in frame_history_cpu.size(): 329 | cpu_polyline[cpu_index] = Vector2( 330 | remap(cpu_index, 0, frame_history_cpu.size(), 0, GRAPH_SIZE.x), 331 | remap(clampf(frame_history_cpu[cpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0) 332 | ) 333 | # Don't use antialiasing to speed up line drawing, but use a width that scales with 334 | # viewport scale to keep the line easily readable on hiDPI displays. 335 | cpu_graph.draw_polyline(cpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0) 336 | 337 | 338 | func _gpu_graph_draw() -> void: 339 | var gpu_polyline := PackedVector2Array() 340 | gpu_polyline.resize(HISTORY_NUM_FRAMES) 341 | for gpu_index in frame_history_gpu.size(): 342 | gpu_polyline[gpu_index] = Vector2( 343 | remap(gpu_index, 0, frame_history_gpu.size(), 0, GRAPH_SIZE.x), 344 | remap(clampf(frame_history_gpu[gpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0) 345 | ) 346 | # Don't use antialiasing to speed up line drawing, but use a width that scales with 347 | # viewport scale to keep the line easily readable on hiDPI displays. 348 | gpu_graph.draw_polyline(gpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0) 349 | 350 | 351 | func _process(_delta: float) -> void: 352 | if visible: 353 | fps_graph.queue_redraw() 354 | total_graph.queue_redraw() 355 | cpu_graph.queue_redraw() 356 | gpu_graph.queue_redraw() 357 | 358 | # Difference between the last two rendered frames in milliseconds. 359 | var frametime := (Time.get_ticks_usec() - last_tick) * 0.001 360 | 361 | frame_history_total.push_back(frametime) 362 | if frame_history_total.size() > HISTORY_NUM_FRAMES: 363 | frame_history_total.pop_front() 364 | 365 | # Frametimes are colored following FPS logic (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS). 366 | # This makes the color gradient non-linear. 367 | frametime_avg = frame_history_total.reduce(sum_func) / frame_history_total.size() 368 | frame_history_total_avg.text = str(frametime_avg).pad_decimals(2) 369 | frame_history_total_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 370 | 371 | var frametime_min: float = frame_history_total.min() 372 | frame_history_total_min.text = str(frametime_min).pad_decimals(2) 373 | frame_history_total_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 374 | 375 | var frametime_max: float = frame_history_total.max() 376 | frame_history_total_max.text = str(frametime_max).pad_decimals(2) 377 | frame_history_total_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 378 | 379 | frame_history_total_last.text = str(frametime).pad_decimals(2) 380 | frame_history_total_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 381 | 382 | var viewport_rid := get_viewport().get_viewport_rid() 383 | var frametime_cpu := RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu() 384 | frame_history_cpu.push_back(frametime_cpu) 385 | if frame_history_cpu.size() > HISTORY_NUM_FRAMES: 386 | frame_history_cpu.pop_front() 387 | 388 | frametime_cpu_avg = frame_history_cpu.reduce(sum_func) / frame_history_cpu.size() 389 | frame_history_cpu_avg.text = str(frametime_cpu_avg).pad_decimals(2) 390 | frame_history_cpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 391 | 392 | var frametime_cpu_min: float = frame_history_cpu.min() 393 | frame_history_cpu_min.text = str(frametime_cpu_min).pad_decimals(2) 394 | frame_history_cpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 395 | 396 | var frametime_cpu_max: float = frame_history_cpu.max() 397 | frame_history_cpu_max.text = str(frametime_cpu_max).pad_decimals(2) 398 | frame_history_cpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 399 | 400 | frame_history_cpu_last.text = str(frametime_cpu).pad_decimals(2) 401 | frame_history_cpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 402 | 403 | var frametime_gpu := RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid) 404 | frame_history_gpu.push_back(frametime_gpu) 405 | if frame_history_gpu.size() > HISTORY_NUM_FRAMES: 406 | frame_history_gpu.pop_front() 407 | 408 | frametime_gpu_avg = frame_history_gpu.reduce(sum_func) / frame_history_gpu.size() 409 | frame_history_gpu_avg.text = str(frametime_gpu_avg).pad_decimals(2) 410 | frame_history_gpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 411 | 412 | var frametime_gpu_min: float = frame_history_gpu.min() 413 | frame_history_gpu_min.text = str(frametime_gpu_min).pad_decimals(2) 414 | frame_history_gpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 415 | 416 | var frametime_gpu_max: float = frame_history_gpu.max() 417 | frame_history_gpu_max.text = str(frametime_gpu_max).pad_decimals(2) 418 | frame_history_gpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 419 | 420 | frame_history_gpu_last.text = str(frametime_gpu).pad_decimals(2) 421 | frame_history_gpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 422 | 423 | frames_per_second = 1000.0 / frametime_avg 424 | fps_history.push_back(frames_per_second) 425 | if fps_history.size() > HISTORY_NUM_FRAMES: 426 | fps_history.pop_front() 427 | 428 | fps.text = str(floor(frames_per_second)) + " FPS" 429 | var frame_time_color := frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) 430 | fps.modulate = frame_time_color 431 | 432 | frame_time.text = str(frametime).pad_decimals(2) + " mspf" 433 | frame_time.modulate = frame_time_color 434 | 435 | var vsync_string := "" 436 | match DisplayServer.window_get_vsync_mode(): 437 | DisplayServer.VSYNC_ENABLED: 438 | vsync_string = "V-Sync" 439 | DisplayServer.VSYNC_ADAPTIVE: 440 | vsync_string = "Adaptive V-Sync" 441 | DisplayServer.VSYNC_MAILBOX: 442 | vsync_string = "Mailbox V-Sync" 443 | 444 | if Engine.max_fps > 0 or OS.low_processor_usage_mode: 445 | # Display FPS cap determined by `Engine.max_fps` or low-processor usage mode sleep duration 446 | # (the lowest FPS cap is used). 447 | var low_processor_max_fps := roundi(1000000.0 / OS.low_processor_usage_mode_sleep_usec) 448 | var fps_cap := low_processor_max_fps 449 | if Engine.max_fps > 0: 450 | fps_cap = mini(Engine.max_fps, low_processor_max_fps) 451 | frame_time.text += " (cap: " + str(fps_cap) + " FPS" 452 | 453 | if not vsync_string.is_empty(): 454 | frame_time.text += " + " + vsync_string 455 | 456 | frame_time.text += ")" 457 | else: 458 | if not vsync_string.is_empty(): 459 | frame_time.text += " (" + vsync_string + ")" 460 | 461 | frame_number.text = "Frame: " + str(Engine.get_frames_drawn()) 462 | 463 | last_tick = Time.get_ticks_usec() 464 | 465 | 466 | func _on_visibility_changed() -> void: 467 | if visible: 468 | # Reset graphs to prevent them from looking strange before `HISTORY_NUM_FRAMES` frames 469 | # have been drawn. 470 | var frametime_last := (Time.get_ticks_usec() - last_tick) * 0.001 471 | fps_history.resize(HISTORY_NUM_FRAMES) 472 | fps_history.fill(1000.0 / frametime_last) 473 | frame_history_total.resize(HISTORY_NUM_FRAMES) 474 | frame_history_total.fill(frametime_last) 475 | frame_history_cpu.resize(HISTORY_NUM_FRAMES) 476 | var viewport_rid := get_viewport().get_viewport_rid() 477 | frame_history_cpu.fill(RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu()) 478 | frame_history_gpu.resize(HISTORY_NUM_FRAMES) 479 | frame_history_gpu.fill(RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid)) 480 | -------------------------------------------------------------------------------- /addons/debug_menu/debug_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cggqb75a8w8r"] 2 | 3 | [ext_resource type="Script" path="res://addons/debug_menu/debug_menu.gd" id="1_p440y"] 4 | 5 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ki0n8"] 6 | bg_color = Color(0, 0, 0, 0.25098) 7 | 8 | [node name="CanvasLayer" type="CanvasLayer" node_paths=PackedStringArray("fps", "frame_time", "frame_number", "frame_history_total_avg", "frame_history_total_min", "frame_history_total_max", "frame_history_total_last", "frame_history_cpu_avg", "frame_history_cpu_min", "frame_history_cpu_max", "frame_history_cpu_last", "frame_history_gpu_avg", "frame_history_gpu_min", "frame_history_gpu_max", "frame_history_gpu_last", "fps_graph", "total_graph", "cpu_graph", "gpu_graph", "information", "settings")] 9 | process_mode = 3 10 | layer = 128 11 | script = ExtResource("1_p440y") 12 | fps = NodePath("DebugMenu/VBoxContainer/FPS") 13 | frame_time = NodePath("DebugMenu/VBoxContainer/FrameTime") 14 | frame_number = NodePath("DebugMenu/VBoxContainer/FrameNumber") 15 | frame_history_total_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalAvg") 16 | frame_history_total_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMin") 17 | frame_history_total_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMax") 18 | frame_history_total_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalLast") 19 | frame_history_cpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUAvg") 20 | frame_history_cpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMin") 21 | frame_history_cpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMax") 22 | frame_history_cpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPULast") 23 | frame_history_gpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUAvg") 24 | frame_history_gpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMin") 25 | frame_history_gpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMax") 26 | frame_history_gpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPULast") 27 | fps_graph = NodePath("DebugMenu/VBoxContainer/FPSGraph/Graph") 28 | total_graph = NodePath("DebugMenu/VBoxContainer/TotalGraph/Graph") 29 | cpu_graph = NodePath("DebugMenu/VBoxContainer/CPUGraph/Graph") 30 | gpu_graph = NodePath("DebugMenu/VBoxContainer/GPUGraph/Graph") 31 | information = NodePath("DebugMenu/VBoxContainer/Information") 32 | settings = NodePath("DebugMenu/VBoxContainer/Settings") 33 | 34 | [node name="DebugMenu" type="Control" parent="."] 35 | custom_minimum_size = Vector2(400, 400) 36 | layout_mode = 3 37 | anchors_preset = 1 38 | anchor_left = 1.0 39 | anchor_right = 1.0 40 | offset_left = -416.0 41 | offset_top = 8.0 42 | offset_right = -16.0 43 | offset_bottom = 408.0 44 | grow_horizontal = 0 45 | size_flags_horizontal = 8 46 | size_flags_vertical = 4 47 | mouse_filter = 2 48 | 49 | [node name="VBoxContainer" type="VBoxContainer" parent="DebugMenu"] 50 | layout_mode = 1 51 | anchors_preset = 1 52 | anchor_left = 1.0 53 | anchor_right = 1.0 54 | offset_left = -300.0 55 | offset_bottom = 374.0 56 | grow_horizontal = 0 57 | mouse_filter = 2 58 | theme_override_constants/separation = 0 59 | 60 | [node name="FPS" type="Label" parent="DebugMenu/VBoxContainer"] 61 | modulate = Color(0, 1, 0, 1) 62 | layout_mode = 2 63 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 64 | theme_override_constants/outline_size = 5 65 | theme_override_constants/line_spacing = 0 66 | theme_override_font_sizes/font_size = 18 67 | text = "60 FPS" 68 | horizontal_alignment = 2 69 | 70 | [node name="FrameTime" type="Label" parent="DebugMenu/VBoxContainer"] 71 | modulate = Color(0, 1, 0, 1) 72 | layout_mode = 2 73 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 74 | theme_override_constants/outline_size = 3 75 | theme_override_font_sizes/font_size = 12 76 | text = "16.67 mspf (cap: 123 FPS + Adaptive V-Sync)" 77 | horizontal_alignment = 2 78 | 79 | [node name="FrameNumber" type="Label" parent="DebugMenu/VBoxContainer"] 80 | layout_mode = 2 81 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 82 | theme_override_constants/outline_size = 3 83 | theme_override_font_sizes/font_size = 12 84 | text = "Frame: 1234" 85 | horizontal_alignment = 2 86 | 87 | [node name="FrameTimeHistory" type="GridContainer" parent="DebugMenu/VBoxContainer"] 88 | layout_mode = 2 89 | size_flags_horizontal = 8 90 | mouse_filter = 2 91 | theme_override_constants/h_separation = 0 92 | theme_override_constants/v_separation = 0 93 | columns = 5 94 | 95 | [node name="Spacer" type="Control" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 96 | custom_minimum_size = Vector2(60, 0) 97 | layout_mode = 2 98 | mouse_filter = 2 99 | 100 | [node name="AvgHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 101 | custom_minimum_size = Vector2(50, 0) 102 | layout_mode = 2 103 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 104 | theme_override_constants/outline_size = 3 105 | theme_override_font_sizes/font_size = 12 106 | text = "Average" 107 | horizontal_alignment = 2 108 | 109 | [node name="MinHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 110 | custom_minimum_size = Vector2(50, 0) 111 | layout_mode = 2 112 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 113 | theme_override_constants/outline_size = 3 114 | theme_override_font_sizes/font_size = 12 115 | text = "Best" 116 | horizontal_alignment = 2 117 | 118 | [node name="MaxHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 119 | custom_minimum_size = Vector2(50, 0) 120 | layout_mode = 2 121 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 122 | theme_override_constants/outline_size = 3 123 | theme_override_font_sizes/font_size = 12 124 | text = "Worst" 125 | horizontal_alignment = 2 126 | 127 | [node name="LastHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 128 | custom_minimum_size = Vector2(50, 0) 129 | layout_mode = 2 130 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 131 | theme_override_constants/outline_size = 3 132 | theme_override_font_sizes/font_size = 12 133 | text = "Last" 134 | horizontal_alignment = 2 135 | 136 | [node name="TotalHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 137 | custom_minimum_size = Vector2(50, 0) 138 | layout_mode = 2 139 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 140 | theme_override_constants/outline_size = 3 141 | theme_override_font_sizes/font_size = 12 142 | text = "Total:" 143 | horizontal_alignment = 2 144 | 145 | [node name="TotalAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 146 | modulate = Color(0, 1, 0, 1) 147 | custom_minimum_size = Vector2(50, 0) 148 | layout_mode = 2 149 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 150 | theme_override_constants/outline_size = 3 151 | theme_override_font_sizes/font_size = 12 152 | text = "123.45" 153 | horizontal_alignment = 2 154 | 155 | [node name="TotalMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 156 | modulate = Color(0, 1, 0, 1) 157 | custom_minimum_size = Vector2(50, 0) 158 | layout_mode = 2 159 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 160 | theme_override_constants/outline_size = 3 161 | theme_override_font_sizes/font_size = 12 162 | text = "123.45" 163 | horizontal_alignment = 2 164 | 165 | [node name="TotalMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 166 | modulate = Color(0, 1, 0, 1) 167 | custom_minimum_size = Vector2(50, 0) 168 | layout_mode = 2 169 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 170 | theme_override_constants/outline_size = 3 171 | theme_override_font_sizes/font_size = 12 172 | text = "123.45" 173 | horizontal_alignment = 2 174 | 175 | [node name="TotalLast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 176 | modulate = Color(0, 1, 0, 1) 177 | custom_minimum_size = Vector2(50, 0) 178 | layout_mode = 2 179 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 180 | theme_override_constants/outline_size = 3 181 | theme_override_font_sizes/font_size = 12 182 | text = "123.45" 183 | horizontal_alignment = 2 184 | 185 | [node name="CPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 186 | custom_minimum_size = Vector2(50, 0) 187 | layout_mode = 2 188 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 189 | theme_override_constants/outline_size = 3 190 | theme_override_font_sizes/font_size = 12 191 | text = "CPU:" 192 | horizontal_alignment = 2 193 | 194 | [node name="CPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 195 | modulate = Color(0, 1, 0, 1) 196 | custom_minimum_size = Vector2(50, 0) 197 | layout_mode = 2 198 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 199 | theme_override_constants/outline_size = 3 200 | theme_override_font_sizes/font_size = 12 201 | text = "123.45" 202 | horizontal_alignment = 2 203 | 204 | [node name="CPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 205 | modulate = Color(0, 1, 0, 1) 206 | custom_minimum_size = Vector2(50, 0) 207 | layout_mode = 2 208 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 209 | theme_override_constants/outline_size = 3 210 | theme_override_font_sizes/font_size = 12 211 | text = "12.34" 212 | horizontal_alignment = 2 213 | 214 | [node name="CPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 215 | modulate = Color(0, 1, 0, 1) 216 | custom_minimum_size = Vector2(50, 0) 217 | layout_mode = 2 218 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 219 | theme_override_constants/outline_size = 3 220 | theme_override_font_sizes/font_size = 12 221 | text = "123.45" 222 | horizontal_alignment = 2 223 | 224 | [node name="CPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 225 | modulate = Color(0, 1, 0, 1) 226 | custom_minimum_size = Vector2(50, 0) 227 | layout_mode = 2 228 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 229 | theme_override_constants/outline_size = 3 230 | theme_override_font_sizes/font_size = 12 231 | text = "123.45" 232 | horizontal_alignment = 2 233 | 234 | [node name="GPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 235 | custom_minimum_size = Vector2(50, 0) 236 | layout_mode = 2 237 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 238 | theme_override_constants/outline_size = 3 239 | theme_override_font_sizes/font_size = 12 240 | text = "GPU:" 241 | horizontal_alignment = 2 242 | 243 | [node name="GPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 244 | modulate = Color(0, 1, 0, 1) 245 | custom_minimum_size = Vector2(50, 0) 246 | layout_mode = 2 247 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 248 | theme_override_constants/outline_size = 3 249 | theme_override_font_sizes/font_size = 12 250 | text = "123.45" 251 | horizontal_alignment = 2 252 | 253 | [node name="GPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 254 | modulate = Color(0, 1, 0, 1) 255 | custom_minimum_size = Vector2(50, 0) 256 | layout_mode = 2 257 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 258 | theme_override_constants/outline_size = 3 259 | theme_override_font_sizes/font_size = 12 260 | text = "1.23" 261 | horizontal_alignment = 2 262 | 263 | [node name="GPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 264 | modulate = Color(0, 1, 0, 1) 265 | custom_minimum_size = Vector2(50, 0) 266 | layout_mode = 2 267 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 268 | theme_override_constants/outline_size = 3 269 | theme_override_font_sizes/font_size = 12 270 | text = "123.45" 271 | horizontal_alignment = 2 272 | 273 | [node name="GPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] 274 | modulate = Color(0, 1, 0, 1) 275 | custom_minimum_size = Vector2(50, 0) 276 | layout_mode = 2 277 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 278 | theme_override_constants/outline_size = 3 279 | theme_override_font_sizes/font_size = 12 280 | text = "123.45" 281 | horizontal_alignment = 2 282 | 283 | [node name="FPSGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"] 284 | layout_mode = 2 285 | mouse_filter = 2 286 | alignment = 2 287 | 288 | [node name="Title" type="Label" parent="DebugMenu/VBoxContainer/FPSGraph"] 289 | custom_minimum_size = Vector2(0, 27) 290 | layout_mode = 2 291 | size_flags_horizontal = 8 292 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 293 | theme_override_constants/outline_size = 3 294 | theme_override_font_sizes/font_size = 12 295 | text = "FPS: ↑" 296 | vertical_alignment = 1 297 | 298 | [node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/FPSGraph"] 299 | custom_minimum_size = Vector2(150, 25) 300 | layout_mode = 2 301 | size_flags_vertical = 0 302 | mouse_filter = 2 303 | theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8") 304 | 305 | [node name="TotalGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"] 306 | layout_mode = 2 307 | mouse_filter = 2 308 | alignment = 2 309 | 310 | [node name="Title" type="Label" parent="DebugMenu/VBoxContainer/TotalGraph"] 311 | custom_minimum_size = Vector2(0, 27) 312 | layout_mode = 2 313 | size_flags_horizontal = 8 314 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 315 | theme_override_constants/outline_size = 3 316 | theme_override_font_sizes/font_size = 12 317 | text = "Total: ↓" 318 | vertical_alignment = 1 319 | 320 | [node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/TotalGraph"] 321 | custom_minimum_size = Vector2(150, 25) 322 | layout_mode = 2 323 | size_flags_vertical = 0 324 | mouse_filter = 2 325 | theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8") 326 | 327 | [node name="CPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"] 328 | layout_mode = 2 329 | mouse_filter = 2 330 | alignment = 2 331 | 332 | [node name="Title" type="Label" parent="DebugMenu/VBoxContainer/CPUGraph"] 333 | custom_minimum_size = Vector2(0, 27) 334 | layout_mode = 2 335 | size_flags_horizontal = 8 336 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 337 | theme_override_constants/outline_size = 3 338 | theme_override_font_sizes/font_size = 12 339 | text = "CPU: ↓" 340 | vertical_alignment = 1 341 | 342 | [node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/CPUGraph"] 343 | custom_minimum_size = Vector2(150, 25) 344 | layout_mode = 2 345 | size_flags_vertical = 0 346 | mouse_filter = 2 347 | theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8") 348 | 349 | [node name="GPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"] 350 | layout_mode = 2 351 | mouse_filter = 2 352 | alignment = 2 353 | 354 | [node name="Title" type="Label" parent="DebugMenu/VBoxContainer/GPUGraph"] 355 | custom_minimum_size = Vector2(0, 27) 356 | layout_mode = 2 357 | size_flags_horizontal = 8 358 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 359 | theme_override_constants/outline_size = 3 360 | theme_override_font_sizes/font_size = 12 361 | text = "GPU: ↓" 362 | vertical_alignment = 1 363 | 364 | [node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/GPUGraph"] 365 | custom_minimum_size = Vector2(150, 25) 366 | layout_mode = 2 367 | size_flags_vertical = 0 368 | mouse_filter = 2 369 | theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8") 370 | 371 | [node name="Information" type="Label" parent="DebugMenu/VBoxContainer"] 372 | modulate = Color(1, 1, 1, 0.752941) 373 | layout_mode = 2 374 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 375 | theme_override_constants/outline_size = 3 376 | theme_override_font_sizes/font_size = 12 377 | text = "12th Gen Intel(R) Core(TM) i0-1234K 378 | Windows 12 64-bit (double precision), Vulkan 1.2.34 379 | NVIDIA GeForce RTX 1234, 123.45.67" 380 | horizontal_alignment = 2 381 | 382 | [node name="Settings" type="Label" parent="DebugMenu/VBoxContainer"] 383 | modulate = Color(0.8, 0.84, 1, 0.752941) 384 | layout_mode = 2 385 | theme_override_colors/font_outline_color = Color(0, 0, 0, 1) 386 | theme_override_constants/outline_size = 3 387 | theme_override_font_sizes/font_size = 12 388 | text = "Project Version: 1.2.3 389 | Rendering Method: Forward+ 390 | Window: 1234×567, Viewport: 1234×567 391 | 3D Scale (FSR 1.0): 100% = 1234×567 392 | 3D Antialiasing: TAA + 2× MSAA + FXAA 393 | SSR: 123 Steps 394 | SSAO: On 395 | SSIL: On 396 | SDFGI: 1 Cascades 397 | Glow: On 398 | Volumetric Fog: On 399 | 2D Antialiasing: 2× MSAA" 400 | horizontal_alignment = 2 401 | 402 | [connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] 403 | -------------------------------------------------------------------------------- /addons/debug_menu/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Debug Menu" 4 | description="In-game debug menu displaying performance metrics and hardware information" 5 | author="Calinou" 6 | version="1.2.0" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/debug_menu/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree() -> void: 5 | if not ProjectSettings.has_setting("autoload/DebugMenu"): 6 | add_autoload_singleton("DebugMenu", "res://addons/debug_menu/debug_menu.tscn") 7 | 8 | if not ProjectSettings.has_setting("application/config/version") or ProjectSettings.get_setting("application/config/version") == "": 9 | ProjectSettings.set_setting("application/config/version", "1.0.0") 10 | print('Debug Menu: Setting "application/config/version" was missing or empty and has been set to "1.0.0".') 11 | 12 | ProjectSettings.add_property_info({ 13 | name = "application/config/version", 14 | type = TYPE_STRING, 15 | }) 16 | 17 | ProjectSettings.save() 18 | 19 | 20 | func _exit_tree() -> void: 21 | remove_autoload_singleton("DebugMenu") 22 | # Don't remove the project setting's value and input map action, 23 | # as the plugin may be re-enabled in the future. 24 | --------------------------------------------------------------------------------