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