Browse Source

Merge pull request #64610 from reduz/startup-benchmark-support

Rémi Verschelde 3 years ago
parent
commit
1fbf7b1ba5
6 changed files with 124 additions and 5 deletions
  1. 39 0
      core/config/engine.cpp
  2. 10 0
      core/config/engine.h
  3. 0 5
      editor/editor_file_system.cpp
  4. 20 0
      editor/editor_node.cpp
  5. 5 0
      editor/editor_node.h
  6. 50 0
      main/main.cpp

+ 39 - 0
core/config/engine.cpp

@@ -33,7 +33,9 @@
 #include "core/authors.gen.h"
 #include "core/authors.gen.h"
 #include "core/config/project_settings.h"
 #include "core/config/project_settings.h"
 #include "core/donors.gen.h"
 #include "core/donors.gen.h"
+#include "core/io/json.h"
 #include "core/license.gen.h"
 #include "core/license.gen.h"
+#include "core/os/os.h"
 #include "core/version.h"
 #include "core/version.h"
 
 
 void Engine::set_physics_ticks_per_second(int p_ips) {
 void Engine::set_physics_ticks_per_second(int p_ips) {
@@ -307,6 +309,43 @@ Engine::Engine() {
 	singleton = this;
 	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) :
 Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr, const StringName &p_class_name) :
 		name(p_name),
 		name(p_name),
 		ptr(p_ptr),
 		ptr(p_ptr),

+ 10 - 0
core/config/engine.h

@@ -79,6 +79,11 @@ private:
 	String write_movie_path;
 	String write_movie_path;
 	String shader_cache_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:
 public:
 	static Engine *get_singleton();
 	static Engine *get_singleton();
 
 
@@ -151,6 +156,11 @@ public:
 	bool is_validation_layers_enabled() const;
 	bool is_validation_layers_enabled() const;
 	int32_t get_gpu_index() 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();
 	Engine();
 	virtual ~Engine() {}
 	virtual ~Engine() {}
 };
 };

+ 0 - 5
editor/editor_file_system.cpp

