Add Startup benchmarking support
This adds support for benchmarking engine startup (and editor startup if used). The goal is to use this in the benchmarking server to track improvements and changes to engine, editor, importer and scene loading startup times.
This commit is contained in:
parent
35cfaafda8
commit
a3936adb29
6 changed files with 124 additions and 5 deletions
|
@ -33,7 +33,9 @@
|
|||
#include "core/authors.gen.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/donors.gen.h"
|
||||
#include "core/io/json.h"
|
||||
#include "core/license.gen.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/version.h"
|
||||
|
||||
void Engine::set_physics_ticks_per_second(int p_ips) {
|
||||
|
@ -307,6 +309,43 @@ Engine::Engine() {
|
|||
singleton = this;
|
||||
}
|
||||
|
||||
void Engine::startup_begin() {
|
||||
startup_benchmark_total_from = OS::get_singleton()->get_ticks_usec();
|
||||
}
|
||||
|
||||
void Engine::startup_benchmark_begin_measure(const String &p_what) {
|
||||
startup_benchmark_section = p_what;
|
||||
startup_benchmark_from = OS::get_singleton()->get_ticks_usec();
|
||||
}
|
||||
void Engine::startup_benchmark_end_measure() {
|
||||
uint64_t total = OS::get_singleton()->get_ticks_usec() - startup_benchmark_from;
|
||||
double total_f = double(total) / double(1000000);
|
||||
|
||||
startup_benchmark_json[startup_benchmark_section] = total_f;
|
||||
}
|
||||
|
||||
void Engine::startup_dump(const String &p_to_file) {
|
||||
uint64_t total = OS::get_singleton()->get_ticks_usec() - startup_benchmark_total_from;
|
||||
double total_f = double(total) / double(1000000);
|
||||
startup_benchmark_json["total_time"] = total_f;
|
||||
|
||||
if (!p_to_file.is_empty()) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_to_file, FileAccess::WRITE);
|
||||
if (f.is_valid()) {
|
||||
Ref<JSON> json;
|
||||
json.instantiate();
|
||||
f->store_string(json->stringify(startup_benchmark_json, "\t", false, true));
|
||||
}
|
||||
} else {
|
||||
List<Variant> keys;
|
||||
startup_benchmark_json.get_key_list(&keys);
|
||||
print_line("STARTUP BENCHMARK:");
|
||||
for (const Variant &K : keys) {
|
||||
print_line("\t-", K, ": ", startup_benchmark_json[K], +" sec.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr, const StringName &p_class_name) :
|
||||
name(p_name),
|
||||
ptr(p_ptr),
|
||||
|
|
|
@ -79,6 +79,11 @@ private:
|
|||
String write_movie_path;
|
||||
String shader_cache_path;
|
||||
|
||||
Dictionary startup_benchmark_json;
|
||||
String startup_benchmark_section;
|
||||
uint64_t startup_benchmark_from = 0;
|
||||
uint64_t startup_benchmark_total_from = 0;
|
||||
|
||||
public:
|
||||
static Engine *get_singleton();
|
||||
|
||||
|
@ -151,6 +156,11 @@ public:
|
|||
bool is_validation_layers_enabled() const;
|
||||
int32_t get_gpu_index() const;
|
||||
|
||||
void startup_begin();
|
||||
void startup_benchmark_begin_measure(const String &p_what);
|
||||
void startup_benchmark_end_measure();
|
||||
void startup_dump(const String &p_to_file);
|
||||
|
||||
Engine();
|
||||
virtual ~Engine() {}
|
||||
};
|
||||
|
|
|
@ -1192,11 +1192,6 @@ void EditorFileSystem::scan_changes() {
|
|||
|
||||
void EditorFileSystem::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
call_deferred(SNAME("scan")); //this should happen after every editor node entered the tree
|
||||
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
Thread &active_thread = thread.is_started() ? thread : thread_sources;
|
||||
if (use_threads && active_thread.is_started()) {
|
||||
|
|
|
@ -661,6 +661,7 @@ void EditorNode::_notification(int p_what) {
|
|||
|
||||
command_palette->register_shortcuts_as_command();
|
||||
|
||||
MessageQueue::get_singleton()->push_callable(callable_mp(this, &EditorNode::_begin_first_scan));
|
||||
/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
|
||||
} break;
|
||||
|
||||
|
@ -1043,6 +1044,8 @@ void EditorNode::_sources_changed(bool p_exist) {
|
|||
if (waiting_for_first_scan) {
|
||||
waiting_for_first_scan = false;
|
||||
|
||||
Engine::get_singleton()->startup_benchmark_end_measure(); // editor_scan_and_reimport
|
||||
|
||||
// Reload the global shader variables, but this time
|
||||
// loading textures, as they are now properly imported.
|
||||
RenderingServer::get_singleton()->global_shader_uniforms_load_settings(true);
|
||||
|
@ -1055,8 +1058,16 @@ void EditorNode::_sources_changed(bool p_exist) {
|
|||
_load_docks();
|
||||
|
||||
if (!defer_load_scene.is_empty()) {
|
||||
Engine::get_singleton()->startup_benchmark_begin_measure("editor_load_scene");
|
||||
load_scene(defer_load_scene);
|
||||
defer_load_scene = "";
|
||||
Engine::get_singleton()->startup_benchmark_end_measure();
|
||||
|
||||
if (use_startup_benchmark) {
|
||||
Engine::get_singleton()->startup_dump(startup_benchmark_file);
|
||||
startup_benchmark_file = String();
|
||||
use_startup_benchmark = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4318,6 +4329,15 @@ void EditorNode::_editor_file_dialog_unregister(EditorFileDialog *p_dialog) {
|
|||
|
||||
Vector<EditorNodeInitCallback> EditorNode::_init_callbacks;
|
||||
|
||||
void EditorNode::_begin_first_scan() {
|
||||
Engine::get_singleton()->startup_benchmark_begin_measure("editor_scan_and_import");
|
||||
EditorFileSystem::get_singleton()->scan();
|
||||
}
|
||||
void EditorNode::set_use_startup_benchmark(bool p_use_startup_benchmark, const String &p_startup_benchmark_file) {
|
||||
use_startup_benchmark = p_use_startup_benchmark;
|
||||
startup_benchmark_file = p_startup_benchmark_file;
|
||||
}
|
||||
|
||||
Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only) {
|
||||
export_defer.preset = p_preset;
|
||||
export_defer.path = p_path;
|
||||
|
|
|
@ -682,6 +682,10 @@ private:
|
|||
void _bottom_panel_switch(bool p_enable, int p_idx);
|
||||
void _bottom_panel_raise_toggled(bool);
|
||||
|
||||
void _begin_first_scan();
|
||||
bool use_startup_benchmark = false;
|
||||
String startup_benchmark_file;
|
||||
|
||||
protected:
|
||||
friend class FileSystemDock;
|
||||
|
||||
|
@ -816,6 +820,7 @@ public:
|
|||
|
||||
void _copy_warning(const String &p_str);
|
||||
|
||||
void set_use_startup_benchmark(bool p_use_startup_benchmark, const String &p_startup_benchmark_file);
|
||||
Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only);
|
||||
|
||||
Control *get_gui_base() { return gui_base; }
|
||||
|
|
|
@ -154,6 +154,8 @@ static OS::ProcessID editor_pid = 0;
|
|||
static bool auto_build_solutions = false;
|
||||
static String debug_server_uri;
|
||||
#endif
|
||||
bool use_startup_benchmark = false;
|
||||
String startup_benchmark_file;
|
||||
|
||||
// Display
|
||||
|
||||
|
@ -386,6 +388,8 @@ void Main::print_help(const char *p_binary) {
|
|||
OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
|
||||
OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
|
||||
OS::get_singleton()->print(" --dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.\n");
|
||||
OS::get_singleton()->print(" --startup-benchmark Benchmark the startup time and print it to console.\n");
|
||||
OS::get_singleton()->print(" --startup-benchmark-file <path> Benchmark the startup time and save it to a given file in JSON format.\n");
|
||||
#ifdef TESTS_ENABLED
|
||||
OS::get_singleton()->print(" --test [--help] Run unit tests. Use --test --help for more information.\n");
|
||||
#endif
|
||||
|
@ -594,6 +598,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
engine = memnew(Engine);
|
||||
|
||||
MAIN_PRINT("Main: Initialize CORE");
|
||||
engine->startup_begin();
|
||||
engine->startup_benchmark_begin_measure("core");
|
||||
|
||||
register_core_types();
|
||||
register_core_driver_types();
|
||||
|
@ -1208,6 +1214,19 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
OS::get_singleton()->print("Missing --xr-mode argument, aborting.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else if (I->get() == "--startup-benchmark") {
|
||||
use_startup_benchmark = true;
|
||||
} else if (I->get() == "--startup-benchmark-file") {
|
||||
if (I->next()) {
|
||||
use_startup_benchmark = true;
|
||||
startup_benchmark_file = I->next()->get();
|
||||
N = I->next()->next();
|
||||
} else {
|
||||
OS::get_singleton()->print("Missing <path> argument for --startup-benchmark-file <path>.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else if (I->get() == "--") {
|
||||
adding_user_args = true;
|
||||
} else {
|
||||
|
@ -1624,6 +1643,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
|
||||
message_queue = memnew(MessageQueue);
|
||||
|
||||
engine->startup_benchmark_end_measure(); // core
|
||||
|
||||
if (p_second_phase) {
|
||||
return setup2();
|
||||
}
|
||||
|
@ -1690,6 +1711,8 @@ error:
|
|||
}
|
||||
|
||||
Error Main::setup2(Thread::ID p_main_tid_override) {
|
||||
engine->startup_benchmark_begin_measure("servers");
|
||||
|
||||
tsman = memnew(TextServerManager);
|
||||
|
||||
if (tsman) {
|
||||
|
@ -2047,8 +2070,12 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
|
|||
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface.");
|
||||
}
|
||||
|
||||
engine->startup_benchmark_end_measure(); // servers
|
||||
|
||||
MAIN_PRINT("Main: Load Scene Types");
|
||||
|
||||
engine->startup_benchmark_begin_measure("scene");
|
||||
|
||||
register_scene_types();
|
||||
register_driver_types();
|
||||
|
||||
|
@ -2124,6 +2151,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
|
|||
print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR)));
|
||||
MAIN_PRINT("Main: Done");
|
||||
|
||||
engine->startup_benchmark_end_measure(); // scene
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -2462,6 +2491,7 @@ bool Main::start() {
|
|||
if (!project_manager && !editor) { // game
|
||||
if (!game_path.is_empty() || !script.is_empty()) {
|
||||
//autoload
|
||||
Engine::get_singleton()->startup_benchmark_begin_measure("load_autoloads");
|
||||
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
|
||||
|
||||
//first pass, add the constants so they exist before any script is loaded
|
||||
|
@ -2516,12 +2546,14 @@ bool Main::start() {
|
|||
for (Node *E : to_add) {
|
||||
sml->get_root()->add_child(E);
|
||||
}
|
||||
Engine::get_singleton()->startup_benchmark_end_measure(); // load autoloads
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorNode *editor_node = nullptr;
|
||||
if (editor) {
|
||||
Engine::get_singleton()->startup_benchmark_begin_measure("editor");
|
||||
editor_node = memnew(EditorNode);
|
||||
sml->get_root()->add_child(editor_node);
|
||||
|
||||
|
@ -2529,6 +2561,13 @@ bool Main::start() {
|
|||
editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only);
|
||||
game_path = ""; // Do not load anything.
|
||||
}
|
||||
|
||||
Engine::get_singleton()->startup_benchmark_end_measure();
|
||||
|
||||
editor_node->set_use_startup_benchmark(use_startup_benchmark, startup_benchmark_file);
|
||||
// Editor takes over
|
||||
use_startup_benchmark = false;
|
||||
startup_benchmark_file = String();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -2693,6 +2732,8 @@ bool Main::start() {
|
|||
|
||||
if (!project_manager && !editor) { // game
|
||||
|
||||
Engine::get_singleton()->startup_benchmark_begin_measure("game_load");
|
||||
|
||||
// Load SSL Certificates from Project Settings (or builtin).
|
||||
Crypto::load_default_certificates(GLOBAL_DEF("network/ssl/certificate_bundle_override", ""));
|
||||
|
||||
|
@ -2732,16 +2773,20 @@ bool Main::start() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Engine::get_singleton()->startup_benchmark_end_measure(); // game_load
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (project_manager) {
|
||||
Engine::get_singleton()->startup_benchmark_begin_measure("project_manager");
|
||||
Engine::get_singleton()->set_editor_hint(true);
|
||||
ProjectManager *pmanager = memnew(ProjectManager);
|
||||
ProgressDialog *progress_dialog = memnew(ProgressDialog);
|
||||
pmanager->add_child(progress_dialog);
|
||||
sml->get_root()->add_child(pmanager);
|
||||
DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_PROJECTMAN);
|
||||
Engine::get_singleton()->startup_benchmark_end_measure();
|
||||
}
|
||||
|
||||
if (project_manager || editor) {
|
||||
|
@ -2771,6 +2816,11 @@ bool Main::start() {
|
|||
}
|
||||
}
|
||||
|
||||
if (use_startup_benchmark) {
|
||||
Engine::get_singleton()->startup_dump(startup_benchmark_file);
|
||||
startup_benchmark_file = String();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue