Repository: HungryProton/monitor_overlay Branch: main Commit: 27e73f598e2d Files: 12 Total size: 16.4 KB Directory structure: gitextract_dm5wy8br/ ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── addons/ │ └── monitor_overlay/ │ ├── LICENSE │ ├── icon.svg.import │ ├── monitor_overlay.gd │ ├── monitor_overlay_debug_graph.gd │ ├── monitor_overlay_plugin.gd │ └── plugin.cfg ├── icon.png.import └── project.godot ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Normalize EOL for all files that Git considers text files. * text=auto eol=lf /.gitattributes export-ignore /.gitignore export-ignore /LICENSE export-ignore /README.md export-ignore /project.godot export-ignore /icon.png export-ignore /*.import export-ignore ================================================ FILE: .gitignore ================================================ # Godot 4+ specific ignores .godot/ # Godot-specific ignores .import/ export.cfg export_presets.cfg # Imported translations (automatically generated from CSV files) *.translation # Mono-specific ignores .mono/ data_*/ mono_crash.*.json ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 HungryProton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Monitor Overlay Displays data from the Monitor panel at runtime in the game window. ![image](https://user-images.githubusercontent.com/52043844/94171965-310f7b80-fe92-11ea-9420-602d4777389b.png) ## How to install ### From the asset library - Search for `Monitor overlay` and click the install button. - Enable the addon in `Project > Project Settings > Plugins` ### Manually from this repository - Clone or download the project. - Copy the contents of the `addons/monitor_overlay` folder in your project folder as follow: ![image](https://user-images.githubusercontent.com/52043844/157983933-7b58573d-d04d-428e-9842-a89fed7999bf.png) - Enable the addon in `Project > Project Settings > Plugins` ## How to use - Add a new `MonitorOverlay` node to a scene. + Make sure it's the last node on the tree otherwise it may be drawn behind other objects. - Select the node and enable turn on the monitors you need in the inspector under the `Active monitors` group. ![image](https://user-images.githubusercontent.com/52043844/157984315-183cbc72-98e8-4b34-bb47-49f00ac9f28c.png) ### Changing the position and size - The whole overlay is a regular Control node. You have access to all the options available on UI nodes. - Default width is 300px. This can be ajusted from the inspector under `Control > Layout > Minimum Size`. - To change the vertical size of the graphs, adjust the `Graph Height` property in the inspector under `Monitor Overlay > Options > Graph Height` ## Important notes - The overlay is a control node that contains other nodes (one for each graph). Because of this, the reported amount of objects / nodes / memory used / primitives drawn (and potentially others too) will differ if the overlay is enabled or not. Although the difference is not significant, keep in mind it exists. - Plotting a graph requires to keep track of the values history and can impact performance. + If you don't need this feature, you can turn it off by disabling the `Plot Graphs` option in the inspector. + Lowering the `History` parameter also helps. ## Licence MIT ================================================ FILE: addons/monitor_overlay/LICENSE ================================================ MIT License Copyright (c) 2020 HungryProton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: addons/monitor_overlay/icon.svg.import ================================================ [remap] importer="texture" type="CompressedTexture2D" uid="uid://d0x55sc75mob4" path="res://.godot/imported/icon.svg-497111bfcdb0b9dd996d646163572e32.ctex" metadata={ "vram_texture": false } [deps] source_file="res://addons/monitor_overlay/icon.svg" dest_files=["res://.godot/imported/icon.svg-497111bfcdb0b9dd996d646163572e32.ctex"] [params] compress/mode=0 compress/lossy_quality=0.7 compress/hdr_compression=1 compress/bptc_ldr=0 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 ================================================ FILE: addons/monitor_overlay/monitor_overlay.gd ================================================ @tool @icon("./icon.svg") class_name MonitorOverlay extends VBoxContainer const DebugGraph := preload("./monitor_overlay_debug_graph.gd") var need_to_rebuild_ui:bool=false # Monitors @export_group("Active Monitors") @export_subgroup("Time") @export var fps := true: set(value): fps = value need_to_rebuild_ui=true @export var process := false: set(value): process = value need_to_rebuild_ui=true @export var physics_process := false: set(value): physics_process = value need_to_rebuild_ui=true @export_subgroup("Memory") @export var static_memory := false: set(value): static_memory = value need_to_rebuild_ui=true @export var max_static_memory := false: set(value): max_static_memory = value need_to_rebuild_ui=true @export var max_message_buffer := false: set(value): max_message_buffer = value need_to_rebuild_ui=true @export_subgroup("Objects") @export var objects := false: set(value): objects = value need_to_rebuild_ui=true @export var resources := false: set(value): resources = value need_to_rebuild_ui=true @export var nodes := false: set(value): nodes = value need_to_rebuild_ui=true @export var orphan_nodes := false: set(value): orphan_nodes = value need_to_rebuild_ui=true @export_subgroup("Raster") @export var objects_drawn := false: set(value): objects_drawn = value need_to_rebuild_ui=true @export var primitives_drawn := false: set(value): primitives_drawn = value need_to_rebuild_ui=true @export var total_draw_calls := false: set(value): total_draw_calls = value need_to_rebuild_ui=true @export_subgroup("Video") @export var video_memory := false: set(value): video_memory = value need_to_rebuild_ui=true @export var texture_memory := false: set(value): texture_memory = value need_to_rebuild_ui=true @export var buffer_memory := false: set(value): buffer_memory = value need_to_rebuild_ui=true @export_subgroup("Physics 2D") @export var active_objects_2d := false: set(value): active_objects_2d = value need_to_rebuild_ui=true @export var collision_pairs_2d := false: set(value): collision_pairs_2d = value need_to_rebuild_ui=true @export var islands_2d := false: set(value): islands_2d = value need_to_rebuild_ui=true @export_subgroup("Physics 3D") @export var active_objects_3d := false: set(value): active_objects_3d = value need_to_rebuild_ui=true @export var collision_pairs_3d := false: set(value): collision_pairs_3d = value need_to_rebuild_ui=true @export var islands_3d := false: set(value): islands_3d = value need_to_rebuild_ui=true @export_subgroup("Audio") @export var audio_output_latency := false: set(value): audio_output_latency = value need_to_rebuild_ui=true # Graph options @export_group("Options") ## Sampling rate in samples per second @export_range(0.0, 1000.0) var sampling_rate := 60.0: set(value): sampling_rate = value # if sampling rate is 0, _t_limit is infinity _t_limit = 1 / sampling_rate @export var normalize_units := true: set(value): normalize_units = value need_to_rebuild_ui=true @export var plot_graphs := true: set(value): plot_graphs = value need_to_rebuild_ui=true @export var history := 100: set(value): history = value need_to_rebuild_ui=true @export var background_color := Color(0.0, 0.0, 0.0, 0.5): set(value): background_color = value need_to_rebuild_ui=true @export var graph_color := Color.ORANGE: set(value): graph_color = value need_to_rebuild_ui=true @export var graph_height := 50: set(value): graph_height = value need_to_rebuild_ui=true @export var graph_thickness := 1.0: set(value): graph_thickness = value need_to_rebuild_ui=true @export var graph_antialiased := false: set(value): graph_antialiased = value need_to_rebuild_ui=true @export var font_size := 14: set(value): font_size = value need_to_rebuild_ui=true var _graphs := [] var _t := 0.0 var _t_limit := 0.0 func _ready(): if custom_minimum_size.x == 0: custom_minimum_size.x = 300 need_to_rebuild_ui=true func clear() -> void: for graph in _graphs: graph.queue_free() _graphs = [] func rebuild_ui(): clear() if fps: _create_graph_for(Performance.TIME_FPS, "FPS") if process: _create_graph_for(Performance.TIME_PROCESS, "Process", "s") if physics_process: _create_graph_for(Performance.TIME_PHYSICS_PROCESS, "Physics Process", "s") if static_memory: _create_graph_for(Performance.MEMORY_STATIC, "Static Memory", "B") if max_static_memory: _create_graph_for(Performance.MEMORY_STATIC_MAX, "Max Static Memory", "B") if max_message_buffer: _create_graph_for(Performance.MEMORY_MESSAGE_BUFFER_MAX, "Message Buffer Max") if objects: _create_graph_for(Performance.OBJECT_COUNT, "Objects") if resources: _create_graph_for(Performance.OBJECT_RESOURCE_COUNT, "Resources") if nodes: _create_graph_for(Performance.OBJECT_NODE_COUNT, "Nodes") if orphan_nodes: _create_graph_for(Performance.OBJECT_ORPHAN_NODE_COUNT, "Orphan Nodes") if objects_drawn: _create_graph_for(Performance.RENDER_TOTAL_OBJECTS_IN_FRAME, "Objects Drawn") if primitives_drawn: _create_graph_for(Performance.RENDER_TOTAL_PRIMITIVES_IN_FRAME, "Primitives Drawn") if total_draw_calls: _create_graph_for(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME, "3D Draw Calls") if video_memory: _create_graph_for(Performance.RENDER_VIDEO_MEM_USED, "Video Memory", "B") if texture_memory: _create_graph_for(Performance.RENDER_TEXTURE_MEM_USED, "Texture Memory", "B") if buffer_memory: _create_graph_for(Performance.RENDER_BUFFER_MEM_USED, "Vertex Memory", "B") if active_objects_2d: _create_graph_for(Performance.PHYSICS_2D_ACTIVE_OBJECTS, "2D Active Objects") if collision_pairs_2d: _create_graph_for(Performance.PHYSICS_2D_COLLISION_PAIRS, " 2D Collision Pairs") if islands_2d: _create_graph_for(Performance.PHYSICS_2D_ISLAND_COUNT, "2D Islands") if active_objects_3d: _create_graph_for(Performance.PHYSICS_3D_ACTIVE_OBJECTS, " 3D Active Objects") if collision_pairs_3d: _create_graph_for(Performance.PHYSICS_3D_COLLISION_PAIRS, "3D Collision Pairs") if islands_3d: _create_graph_for(Performance.PHYSICS_3D_ISLAND_COUNT, "3D Islands") if audio_output_latency: _create_graph_for(Performance.AUDIO_OUTPUT_LATENCY, "Audio Latency", "s") func _process(delta: float) -> void: if need_to_rebuild_ui: rebuild_ui() need_to_rebuild_ui=false _t += delta if _t >= _t_limit: _t = 0 for item in _graphs: item.queue_redraw() func _create_graph_for(monitor: int, monitor_name: String, unit: String = "") -> void: var graph = DebugGraph.new() graph.monitor = monitor graph.monitor_name = monitor_name graph.font = get_theme_default_font() graph.font_size = font_size graph.custom_minimum_size.y = graph_height graph.max_points = history graph.background_color = background_color graph.graph_color = graph_color graph.plot_graph = plot_graphs graph.unit = unit graph.normalize_units = normalize_units graph.thickness = graph_thickness graph.antialiased = graph_antialiased graph.name = monitor_name graph.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(graph) _graphs.push_back(graph) ================================================ FILE: addons/monitor_overlay/monitor_overlay_debug_graph.gd ================================================ extends Control var monitor: int var monitor_name: String var max_points := 100 var font: Font var background_color: Color var graph_color: Color var normalize_units := true var plot_graph := true var unit: String var thickness: float var antialiased: bool var _history := [] var _last_value: float func _draw() -> void: _update_history() _draw_background_panel() _draw_graph() _draw_text() func _draw_background_panel() -> void: var panel := Rect2() panel.position = get_canvas_transform().origin panel.size = size draw_rect(panel, background_color) var font_size: int = 14 func _draw_text() -> void: var s_value := _normalize_value(_last_value) var text = monitor_name + ": " + s_value var position = Vector2(0, font_size) # Y offset = font size draw_string(font, position, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size) # TODO: That function is unoptimized func _draw_graph() -> void: if not plot_graph: return # Get the values range var min_value = _history[0] var max_value = _history[0] for value in _history: min_value = min(value, min_value) max_value = max(value, max_value) if min_value == max_value: min_value -= 1 max_value += 1 # Convert to 2D coordinates var x := 0.0 var offset := size.x / max_points var height := size.y var margin = height / 10.0 var origin := get_canvas_transform().origin var points = PackedVector2Array() for value in _history: value = remap(value, min_value, max_value, margin, height - margin) if x > 0: points.push_back(Vector2(x, height - value) + origin) x += offset if points.size() > 1: draw_polyline(points, graph_color, thickness, antialiased) func _update_history(): _last_value = Performance.get_monitor(monitor) if not plot_graph: return _history.push_back(_last_value) if _history.size() >= max_points: _history.pop_front() func _normalize_value(value: float) -> String: var result := str(value) + unit if not normalize_units or _last_value == 0.0: return result if _last_value > 1000.0: var v = _last_value var index = -1 while v > 1000.0 and index < 3: v /= 1000.0 index += 1 var scale = ["K", "M", "G", "T"] result = str(snapped(v, 0.001)) + scale[index] + unit if _last_value < 1.0: var v = _last_value var index = -1 while v < 1.0 and index < 2: v *= 1000.0 index += 1 var scale = ["m", "u", "n"] result = str(snapped(v, 0.001)) + scale[index] + unit return result ================================================ FILE: addons/monitor_overlay/monitor_overlay_plugin.gd ================================================ @tool extends EditorPlugin func _get_plugin_name() -> String: return "MonitorOverlay" func _get_plugin_icon() -> Texture2D: return load("./icon.svg") ================================================ FILE: addons/monitor_overlay/plugin.cfg ================================================ [plugin] name="Monitor Overlay" description="Same as the Monitor tab, but displayed directly in your game" author="HungryProton" version="1.1.0" script="monitor_overlay_plugin.gd" ================================================ FILE: icon.png.import ================================================ [remap] importer="texture" type="CompressedTexture2D" uid="uid://dglmusrwc0o2i" path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" metadata={ "vram_texture": false } [deps] source_file="res://icon.png" dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] [params] compress/mode=0 compress/lossy_quality=0.7 compress/hdr_compression=1 compress/bptc_ldr=0 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 ================================================ FILE: project.godot ================================================ ; Engine configuration file. ; It's best edited using the editor UI and not directly, ; since the parameters that go here are not all obvious. ; ; Format: ; [section] ; section goes between [] ; param=value ; assign values to parameters config_version=5 [application] config/name="Monitor Overlay" config/features=PackedStringArray("4.0", "Vulkan Mobile") config/icon="res://icon.png" [editor_plugins] enabled=PackedStringArray("res://addons/monitor_overlay/plugin.cfg") [rendering] vulkan/rendering/back_end=1