| 1 | /**************************************************************************/ |
| 2 | /* performance.cpp */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #include "performance.h" |
| 32 | |
| 33 | #include "core/object/message_queue.h" |
| 34 | #include "core/os/os.h" |
| 35 | #include "core/variant/typed_array.h" |
| 36 | #include "scene/main/node.h" |
| 37 | #include "scene/main/scene_tree.h" |
| 38 | #include "servers/audio_server.h" |
| 39 | #include "servers/navigation_server_3d.h" |
| 40 | #include "servers/physics_server_2d.h" |
| 41 | #include "servers/physics_server_3d.h" |
| 42 | #include "servers/rendering_server.h" |
| 43 | |
| 44 | Performance *Performance::singleton = nullptr; |
| 45 | |
| 46 | void Performance::_bind_methods() { |
| 47 | ClassDB::bind_method(D_METHOD("get_monitor" , "monitor" ), &Performance::get_monitor); |
| 48 | ClassDB::bind_method(D_METHOD("add_custom_monitor" , "id" , "callable" , "arguments" ), &Performance::add_custom_monitor, DEFVAL(Array())); |
| 49 | ClassDB::bind_method(D_METHOD("remove_custom_monitor" , "id" ), &Performance::remove_custom_monitor); |
| 50 | ClassDB::bind_method(D_METHOD("has_custom_monitor" , "id" ), &Performance::has_custom_monitor); |
| 51 | ClassDB::bind_method(D_METHOD("get_custom_monitor" , "id" ), &Performance::get_custom_monitor); |
| 52 | ClassDB::bind_method(D_METHOD("get_monitor_modification_time" ), &Performance::get_monitor_modification_time); |
| 53 | ClassDB::bind_method(D_METHOD("get_custom_monitor_names" ), &Performance::get_custom_monitor_names); |
| 54 | |
| 55 | BIND_ENUM_CONSTANT(TIME_FPS); |
| 56 | BIND_ENUM_CONSTANT(TIME_PROCESS); |
| 57 | BIND_ENUM_CONSTANT(TIME_PHYSICS_PROCESS); |
| 58 | BIND_ENUM_CONSTANT(TIME_NAVIGATION_PROCESS); |
| 59 | BIND_ENUM_CONSTANT(MEMORY_STATIC); |
| 60 | BIND_ENUM_CONSTANT(MEMORY_STATIC_MAX); |
| 61 | BIND_ENUM_CONSTANT(MEMORY_MESSAGE_BUFFER_MAX); |
| 62 | BIND_ENUM_CONSTANT(OBJECT_COUNT); |
| 63 | BIND_ENUM_CONSTANT(OBJECT_RESOURCE_COUNT); |
| 64 | BIND_ENUM_CONSTANT(OBJECT_NODE_COUNT); |
| 65 | BIND_ENUM_CONSTANT(OBJECT_ORPHAN_NODE_COUNT); |
| 66 | BIND_ENUM_CONSTANT(RENDER_TOTAL_OBJECTS_IN_FRAME); |
| 67 | BIND_ENUM_CONSTANT(RENDER_TOTAL_PRIMITIVES_IN_FRAME); |
| 68 | BIND_ENUM_CONSTANT(RENDER_TOTAL_DRAW_CALLS_IN_FRAME); |
| 69 | BIND_ENUM_CONSTANT(RENDER_VIDEO_MEM_USED); |
| 70 | BIND_ENUM_CONSTANT(RENDER_TEXTURE_MEM_USED); |
| 71 | BIND_ENUM_CONSTANT(RENDER_BUFFER_MEM_USED); |
| 72 | BIND_ENUM_CONSTANT(PHYSICS_2D_ACTIVE_OBJECTS); |
| 73 | BIND_ENUM_CONSTANT(PHYSICS_2D_COLLISION_PAIRS); |
| 74 | BIND_ENUM_CONSTANT(PHYSICS_2D_ISLAND_COUNT); |
| 75 | BIND_ENUM_CONSTANT(PHYSICS_3D_ACTIVE_OBJECTS); |
| 76 | BIND_ENUM_CONSTANT(PHYSICS_3D_COLLISION_PAIRS); |
| 77 | BIND_ENUM_CONSTANT(PHYSICS_3D_ISLAND_COUNT); |
| 78 | BIND_ENUM_CONSTANT(AUDIO_OUTPUT_LATENCY); |
| 79 | BIND_ENUM_CONSTANT(NAVIGATION_ACTIVE_MAPS); |
| 80 | BIND_ENUM_CONSTANT(NAVIGATION_REGION_COUNT); |
| 81 | BIND_ENUM_CONSTANT(NAVIGATION_AGENT_COUNT); |
| 82 | BIND_ENUM_CONSTANT(NAVIGATION_LINK_COUNT); |
| 83 | BIND_ENUM_CONSTANT(NAVIGATION_POLYGON_COUNT); |
| 84 | BIND_ENUM_CONSTANT(NAVIGATION_EDGE_COUNT); |
| 85 | BIND_ENUM_CONSTANT(NAVIGATION_EDGE_MERGE_COUNT); |
| 86 | BIND_ENUM_CONSTANT(NAVIGATION_EDGE_CONNECTION_COUNT); |
| 87 | BIND_ENUM_CONSTANT(NAVIGATION_EDGE_FREE_COUNT); |
| 88 | BIND_ENUM_CONSTANT(MONITOR_MAX); |
| 89 | } |
| 90 | |
| 91 | int Performance::_get_node_count() const { |
| 92 | MainLoop *ml = OS::get_singleton()->get_main_loop(); |
| 93 | SceneTree *sml = Object::cast_to<SceneTree>(ml); |
| 94 | if (!sml) { |
| 95 | return 0; |
| 96 | } |
| 97 | return sml->get_node_count(); |
| 98 | } |
| 99 | |
| 100 | String Performance::get_monitor_name(Monitor p_monitor) const { |
| 101 | ERR_FAIL_INDEX_V(p_monitor, MONITOR_MAX, String()); |
| 102 | static const char *names[MONITOR_MAX] = { |
| 103 | "time/fps" , |
| 104 | "time/process" , |
| 105 | "time/physics_process" , |
| 106 | "time/navigation_process" , |
| 107 | "memory/static" , |
| 108 | "memory/static_max" , |
| 109 | "memory/msg_buf_max" , |
| 110 | "object/objects" , |
| 111 | "object/resources" , |
| 112 | "object/nodes" , |
| 113 | "object/orphan_nodes" , |
| 114 | "raster/total_objects_drawn" , |
| 115 | "raster/total_primitives_drawn" , |
| 116 | "raster/total_draw_calls" , |
| 117 | "video/video_mem" , |
| 118 | "video/texture_mem" , |
| 119 | "video/buffer_mem" , |
| 120 | "physics_2d/active_objects" , |
| 121 | "physics_2d/collision_pairs" , |
| 122 | "physics_2d/islands" , |
| 123 | "physics_3d/active_objects" , |
| 124 | "physics_3d/collision_pairs" , |
| 125 | "physics_3d/islands" , |
| 126 | "audio/driver/output_latency" , |
| 127 | "navigation/active_maps" , |
| 128 | "navigation/regions" , |
| 129 | "navigation/agents" , |
| 130 | "navigation/links" , |
| 131 | "navigation/polygons" , |
| 132 | "navigation/edges" , |
| 133 | "navigation/edges_merged" , |
| 134 | "navigation/edges_connected" , |
| 135 | "navigation/edges_free" , |
| 136 | |
| 137 | }; |
| 138 | |
| 139 | return names[p_monitor]; |
| 140 | } |
| 141 | |
| 142 | double Performance::get_monitor(Monitor p_monitor) const { |
| 143 | switch (p_monitor) { |
| 144 | case TIME_FPS: |
| 145 | return Engine::get_singleton()->get_frames_per_second(); |
| 146 | case TIME_PROCESS: |
| 147 | return _process_time; |
| 148 | case TIME_PHYSICS_PROCESS: |
| 149 | return _physics_process_time; |
| 150 | case TIME_NAVIGATION_PROCESS: |
| 151 | return _navigation_process_time; |
| 152 | case MEMORY_STATIC: |
| 153 | return Memory::get_mem_usage(); |
| 154 | case MEMORY_STATIC_MAX: |
| 155 | return Memory::get_mem_max_usage(); |
| 156 | case MEMORY_MESSAGE_BUFFER_MAX: |
| 157 | return MessageQueue::get_singleton()->get_max_buffer_usage(); |
| 158 | case OBJECT_COUNT: |
| 159 | return ObjectDB::get_object_count(); |
| 160 | case OBJECT_RESOURCE_COUNT: |
| 161 | return ResourceCache::get_cached_resource_count(); |
| 162 | case OBJECT_NODE_COUNT: |
| 163 | return _get_node_count(); |
| 164 | case OBJECT_ORPHAN_NODE_COUNT: |
| 165 | return Node::orphan_node_count; |
| 166 | case RENDER_TOTAL_OBJECTS_IN_FRAME: |
| 167 | return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TOTAL_OBJECTS_IN_FRAME); |
| 168 | case RENDER_TOTAL_PRIMITIVES_IN_FRAME: |
| 169 | return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TOTAL_PRIMITIVES_IN_FRAME); |
| 170 | case RENDER_TOTAL_DRAW_CALLS_IN_FRAME: |
| 171 | return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TOTAL_DRAW_CALLS_IN_FRAME); |
| 172 | case RENDER_VIDEO_MEM_USED: |
| 173 | return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_VIDEO_MEM_USED); |
| 174 | case RENDER_TEXTURE_MEM_USED: |
| 175 | return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TEXTURE_MEM_USED); |
| 176 | case RENDER_BUFFER_MEM_USED: |
| 177 | return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_BUFFER_MEM_USED); |
| 178 | case PHYSICS_2D_ACTIVE_OBJECTS: |
| 179 | return PhysicsServer2D::get_singleton()->get_process_info(PhysicsServer2D::INFO_ACTIVE_OBJECTS); |
| 180 | case PHYSICS_2D_COLLISION_PAIRS: |
| 181 | return PhysicsServer2D::get_singleton()->get_process_info(PhysicsServer2D::INFO_COLLISION_PAIRS); |
| 182 | case PHYSICS_2D_ISLAND_COUNT: |
| 183 | return PhysicsServer2D::get_singleton()->get_process_info(PhysicsServer2D::INFO_ISLAND_COUNT); |
| 184 | case PHYSICS_3D_ACTIVE_OBJECTS: |
| 185 | return PhysicsServer3D::get_singleton()->get_process_info(PhysicsServer3D::INFO_ACTIVE_OBJECTS); |
| 186 | case PHYSICS_3D_COLLISION_PAIRS: |
| 187 | return PhysicsServer3D::get_singleton()->get_process_info(PhysicsServer3D::INFO_COLLISION_PAIRS); |
| 188 | case PHYSICS_3D_ISLAND_COUNT: |
| 189 | return PhysicsServer3D::get_singleton()->get_process_info(PhysicsServer3D::INFO_ISLAND_COUNT); |
| 190 | case AUDIO_OUTPUT_LATENCY: |
| 191 | return AudioServer::get_singleton()->get_output_latency(); |
| 192 | case NAVIGATION_ACTIVE_MAPS: |
| 193 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_ACTIVE_MAPS); |
| 194 | case NAVIGATION_REGION_COUNT: |
| 195 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_REGION_COUNT); |
| 196 | case NAVIGATION_AGENT_COUNT: |
| 197 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_AGENT_COUNT); |
| 198 | case NAVIGATION_LINK_COUNT: |
| 199 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_LINK_COUNT); |
| 200 | case NAVIGATION_POLYGON_COUNT: |
| 201 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_POLYGON_COUNT); |
| 202 | case NAVIGATION_EDGE_COUNT: |
| 203 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_COUNT); |
| 204 | case NAVIGATION_EDGE_MERGE_COUNT: |
| 205 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_MERGE_COUNT); |
| 206 | case NAVIGATION_EDGE_CONNECTION_COUNT: |
| 207 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_CONNECTION_COUNT); |
| 208 | case NAVIGATION_EDGE_FREE_COUNT: |
| 209 | return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_FREE_COUNT); |
| 210 | |
| 211 | default: { |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | return 0; |
| 216 | } |
| 217 | |
| 218 | Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const { |
| 219 | ERR_FAIL_INDEX_V(p_monitor, MONITOR_MAX, MONITOR_TYPE_QUANTITY); |
| 220 | // ugly |
| 221 | static const MonitorType types[MONITOR_MAX] = { |
| 222 | MONITOR_TYPE_QUANTITY, |
| 223 | MONITOR_TYPE_TIME, |
| 224 | MONITOR_TYPE_TIME, |
| 225 | MONITOR_TYPE_TIME, |
| 226 | MONITOR_TYPE_MEMORY, |
| 227 | MONITOR_TYPE_MEMORY, |
| 228 | MONITOR_TYPE_MEMORY, |
| 229 | MONITOR_TYPE_QUANTITY, |
| 230 | MONITOR_TYPE_QUANTITY, |
| 231 | MONITOR_TYPE_QUANTITY, |
| 232 | MONITOR_TYPE_QUANTITY, |
| 233 | MONITOR_TYPE_QUANTITY, |
| 234 | MONITOR_TYPE_QUANTITY, |
| 235 | MONITOR_TYPE_QUANTITY, |
| 236 | MONITOR_TYPE_MEMORY, |
| 237 | MONITOR_TYPE_MEMORY, |
| 238 | MONITOR_TYPE_MEMORY, |
| 239 | MONITOR_TYPE_QUANTITY, |
| 240 | MONITOR_TYPE_QUANTITY, |
| 241 | MONITOR_TYPE_QUANTITY, |
| 242 | MONITOR_TYPE_QUANTITY, |
| 243 | MONITOR_TYPE_QUANTITY, |
| 244 | MONITOR_TYPE_QUANTITY, |
| 245 | MONITOR_TYPE_TIME, |
| 246 | MONITOR_TYPE_QUANTITY, |
| 247 | MONITOR_TYPE_QUANTITY, |
| 248 | MONITOR_TYPE_QUANTITY, |
| 249 | MONITOR_TYPE_QUANTITY, |
| 250 | MONITOR_TYPE_QUANTITY, |
| 251 | MONITOR_TYPE_QUANTITY, |
| 252 | MONITOR_TYPE_QUANTITY, |
| 253 | MONITOR_TYPE_QUANTITY, |
| 254 | MONITOR_TYPE_QUANTITY, |
| 255 | |
| 256 | }; |
| 257 | |
| 258 | return types[p_monitor]; |
| 259 | } |
| 260 | |
| 261 | void Performance::set_process_time(double p_pt) { |
| 262 | _process_time = p_pt; |
| 263 | } |
| 264 | |
| 265 | void Performance::set_physics_process_time(double p_pt) { |
| 266 | _physics_process_time = p_pt; |
| 267 | } |
| 268 | |
| 269 | void Performance::set_navigation_process_time(double p_pt) { |
| 270 | _navigation_process_time = p_pt; |
| 271 | } |
| 272 | |
| 273 | void Performance::add_custom_monitor(const StringName &p_id, const Callable &p_callable, const Vector<Variant> &p_args) { |
| 274 | ERR_FAIL_COND_MSG(has_custom_monitor(p_id), "Custom monitor with id '" + String(p_id) + "' already exists." ); |
| 275 | _monitor_map.insert(p_id, MonitorCall(p_callable, p_args)); |
| 276 | _monitor_modification_time = OS::get_singleton()->get_ticks_usec(); |
| 277 | } |
| 278 | |
| 279 | void Performance::remove_custom_monitor(const StringName &p_id) { |
| 280 | ERR_FAIL_COND_MSG(!has_custom_monitor(p_id), "Custom monitor with id '" + String(p_id) + "' doesn't exists." ); |
| 281 | _monitor_map.erase(p_id); |
| 282 | _monitor_modification_time = OS::get_singleton()->get_ticks_usec(); |
| 283 | } |
| 284 | |
| 285 | bool Performance::has_custom_monitor(const StringName &p_id) { |
| 286 | return _monitor_map.has(p_id); |
| 287 | } |
| 288 | |
| 289 | Variant Performance::get_custom_monitor(const StringName &p_id) { |
| 290 | ERR_FAIL_COND_V_MSG(!has_custom_monitor(p_id), Variant(), "Custom monitor with id '" + String(p_id) + "' doesn't exists." ); |
| 291 | bool error; |
| 292 | String error_message; |
| 293 | Variant return_value = _monitor_map[p_id].call(error, error_message); |
| 294 | ERR_FAIL_COND_V_MSG(error, return_value, "Error calling from custom monitor '" + String(p_id) + "' to callable: " + error_message); |
| 295 | return return_value; |
| 296 | } |
| 297 | |
| 298 | TypedArray<StringName> Performance::get_custom_monitor_names() { |
| 299 | if (!_monitor_map.size()) { |
| 300 | return TypedArray<StringName>(); |
| 301 | } |
| 302 | TypedArray<StringName> return_array; |
| 303 | return_array.resize(_monitor_map.size()); |
| 304 | int index = 0; |
| 305 | for (KeyValue<StringName, MonitorCall> i : _monitor_map) { |
| 306 | return_array.set(index, i.key); |
| 307 | index++; |
| 308 | } |
| 309 | return return_array; |
| 310 | } |
| 311 | |
| 312 | uint64_t Performance::get_monitor_modification_time() { |
| 313 | return _monitor_modification_time; |
| 314 | } |
| 315 | |
| 316 | Performance::Performance() { |
| 317 | _process_time = 0; |
| 318 | _physics_process_time = 0; |
| 319 | _navigation_process_time = 0; |
| 320 | _monitor_modification_time = 0; |
| 321 | singleton = this; |
| 322 | } |
| 323 | |
| 324 | Performance::MonitorCall::MonitorCall(Callable p_callable, Vector<Variant> p_arguments) { |
| 325 | _callable = p_callable; |
| 326 | _arguments = p_arguments; |
| 327 | } |
| 328 | |
| 329 | Performance::MonitorCall::MonitorCall() { |
| 330 | } |
| 331 | |
| 332 | Variant Performance::MonitorCall::call(bool &r_error, String &r_error_message) { |
| 333 | Vector<const Variant *> arguments_mem; |
| 334 | arguments_mem.resize(_arguments.size()); |
| 335 | for (int i = 0; i < _arguments.size(); i++) { |
| 336 | arguments_mem.write[i] = &_arguments[i]; |
| 337 | } |
| 338 | const Variant **args = (const Variant **)arguments_mem.ptr(); |
| 339 | int argc = _arguments.size(); |
| 340 | Variant return_value; |
| 341 | Callable::CallError error; |
| 342 | _callable.callp(args, argc, return_value, error); |
| 343 | r_error = (error.error != Callable::CallError::CALL_OK); |
| 344 | if (r_error) { |
| 345 | r_error_message = Variant::get_callable_error_text(_callable, args, argc, error); |
| 346 | } |
| 347 | return return_value; |
| 348 | } |
| 349 | |