[Debugger] New extensible EngineProfiler class.

Uses GDExtension, replaces old Callable system for profilers, and is
also used internally.
This commit is contained in:
Fabio Alessandrelli 2022-02-06 01:55:58 +01:00
parent 79a4d782a5
commit 789e648f4d
7 changed files with 204 additions and 84 deletions

View File

@ -2376,21 +2376,18 @@ bool EngineDebugger::is_active() {
return ::EngineDebugger::is_active(); return ::EngineDebugger::is_active();
} }
void EngineDebugger::register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) { void EngineDebugger::register_profiler(const StringName &p_name, Ref<EngineProfiler> p_profiler) {
ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler already registered: " + p_name); ERR_FAIL_COND(p_profiler.is_null());
profilers.insert(p_name, ProfilerCallable(p_toggle, p_add, p_tick)); ERR_FAIL_COND_MSG(p_profiler->is_bound(), "Profiler already registered.");
ProfilerCallable &p = profilers[p_name]; ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler name already in use: " + p_name);
::EngineDebugger::Profiler profiler( Error err = p_profiler->bind(p_name);
&p, ERR_FAIL_COND_MSG(err != OK, "Profiler failed to register with error: " + itos(err));
&EngineDebugger::call_toggle, profilers.insert(p_name, p_profiler);
&EngineDebugger::call_add,
&EngineDebugger::call_tick);
::EngineDebugger::register_profiler(p_name, profiler);
} }
void EngineDebugger::unregister_profiler(const StringName &p_name) { void EngineDebugger::unregister_profiler(const StringName &p_name) {
ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name); ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name);
::EngineDebugger::unregister_profiler(p_name); profilers[p_name]->unbind();
profilers.erase(p_name); profilers.erase(p_name);
} }
@ -2435,45 +2432,6 @@ void EngineDebugger::send_message(const String &p_msg, const Array &p_data) {
::EngineDebugger::get_singleton()->send_message(p_msg, p_data); ::EngineDebugger::get_singleton()->send_message(p_msg, p_data);
} }
void EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_opts) {
Callable &toggle = ((ProfilerCallable *)p_user)->callable_toggle;
if (toggle.is_null()) {
return;
}
Variant enable = p_enable, opts = p_opts;
const Variant *args[2] = { &enable, &opts };
Variant retval;
Callable::CallError err;
toggle.call(args, 2, retval, err);
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'toggle' to callable: " + Variant::get_callable_error_text(toggle, args, 2, err));
}
void EngineDebugger::call_add(void *p_user, const Array &p_data) {
Callable &add = ((ProfilerCallable *)p_user)->callable_add;
if (add.is_null()) {
return;
}
Variant data = p_data;
const Variant *args[1] = { &data };
Variant retval;
Callable::CallError err;
add.call(args, 1, retval, err);
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'add' to callable: " + Variant::get_callable_error_text(add, args, 1, err));
}
void EngineDebugger::call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
Callable &tick = ((ProfilerCallable *)p_user)->callable_tick;
if (tick.is_null()) {
return;
}
Variant frame_time = p_frame_time, idle_time = p_idle_time, physics_time = p_physics_time, physics_frame_time = p_physics_frame_time;
const Variant *args[4] = { &frame_time, &idle_time, &physics_time, &physics_frame_time };
Variant retval;
Callable::CallError err;
tick.call(args, 4, retval, err);
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'tick' to callable: " + Variant::get_callable_error_text(tick, args, 4, err));
}
Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) { Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
Callable &capture = *(Callable *)p_user; Callable &capture = *(Callable *)p_user;
if (capture.is_null()) { if (capture.is_null()) {
@ -2495,10 +2453,6 @@ EngineDebugger::~EngineDebugger() {
::EngineDebugger::unregister_message_capture(E.key); ::EngineDebugger::unregister_message_capture(E.key);
} }
captures.clear(); captures.clear();
for (const KeyValue<StringName, ProfilerCallable> &E : profilers) {
::EngineDebugger::unregister_profiler(E.key);
}
profilers.clear();
} }
EngineDebugger *EngineDebugger::singleton = nullptr; EngineDebugger *EngineDebugger::singleton = nullptr;
@ -2506,8 +2460,9 @@ EngineDebugger *EngineDebugger::singleton = nullptr;
void EngineDebugger::_bind_methods() { void EngineDebugger::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_active"), &EngineDebugger::is_active); ClassDB::bind_method(D_METHOD("is_active"), &EngineDebugger::is_active);
ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &EngineDebugger::register_profiler); ClassDB::bind_method(D_METHOD("register_profiler", "name", "profiler"), &EngineDebugger::register_profiler);
ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &EngineDebugger::unregister_profiler); ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &EngineDebugger::unregister_profiler);
ClassDB::bind_method(D_METHOD("is_profiling", "name"), &EngineDebugger::is_profiling); ClassDB::bind_method(D_METHOD("is_profiling", "name"), &EngineDebugger::is_profiling);
ClassDB::bind_method(D_METHOD("has_profiler", "name"), &EngineDebugger::has_profiler); ClassDB::bind_method(D_METHOD("has_profiler", "name"), &EngineDebugger::has_profiler);