@@ -1192,11 +1192,6 @@ void EditorFileSystem::scan_changes() {
 
 
 void EditorFileSystem::_notification(int p_what) {
 void EditorFileSystem::_notification(int p_what) {
 	switch (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: {
 		case NOTIFICATION_EXIT_TREE: {
 			Thread &active_thread = thread.is_started() ? thread : thread_sources;
 			Thread &active_thread = thread.is_started() ? thread : thread_sources;
 			if (use_threads && active_thread.is_started()) {
 			if (use_threads && active_thread.is_started()) {

+ 20 - 0
editor/editor_node.cpp

@@ -661,6 +661,7 @@ void EditorNode::_notification(int p_what) {
 
 
 			command_palette->register_shortcuts_as_command();
 			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 */
 			/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
 		} break;
 		} break;
 
 
@@ -1043,6 +1044,8 @@ void EditorNode::_sources_changed(bool p_exist) {
 	if (waiting_for_first_scan) {
 	if (waiting_for_first_scan) {
 		waiting_for_first_scan = false;
 		waiting_for_first_scan = false;
 
 
+		Engine::get_singleton()->startup_benchmark_end_measure(); // editor_scan_and_reimport
+
 		// Reload the global shader variables, but this time
 		// Reload the global shader variables, but this time
 		// loading textures, as they are now properly imported.
 		// loading textures, as they are now properly imported.
 		RenderingServer::get_singleton()->global_shader_uniforms_load_settings(true);
 		RenderingServer::get_singleton()->global_shader_uniforms_load_settings(true);
@@ -1055,8 +1058,16 @@ void EditorNode::_sources_changed(bool p_exist) {
 		_load_docks();
 		_load_docks();
 
 
 		if (!defer_load_scene.is_empty()) {
 		if (!defer_load_scene.is_empty()) {
+			Engine::get_singleton()->startup_benchmark_begin_measure("editor_load_scene");
 			load_scene(defer_load_scene);
 			load_scene(defer_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;
 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) {
 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.preset = p_preset;
 	export_defer.path = p_path;
 	export_defer.path = p_path;

+ 5 - 0
editor/editor_node.h

@@ -682,6 +682,10 @@ private:
 	void _bottom_panel_switch(bool p_enable, int p_idx);
 	void _bottom_panel_switch(bool p_enable, int p_idx);
 	void _bottom_panel_raise_toggled(bool);
 	void _bottom_panel_raise_toggled(bool);
 
 
+	void _begin_first_scan();
+	bool use_startup_benchmark = false;
+	String startup_benchmark_file;
+
 protected:
 protected:
 	friend class FileSystemDock;
 	friend class FileSystemDock;
 
 
@@ -816,6 +820,7 @@ public:
 
 
 	void _copy_warning(const String &p_str);
 	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);
 	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; }
 	Control *get_gui_base() { return gui_base; }

+ 50 - 0
main/main.cpp

@@ -154,6 +154,8 @@ static OS::ProcessID editor_pid = 0;
 static bool auto_build_solutions = false;
 static bool auto_build_solutions = false;
 static String debug_server_uri;
 static String debug_server_uri;
 #endif
 #endif
+bool use_startup_benchmark = false;
+String startup_benchmark_file;
 
 
 // Display
 // 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("  --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("  --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("  --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
 #ifdef TESTS_ENABLED
 	OS::get_singleton()->print("  --test [--help]                              Run unit tests. Use --test --help for more information.\n");
 	OS::get_singleton()->print("  --test [--help]                              Run unit tests. Use --test --help for more information.\n");
 #endif
 #endif
@@ -594,6 +598,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	engine = memnew(Engine);
 	engine = memnew(Engine);
 
 
 	MAIN_PRINT("Main: Initialize CORE");
 	MAIN_PRINT("Main: Initialize CORE");
+	engine->startup_begin();
+	engine->startup_benchmark_begin_measure("core");
 
 
 	register_core_types();
 	register_core_types();
 	register_core_driver_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");
 				OS::get_singleton()->print("Missing --xr-mode argument, aborting.\n");
 				goto error;
 				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() == "--") {
 		} else if (I->get() == "--") {
 			adding_user_args = true;
 			adding_user_args = true;
 		} else {
 		} else {
@@ -1624,6 +1643,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 
 
 	message_queue = memnew(MessageQueue);
 	message_queue = memnew(MessageQueue);
 
 
+	engine->startup_benchmark_end_measure(); // core
+
 	if (p_second_phase) {
 	if (p_second_phase) {
 		return setup2();
 		return setup2();
 	}
 	}
@@ -1690,6 +1711,8 @@ error:
 }
 }
 
 
 Error Main::setup2(Thread::ID p_main_tid_override) {
 Error Main::setup2(Thread::ID p_main_tid_override) {
+	engine->startup_benchmark_begin_measure("servers");
+
 	tsman = memnew(TextServerManager);
 	tsman = memnew(TextServerManager);
 
 
 	if (tsman) {
 	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.");
 		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");
 	MAIN_PRINT("Main: Load Scene Types");
 
 
+	engine->startup_benchmark_begin_measure("scene");
+
 	register_scene_types();
 	register_scene_types();
 	register_driver_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)));
 	print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR)));
 	MAIN_PRINT("Main: Done");
 	MAIN_PRINT("Main: Done");
 
 
+	engine->startup_benchmark_end_measure(); // scene
+
 	return OK;
 	return OK;
 }
 }
 
 
@@ -2450,6 +2479,7 @@ bool Main::start() {
 		if (!project_manager && !editor) { // game
 		if (!project_manager && !editor) { // game
 			if (!game_path.is_empty() || !script.is_empty()) {
 			if (!game_path.is_empty() || !script.is_empty()) {
 				//autoload
 				//autoload
+				Engine::get_singleton()->startup_benchmark_begin_measure("load_autoloads");
 				HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
 				HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
 
 
 				//first pass, add the constants so they exist before any script is loaded
 				//first pass, add the constants so they exist before any script is loaded
@@ -2504,12 +2534,14 @@ bool Main::start() {
 				for (Node *E : to_add) {
 				for (Node *E : to_add) {
 					sml->get_root()->add_child(E);
 					sml->get_root()->add_child(E);
 				}
 				}
+				Engine::get_singleton()->startup_benchmark_end_measure(); // load autoloads
 			}
 			}
 		}
 		}
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 		EditorNode *editor_node = nullptr;
 		EditorNode *editor_node = nullptr;
 		if (editor) {
 		if (editor) {
+			Engine::get_singleton()->startup_benchmark_begin_measure("editor");
 			editor_node = memnew(EditorNode);
 			editor_node = memnew(EditorNode);
 			sml->get_root()->add_child(editor_node);
 			sml->get_root()->add_child(editor_node);
 
 
@@ -2517,6 +2549,13 @@ bool Main::start() {
 				editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only);
 				editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only);
 				game_path = ""; // Do not load anything.
 				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
 #endif
 
 
@@ -2681,6 +2720,8 @@ bool Main::start() {
 
 
 		if (!project_manager && !editor) { // game
 		if (!project_manager && !editor) { // game
 
 
+			Engine::get_singleton()->startup_benchmark_begin_measure("game_load");
+
 			// Load SSL Certificates from Project Settings (or builtin).
 			// Load SSL Certificates from Project Settings (or builtin).
 			Crypto::load_default_certificates(GLOBAL_DEF("network/ssl/certificate_bundle_override", ""));
 			Crypto::load_default_certificates(GLOBAL_DEF("network/ssl/certificate_bundle_override", ""));
 
 
@@ -2720,16 +2761,20 @@ bool Main::start() {
 					}
 					}
 				}
 				}
 			}
 			}
+
+			Engine::get_singleton()->startup_benchmark_end_measure(); // game_load
 		}
 		}
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 		if (project_manager) {
 		if (project_manager) {
+			Engine::get_singleton()->startup_benchmark_begin_measure("project_manager");
 			Engine::get_singleton()->set_editor_hint(true);
 			Engine::get_singleton()->set_editor_hint(true);
 			ProjectManager *pmanager = memnew(ProjectManager);
 			ProjectManager *pmanager = memnew(ProjectManager);
 			ProgressDialog *progress_dialog = memnew(ProgressDialog);
 			ProgressDialog *progress_dialog = memnew(ProgressDialog);
 			pmanager->add_child(progress_dialog);
 			pmanager->add_child(progress_dialog);
 			sml->get_root()->add_child(pmanager);
 			sml->get_root()->add_child(pmanager);
 			DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_PROJECTMAN);
 			DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_PROJECTMAN);
+			Engine::get_singleton()->startup_benchmark_end_measure();
 		}
 		}
 
 
 		if (project_manager || editor) {
 		if (project_manager || editor) {
@@ -2759,6 +2804,11 @@ bool Main::start() {
 		}
 		}
 	}
 	}
 
 
+	if (use_startup_benchmark) {
+		Engine::get_singleton()->startup_dump(startup_benchmark_file);
+		startup_benchmark_file = String();
+	}
+
 	return true;
 	return true;
 }
 }