View File

@ -31,6 +31,7 @@
#ifndef CORE_BIND_H #ifndef CORE_BIND_H
#define CORE_BIND_H #define CORE_BIND_H
#include "core/debugger/engine_profiler.h"
#include "core/io/compression.h" #include "core/io/compression.h"
#include "core/io/dir_access.h" #include "core/io/dir_access.h"
#include "core/io/file_access.h" #include "core/io/file_access.h"
@ -673,25 +674,8 @@ public:
class EngineDebugger : public Object { class EngineDebugger : public Object {
GDCLASS(EngineDebugger, Object); GDCLASS(EngineDebugger, Object);
class ProfilerCallable {
friend class EngineDebugger;
Callable callable_toggle;
Callable callable_add;
Callable callable_tick;
public:
ProfilerCallable() {}
ProfilerCallable(const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) {
callable_toggle = p_toggle;
callable_add = p_add;
callable_tick = p_tick;
}
};
Map<StringName, Callable> captures; Map<StringName, Callable> captures;
Map<StringName, ProfilerCallable> profilers; Map<StringName, Ref<EngineProfiler>> profilers;
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -702,7 +686,7 @@ public:
bool is_active(); bool is_active();
void register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick); void register_profiler(const StringName &p_name, Ref<EngineProfiler> p_profiler);
void unregister_profiler(const StringName &p_name); void unregister_profiler(const StringName &p_name);
bool is_profiling(const StringName &p_name); bool is_profiling(const StringName &p_name);
bool has_profiler(const StringName &p_name); bool has_profiler(const StringName &p_name);
@ -715,9 +699,6 @@ public:
void send_message(const String &p_msg, const Array &p_data); void send_message(const String &p_msg, const Array &p_data);
static void call_toggle(void *p_user, bool p_enable, const Array &p_opts);
static void call_add(void *p_user, const Array &p_data);
static void call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time);
static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured); static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);
EngineDebugger() { singleton = this; } EngineDebugger() { singleton = this; }

View File

@ -0,0 +1,82 @@
/*************************************************************************/
/* engine_profiler.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#include "engine_profiler.h"
#include "core/debugger/engine_debugger.h"
void EngineProfiler::_bind_methods() {
GDVIRTUAL_BIND(_toggle, "enable", "options");
GDVIRTUAL_BIND(_add_frame, "data");
GDVIRTUAL_BIND(_tick, "frame_time", "idle_time", "physics_time", "physics_frame_time");
}
void EngineProfiler::toggle(bool p_enable, const Array &p_array) {
GDVIRTUAL_CALL(_toggle, p_enable, p_array);
}
void EngineProfiler::add(const Array &p_data) {
GDVIRTUAL_CALL(_add_frame, p_data);
}
void EngineProfiler::tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
GDVIRTUAL_CALL(_tick, p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
}
Error EngineProfiler::bind(const String &p_name) {
ERR_FAIL_COND_V(is_bound(), ERR_ALREADY_IN_USE);
EngineDebugger::Profiler prof(
this,
[](void *p_user, bool p_enable, const Array &p_opts) {
((EngineProfiler *)p_user)->toggle(p_enable, p_opts);
},
[](void *p_user, const Array &p_data) {
((EngineProfiler *)p_user)->add(p_data);
},
[](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
((EngineProfiler *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
});
registration = p_name;
EngineDebugger::register_profiler(p_name, prof);
return OK;
}
Error EngineProfiler::unbind() {
ERR_FAIL_COND_V(!is_bound(), ERR_UNCONFIGURED);
EngineDebugger::unregister_profiler(registration);
registration.clear();
return OK;
}
EngineProfiler::~EngineProfiler() {
if (is_bound()) {
unbind();
}
}

View File

@ -0,0 +1,65 @@
/*************************************************************************/
/* engine_profiler.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef ENGINE_PROFILER_H
#define ENGINE_PROFILER_H
#include "core/object/ref_counted.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/object/script_language.h"
class EngineProfiler : public RefCounted {
GDCLASS(EngineProfiler, RefCounted);
private:
String registration;
protected:
static void _bind_methods();
public:
virtual void toggle(bool p_enable, const Array &p_opts);
virtual void add(const Array &p_data);
virtual void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time);
Error bind(const String &p_name);
Error unbind();
bool is_bound() const { return registration.length() > 0; }
GDVIRTUAL2(_toggle, bool, Array);
GDVIRTUAL1(_add_frame, Array);
GDVIRTUAL4(_tick, double, double, double, double);
EngineProfiler() {}
virtual ~EngineProfiler();
};
#endif // ENGINE_PROFILER_H

View File

@ -37,6 +37,7 @@
#include "core/crypto/aes_context.h" #include "core/crypto/aes_context.h"
#include "core/crypto/crypto.h" #include "core/crypto/crypto.h"
#include "core/crypto/hashing_context.h" #include "core/crypto/hashing_context.h"
#include "core/debugger/engine_profiler.h"
#include "core/extension/native_extension.h" #include "core/extension/native_extension.h"
#include "core/extension/native_extension_manager.h" #include "core/extension/native_extension_manager.h"
#include "core/input/input.h" #include "core/input/input.h"
@ -237,6 +238,8 @@ void register_core_types() {
GDREGISTER_VIRTUAL_CLASS(ResourceUID); GDREGISTER_VIRTUAL_CLASS(ResourceUID);
GDREGISTER_CLASS(EngineProfiler);
resource_uid = memnew(ResourceUID); resource_uid = memnew(ResourceUID);
native_extension_manager = memnew(NativeExtensionManager); native_extension_manager = memnew(NativeExtensionManager);

View File

@ -65,14 +65,9 @@
<method name="register_profiler"> <method name="register_profiler">
<return type="void" /> <return type="void" />
<argument index="0" name="name" type="StringName" /> <argument index="0" name="name" type="StringName" />
<argument index="1" name="toggle" type="Callable" /> <argument index="1" name="profiler" type="EngineProfiler" />
<argument index="2" name="add" type="Callable" />
<argument index="3" name="tick" type="Callable" />
<description> <description>
Registers a profiler with the given [code]name[/code]. Registers a profiler with the given [code]name[/code]. See [EngineProfiler] for more informations.
[code]toggle[/code] callable is called when the profiler is enabled/disabled. It must take an argument array as an argument.
[code]add[/code] callable is called when data is added to profiler using [method EngineDebugger.profiler_add_frame_data]. It must take a data array as argument.
[code]tick[/code] callable is called at every active profiler iteration. It must take frame time, idle time, physics time, and physics idle time as arguments.
</description> </description>
</method> </method>
<method name="send_message"> <method name="send_message">

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EngineProfiler" inherits="RefCounted" version="4.0">
<brief_description>
Base class for creating custom profilers.
</brief_description>
<description>
This class can be used to implement custom profilers that are able to interact with the engine and editor debugger.
See [EngineDebugger] and [EditorDebuggerPlugin] for more informations.
</description>
<tutorials>
</tutorials>
<methods>
<method name="_add_frame" qualifiers="virtual">
<return type="void" />
<argument index="0" name="data" type="Array" />
<description>
Called when data is added to profiler using [method EngineDebugger.profiler_add_frame_data].
</description>
</method>
<method name="_tick" qualifiers="virtual">
<return type="void" />
<argument index="0" name="frame_time" type="float" />
<argument index="1" name="idle_time" type="float" />
<argument index="2" name="physics_time" type="float" />
<argument index="3" name="physics_frame_time" type="float" />
<description>
Called once every engine iteration when the profiler is active with informations about the current frame.
</description>
</method>
<method name="_toggle" qualifiers="virtual">
<return type="void" />
<argument index="0" name="enable" type="bool" />
<argument index="1" name="options" type="Array" />
<description>
Called when the profiler is enabled/disabled, along with a set of [code]options[/code].
</description>
</method>
</methods>
</class>