diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/main.cpp | 537 | ||||
-rw-r--r-- | main/main.h | 2 | ||||
-rw-r--r-- | main/main_builders.py | 5 | ||||
-rw-r--r-- | main/main_timer_sync.cpp | 79 | ||||
-rw-r--r-- | main/main_timer_sync.h | 22 | ||||
-rw-r--r-- | main/performance.cpp | 64 | ||||
-rw-r--r-- | main/performance.h | 24 |
7 files changed, 425 insertions, 308 deletions
diff --git a/main/main.cpp b/main/main.cpp index 884caab1e9..5a2a088494 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -34,8 +34,10 @@ #include "core/core_string_names.h" #include "core/crypto/crypto.h" #include "core/debugger/engine_debugger.h" +#include "core/extension/extension_api_dump.h" #include "core/input/input.h" #include "core/input/input_map.h" +#include "core/io/dir_access.h" #include "core/io/file_access_network.h" #include "core/io/file_access_pack.h" #include "core/io/file_access_zip.h" @@ -43,8 +45,8 @@ #include "core/io/ip.h" #include "core/io/resource_loader.h" #include "core/object/message_queue.h" -#include "core/os/dir_access.h" #include "core/os/os.h" +#include "core/os/time.h" #include "core/register_core_types.h" #include "core/string/translation.h" #include "core/version.h" @@ -101,6 +103,7 @@ static InputMap *input_map = nullptr; static TranslationServer *translation_server = nullptr; static Performance *performance = nullptr; static PackedData *packed_data = nullptr; +static Time *time_singleton = nullptr; #ifdef MINIZIP_ENABLED static ZipArchive *zip_packed_data = nullptr; #endif @@ -132,24 +135,25 @@ static int audio_driver_idx = -1; // Engine config/tools -static bool single_window = false; static bool editor = false; static bool project_manager = false; +static bool cmdline_tool = false; static String locale; static bool show_help = false; static bool auto_quit = false; static OS::ProcessID allow_focus_steal_pid = 0; #ifdef TOOLS_ENABLED static bool auto_build_solutions = false; +static String debug_server_uri; #endif // Display static DisplayServer::WindowMode window_mode = DisplayServer::WINDOW_MODE_WINDOWED; static DisplayServer::ScreenOrientation window_orientation = DisplayServer::SCREEN_LANDSCAPE; +static DisplayServer::VSyncMode window_vsync_mode = DisplayServer::VSYNC_ENABLED; static uint32_t window_flags = 0; static Size2i window_size = Size2i(1024, 600); -static bool window_vsync_via_compositor = false; static int init_screen = -1; static bool init_fullscreen = false; @@ -171,7 +175,9 @@ static int frame_delay = 0; static bool disable_render_loop = false; static int fixed_fps = -1; static bool print_fps = false; - +#ifdef TOOLS_ENABLED +static bool dump_extension_api = false; +#endif bool profile_gpu = false; /* Helper methods */ @@ -183,6 +189,10 @@ bool Main::is_project_manager() { return project_manager; } +bool Main::is_cmdline_tool() { + return cmdline_tool; +} + static String unescape_cmdline(const String &p_str) { return p_str.replace("%20", " "); } @@ -276,6 +286,7 @@ void Main::print_help(const char *p_binary) { #ifdef TOOLS_ENABLED OS::get_singleton()->print(" -e, --editor Start the editor instead of running the scene.\n"); OS::get_singleton()->print(" -p, --project-manager Start the project manager, even if a project is auto-detected.\n"); + OS::get_singleton()->print(" --debug-server <uri> Start the editor debug server (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007)\n"); #endif OS::get_singleton()->print(" -q, --quit Quit after the first iteration.\n"); OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n"); @@ -316,9 +327,10 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping)\n"); + OS::get_singleton()->print(" --headless Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.\n"); + OS::get_singleton()->print("\n"); -#ifndef SERVER_ENABLED OS::get_singleton()->print("Display options:\n"); OS::get_singleton()->print(" -f, --fullscreen Request fullscreen mode.\n"); OS::get_singleton()->print(" -m, --maximized Request a maximized window.\n"); @@ -327,13 +339,9 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --resolution <W>x<H> Request window resolution.\n"); OS::get_singleton()->print(" --position <X>,<Y> Request window position.\n"); OS::get_singleton()->print(" --low-dpi Force low-DPI mode (macOS and Windows only).\n"); - OS::get_singleton()->print(" --no-window Disable window creation (Windows only). Useful together with --script.\n"); - OS::get_singleton()->print(" --enable-vsync-via-compositor When vsync is enabled, vsync via the OS' window compositor (Windows only).\n"); - OS::get_singleton()->print(" --disable-vsync-via-compositor Disable vsync via the OS' window compositor (Windows only).\n"); OS::get_singleton()->print(" --single-window Use a single window (no separate subwindows).\n"); OS::get_singleton()->print(" --tablet-driver Pen tablet input driver.\n"); OS::get_singleton()->print("\n"); -#endif OS::get_singleton()->print("Debug options:\n"); OS::get_singleton()->print(" -d, --debug Debug (local stdout debugger).\n"); @@ -344,7 +352,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --gpu-abort Abort on GPU errors (usually validation layer errors), may help see the problem if your system freezes.\n"); #endif OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n"); -#if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED) +#if defined(DEBUG_ENABLED) OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n"); OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n"); #endif @@ -365,7 +373,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" <path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe'). The target directory should exist.\n"); OS::get_singleton()->print(" --export-debug <preset> <path> Same as --export, but using the debug template.\n"); OS::get_singleton()->print(" --export-pack <preset> <path> Same as --export, but only export the game pack for the given preset. The <path> extension determines whether it will be in PCK or ZIP format.\n"); - OS::get_singleton()->print(" --doctool <path> Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n"); + OS::get_singleton()->print(" --doctool [<path>] Dump the engine API reference to the given <path> (defaults to current dir) in XML format, merging if existing files are found.\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"); #ifdef DEBUG_METHODS_ENABLED @@ -375,8 +383,8 @@ void Main::print_help(const char *p_binary) { #ifdef TESTS_ENABLED OS::get_singleton()->print(" --test [--help] Run unit tests. Use --test --help for more information.\n"); #endif - OS::get_singleton()->print("\n"); #endif + OS::get_singleton()->print("\n"); } #ifdef TESTS_ENABLED @@ -390,12 +398,18 @@ Error Main::test_setup() { register_core_types(); register_core_driver_types(); + packed_data = memnew(PackedData); + globals = memnew(ProjectSettings); GLOBAL_DEF("debug/settings/crash_handler/message", String("Please include this when reporting the bug on https://github.com/godotengine/godot/issues")); + GLOBAL_DEF_RST("rendering/occlusion_culling/bvh_build_quality", 2); translation_server = memnew(TranslationServer); + tsman = memnew(TextServerManager); + + register_core_extensions(); // From `Main::setup2()`. preregister_module_types(); @@ -427,6 +441,9 @@ Error Main::test_setup() { register_module_types(); register_driver_types(); + ERR_FAIL_COND_V(TextServerManager::get_singleton()->get_interface_count() == 0, ERR_CANT_CREATE); + TextServerManager::get_singleton()->set_primary_interface(TextServerManager::get_singleton()->get_interface(0)); + ClassDB::set_current_api(ClassDB::API_NONE); _start_success = true; @@ -442,10 +459,11 @@ void Main::test_cleanup() { ResourceLoader::remove_custom_loaders(); ResourceSaver::remove_custom_savers(); + unregister_driver_types(); #ifdef TOOLS_ENABLED EditorNode::unregister_editor_types(); #endif - unregister_driver_types(); + unregister_module_types(); unregister_platform_apis(); unregister_scene_types(); @@ -456,9 +474,15 @@ void Main::test_cleanup() { if (translation_server) { memdelete(translation_server); } + if (tsman) { + memdelete(tsman); + } if (globals) { memdelete(globals); } + if (packed_data) { + memdelete(packed_data); + } if (engine) { memdelete(engine); } @@ -527,13 +551,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph MAIN_PRINT("Main: Initialize Globals"); input_map = memnew(InputMap); + time_singleton = memnew(Time); globals = memnew(ProjectSettings); - register_core_settings(); //here globals is present + register_core_settings(); //here globals are present translation_server = memnew(TranslationServer); performance = memnew(Performance); - ClassDB::register_class<Performance>(); + GDREGISTER_CLASS(Performance); engine->add_singleton(Engine::Singleton("Performance", performance)); // Only flush stdout in debug builds by default, as spamming `print()` will @@ -556,8 +581,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph List<String>::Element *I = args.front(); - I = args.front(); - while (I) { I->get() = unescape_cmdline(I->get().strip_edges()); I = I->next(); @@ -581,11 +604,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Vector<String> breakpoints; bool use_custom_res = true; bool force_res = false; - bool saw_vsync_via_compositor_override = false; #ifdef TOOLS_ENABLED bool found_project = false; #endif - bool use_vsync = false; packed_data = PackedData::get_singleton(); if (!packed_data) { @@ -714,7 +735,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->print("Missing video driver argument, aborting.\n"); goto error; } -#ifndef SERVER_ENABLED } else if (I->get() == "-f" || I->get() == "--fullscreen") { // force fullscreen init_fullscreen = true; @@ -742,7 +762,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } } else if (I->get() == "--single-window") { // force single window - single_window = true; + OS::get_singleton()->_single_window = true; } else if (I->get() == "-t" || I->get() == "--always-on-top") { // force always-on-top window init_always_on_top = true; @@ -804,16 +824,11 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "--low-dpi") { // force low DPI (macOS only) force_lowdpi = true; - } else if (I->get() == "--no-window") { // disable window creation (Windows only) - - OS::get_singleton()->set_no_window_mode(true); - } else if (I->get() == "--enable-vsync-via-compositor") { - window_vsync_via_compositor = true; - saw_vsync_via_compositor_override = true; - } else if (I->get() == "--disable-vsync-via-compositor") { - window_vsync_via_compositor = false; - saw_vsync_via_compositor_override = true; -#endif + } else if (I->get() == "--headless") { // enable headless mode (no audio, no rendering). + + audio_driver = "Dummy"; + display_driver = "headless"; + } else if (I->get() == "--profiling") { // enable profiling use_debug_profiler = true; @@ -869,22 +884,47 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "-p" || I->get() == "--project-manager") { // starts project manager project_manager = true; + } else if (I->get() == "--debug-server") { + if (I->next()) { + debug_server_uri = I->next()->get(); + if (debug_server_uri.find("://") == -1) { // wrong address + OS::get_singleton()->print("Invalid debug server uri. It should be of the form <protocol>://<bind_address>:<port>.\n"); + goto error; + } + N = I->next()->next(); + } else { + OS::get_singleton()->print("Missing remote debug server uri, aborting.\n"); + goto error; + } } else if (I->get() == "--build-solutions") { // Build the scripting solution such C# auto_build_solutions = true; editor = true; -#ifdef DEBUG_METHODS_ENABLED + cmdline_tool = true; + } else if (I->get() == "--gdnative-generate-json-api" || I->get() == "--gdnative-generate-json-builtin-api") { // Register as an editor instance to use low-end fallback if relevant. editor = true; + cmdline_tool = true; // We still pass it to the main arguments since the argument handling itself is not done in this function main_args.push_back(I->get()); -#endif + } else if (I->get() == "--dump-extension-api") { + // Register as an editor instance to use low-end fallback if relevant. + editor = true; + cmdline_tool = true; + dump_extension_api = true; + print_line("dump extension?"); + main_args.push_back(I->get()); } else if (I->get() == "--export" || I->get() == "--export-debug" || I->get() == "--export-pack") { // Export project - + // Actually handling is done in start(). editor = true; + cmdline_tool = true; + main_args.push_back(I->get()); + } else if (I->get() == "--doctool") { + // Actually handling is done in start(). + cmdline_tool = true; main_args.push_back(I->get()); #endif } else if (I->get() == "--path") { // set path of project to start or edit @@ -965,11 +1005,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "-d" || I->get() == "--debug") { debug_uri = "local://"; OS::get_singleton()->_debug_stdout = true; -#if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED) +#if defined(DEBUG_ENABLED) } else if (I->get() == "--debug-collisions") { debug_collisions = true; } else if (I->get() == "--debug-navigation") { debug_navigation = true; + } else if (I->get() == "--debug-stringnames") { + StringName::set_debug_stringnames(true); #endif } else if (I->get() == "--remote-debug") { if (I->next()) { @@ -1056,8 +1098,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph editor = false; #else const String error_msg = "Error: Couldn't load project data at path \"" + project_path + "\". Is the .pck file missing?\nIf you've renamed the executable, the associated .pck file should also be renamed to match the executable's name (without the extension).\n"; - OS::get_singleton()->print("%s", error_msg.ascii().get_data()); - DisplayServer::get_singleton()->alert(error_msg); + OS::get_singleton()->print("%s", error_msg.utf8().get_data()); + OS::get_singleton()->alert(error_msg); goto error; #endif @@ -1066,6 +1108,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // Initialize user data dir. OS::get_singleton()->ensure_user_data_dir(); + ResourceUID::get_singleton()->load_from_cache(); // load UUIDs from cache. + GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60); ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc", PropertyInfo(Variant::INT, @@ -1117,8 +1161,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } if (!project_manager && !editor) { - // Determine if the project manager should be requested - project_manager = main_args.size() == 0 && !found_project; + // If we didn't find a project, we fall back to the project manager. + project_manager = !found_project && !cmdline_tool; } #endif @@ -1148,7 +1192,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #ifdef TOOLS_ENABLED if (!editor && !project_manager) { #endif - OS::get_singleton()->print("Error: Can't run project: no main scene defined.\n"); + const String error_msg = "Error: Can't run project: no main scene defined in the project.\n"; + OS::get_singleton()->print("%s", error_msg.utf8().get_data()); + OS::get_singleton()->alert(error_msg); goto error; #ifdef TOOLS_ENABLED } @@ -1178,6 +1224,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->set_cmdline(execpath, main_args); + register_core_extensions(); //before display + GLOBAL_DEF("rendering/driver/driver_name", "Vulkan"); ProjectSettings::get_singleton()->set_custom_property_info("rendering/driver/driver_name", PropertyInfo(Variant::STRING, @@ -1248,24 +1296,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } GLOBAL_DEF("internationalization/rendering/force_right_to_left_layout_direction", false); + GLOBAL_DEF("internationalization/locale/include_text_server_data", false); if (!force_lowdpi) { OS::get_singleton()->_allow_hidpi = GLOBAL_DEF("display/window/dpi/allow_hidpi", false); } - use_vsync = GLOBAL_DEF_RST("display/window/vsync/use_vsync", true); - OS::get_singleton()->_use_vsync = use_vsync; - - if (!saw_vsync_via_compositor_override) { - // If one of the command line options to enable/disable vsync via the - // window compositor ("--enable-vsync-via-compositor" or - // "--disable-vsync-via-compositor") was present then it overrides the - // project setting. - window_vsync_via_compositor = GLOBAL_DEF("display/window/vsync/vsync_via_compositor", false); - } - - OS::get_singleton()->_vsync_via_compositor = window_vsync_via_compositor; - /* todo restore OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/per_pixel_transparency/allowed", false); video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency/enabled", false); @@ -1321,37 +1357,24 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } { - String orientation = GLOBAL_DEF("display/window/handheld/orientation", "landscape"); - - if (orientation == "portrait") { - window_orientation = DisplayServer::SCREEN_PORTRAIT; - } else if (orientation == "reverse_landscape") { - window_orientation = DisplayServer::SCREEN_REVERSE_LANDSCAPE; - } else if (orientation == "reverse_portrait") { - window_orientation = DisplayServer::SCREEN_REVERSE_PORTRAIT; - } else if (orientation == "sensor_landscape") { - window_orientation = DisplayServer::SCREEN_SENSOR_LANDSCAPE; - } else if (orientation == "sensor_portrait") { - window_orientation = DisplayServer::SCREEN_SENSOR_PORTRAIT; - } else if (orientation == "sensor") { - window_orientation = DisplayServer::SCREEN_SENSOR; - } else { - window_orientation = DisplayServer::SCREEN_LANDSCAPE; - } + window_orientation = DisplayServer::ScreenOrientation(int(GLOBAL_DEF_BASIC("display/window/handheld/orientation", DisplayServer::ScreenOrientation::SCREEN_LANDSCAPE))); } - - Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF_BASIC("physics/common/physics_fps", 60)); - ProjectSettings::get_singleton()->set_custom_property_info("physics/common/physics_fps", - PropertyInfo(Variant::INT, "physics/common/physics_fps", - PROPERTY_HINT_RANGE, "1,120,1,or_greater")); + { + window_vsync_mode = DisplayServer::VSyncMode(int(GLOBAL_DEF("display/window/vsync/vsync_mode", DisplayServer::VSyncMode::VSYNC_ENABLED))); + } + Engine::get_singleton()->set_physics_ticks_per_second(GLOBAL_DEF_BASIC("physics/common/physics_ticks_per_second", 60)); + ProjectSettings::get_singleton()->set_custom_property_info("physics/common/physics_ticks_per_second", + PropertyInfo(Variant::INT, "physics/common/physics_ticks_per_second", + PROPERTY_HINT_RANGE, "1,1000,1")); Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0)); ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps", PropertyInfo(Variant::INT, "debug/settings/fps/force_fps", - PROPERTY_HINT_RANGE, "0,120,1,or_greater")); + PROPERTY_HINT_RANGE, "0,1000,1")); GLOBAL_DEF("debug/settings/stdout/print_fps", false); + GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false); GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false); if (!OS::get_singleton()->_verbose_stdout) { // Not manually overridden. @@ -1412,6 +1435,9 @@ error: if (input_map) { memdelete(input_map); } + if (time_singleton) { + memdelete(time_singleton); + } if (translation_server) { memdelete(translation_server); } @@ -1443,6 +1469,8 @@ error: } Error Main::setup2(Thread::ID p_main_tid_override) { + tsman = memnew(TextServerManager); + preregister_module_types(); preregister_server_types(); @@ -1455,82 +1483,30 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } #endif - /* Determine text driver */ - - if (text_driver == "") { - text_driver = GLOBAL_GET("internationalization/rendering/text_driver"); - } - - if (text_driver != "") { - /* Load user selected text server. */ - for (int i = 0; i < TextServerManager::get_interface_count(); i++) { - if (text_driver == TextServerManager::get_interface_name(i)) { - text_driver_idx = i; - break; - } - } - } - - if (text_driver_idx < 0) { - /* If not selected, use one with the most features available. */ - int max_features = 0; - for (int i = 0; i < TextServerManager::get_interface_count(); i++) { - uint32_t ftrs = TextServerManager::get_interface_features(i); - int features = 0; - while (ftrs) { - features += ftrs & 1; - ftrs >>= 1; - } - if (features >= max_features) { - max_features = features; - text_driver_idx = i; - } - } - } - print_verbose("Using \"" + TextServerManager::get_interface_name(text_driver_idx) + "\" text server..."); - - /* Initialize Text Server */ - - { - tsman = memnew(TextServerManager); - Error err; - TextServer *text_server = TextServerManager::initialize(text_driver_idx, err); - if (err != OK || text_server == nullptr) { - for (int i = 0; i < TextServerManager::get_interface_count(); i++) { - if (i == text_driver_idx) { - continue; //don't try the same twice - } - text_server = TextServerManager::initialize(i, err); - if (err == OK && text_server != nullptr) { - break; - } - } - } - - if (err != OK || text_server == nullptr) { - ERR_PRINT("Unable to create TextServer, all text drivers failed."); - return err; - } +#ifdef TOOLS_ENABLED + if (editor || project_manager || cmdline_tool) { + EditorPaths::create(); } +#endif /* Initialize Input */ input = memnew(Input); - /* Iniitalize Display Server */ + /* Initialize Display Server */ { String rendering_driver; // temp broken Error err; - display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_flags, window_size, err); + display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_size, err); if (err != OK || display_server == nullptr) { //ok i guess we can't use this display server, try other ones for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { if (i == display_driver_idx) { continue; //don't try the same twice } - display_server = DisplayServer::create(i, rendering_driver, window_mode, window_flags, window_size, err); + display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_size, err); if (err == OK && display_server != nullptr) { break; } @@ -1547,7 +1523,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { display_server->screen_set_orientation(window_orientation); } - /* Initialize Pen Table Driver */ + /* Initialize Pen Tablet Driver */ { GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", ""); @@ -1575,17 +1551,24 @@ Error Main::setup2(Thread::ID p_main_tid_override) { print_verbose("Using \"" + tablet_driver + "\" pen tablet driver..."); - /* Initialize Visual Server */ + /* Initialize Rendering Server */ rendering_server = memnew(RenderingServerDefault(OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD)); rendering_server->init(); rendering_server->set_render_loop_enabled(!disable_render_loop); - if (profile_gpu) { + if (profile_gpu || (!editor && bool(GLOBAL_GET("debug/settings/stdout/print_gpu_profile")))) { rendering_server->set_print_gpu_profile(true); } +#ifdef UNIX_ENABLED + // Print warning after initializing the renderer but before initializing audio. + if (OS::get_singleton()->get_environment("USER") == "root" && !OS::get_singleton()->has_environment("GODOT_SILENCE_ROOT_WARNING")) { + WARN_PRINT("Started the engine as `root`/superuser. This is a security risk, and subsystems like audio may not work correctly.\nSet the environment variable `GODOT_SILENCE_ROOT_WARNING` to 1 to silence this warning."); + } +#endif + OS::get_singleton()->initialize_joypads(); /* Initialize Audio Driver */ @@ -1655,14 +1638,20 @@ Error Main::setup2(Thread::ID p_main_tid_override) { boot_logo_path = boot_logo_path.strip_edges(); if (boot_logo_path != String()) { - boot_logo.instance(); + boot_logo.instantiate(); Error load_err = ImageLoader::load_image(boot_logo_path, boot_logo); if (load_err) { ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + "'. Loading default splash."); } } - Color boot_bg_color = GLOBAL_DEF("application/boot_splash/bg_color", boot_splash_bg_color); +#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH) + const Color boot_bg_color = + GLOBAL_DEF("application/boot_splash/bg_color", + (editor || project_manager) ? boot_splash_editor_bg_color : boot_splash_bg_color); +#else + const Color boot_bg_color = GLOBAL_DEF("application/boot_splash/bg_color", boot_splash_bg_color); +#endif if (boot_logo.is_valid()) { RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale, boot_logo_filter); @@ -1684,8 +1673,10 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } #ifdef TOOLS_ENABLED - Ref<Image> icon = memnew(Image(app_icon_png)); - DisplayServer::get_singleton()->set_icon(icon); + if (OS::get_singleton()->get_bundle_icon_path().is_empty()) { + Ref<Image> icon = memnew(Image(app_icon_png)); + DisplayServer::get_singleton()->set_icon(icon); + } #endif } @@ -1712,6 +1703,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) { Input *id = Input::get_singleton(); if (id) { + agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false); + if (bool(GLOBAL_DEF("input_devices/pointing/emulate_touch_from_mouse", false)) && !(editor || project_manager)) { bool found_touchscreen = false; @@ -1740,10 +1733,74 @@ Error Main::setup2(Thread::ID p_main_tid_override) { ResourceLoader::load_path_remaps(); + MAIN_PRINT("Main: Load TextServer"); + + /* Enum text drivers */ + GLOBAL_DEF("internationalization/rendering/text_driver", ""); + String text_driver_options; + for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) { + if (i > 0) { + text_driver_options += ","; + } + text_driver_options += TextServerManager::get_singleton()->get_interface(i)->get_name(); + } + ProjectSettings::get_singleton()->set_custom_property_info("internationalization/rendering/text_driver", PropertyInfo(Variant::STRING, "internationalization/rendering/text_driver", PROPERTY_HINT_ENUM, text_driver_options)); + + /* Determine text driver */ + if (text_driver == "") { + text_driver = GLOBAL_GET("internationalization/rendering/text_driver"); + } + + if (text_driver != "") { + /* Load user selected text server. */ + for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) { + if (TextServerManager::get_singleton()->get_interface(i)->get_name() == text_driver) { + text_driver_idx = i; + break; + } + } + } + + if (text_driver_idx < 0) { + /* If not selected, use one with the most features available. */ + int max_features = 0; + for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) { + uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features(); + int feature_number = 0; + while (features) { + feature_number += features & 1; + features >>= 1; + } + if (feature_number >= max_features) { + max_features = feature_number; + text_driver_idx = i; + } + } + } + if (text_driver_idx >= 0) { + TextServerManager::get_singleton()->set_primary_interface(TextServerManager::get_singleton()->get_interface(text_driver_idx)); + } else { + ERR_PRINT("TextServer: Unable to create TextServer interface."); + return ERR_CANT_CREATE; + } + MAIN_PRINT("Main: Load Scene Types"); register_scene_types(); +#ifdef TOOLS_ENABLED + ClassDB::set_current_api(ClassDB::API_EDITOR); + EditorNode::register_editor_types(); + + ClassDB::set_current_api(ClassDB::API_CORE); + +#endif + + MAIN_PRINT("Main: Load Modules"); + + register_platform_apis(); + register_module_types(); + GLOBAL_DEF("display/mouse_cursor/custom_image", String()); GLOBAL_DEF("display/mouse_cursor/custom_image_hotspot", Vector2()); GLOBAL_DEF("display/mouse_cursor/tooltip_position_offset", Point2(10, 10)); @@ -1760,21 +1817,11 @@ Error Main::setup2(Thread::ID p_main_tid_override) { Input::get_singleton()->set_custom_mouse_cursor(cursor, Input::CURSOR_ARROW, hotspot); } } -#ifdef TOOLS_ENABLED - ClassDB::set_current_api(ClassDB::API_EDITOR); - EditorNode::register_editor_types(); - - ClassDB::set_current_api(ClassDB::API_CORE); - -#endif - - MAIN_PRINT("Main: Load Modules, Physics, Drivers, Scripts"); - - register_platform_apis(); - register_module_types(); camera_server = CameraServer::create(); + MAIN_PRINT("Main: Load Physics, Drivers, Scripts"); + initialize_physics(); initialize_navigation_server(); register_server_singletons(); @@ -1795,7 +1842,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { if (!project_manager) { // If not running the project manager, and now that the engine is // able to load resources, load the global shader variables. - // If running on editor, dont load the textures because the editor + // If running on editor, don't load the textures because the editor // may want to import them first. Editor will reload those later. rendering_server->global_variables_load_settings(!editor); } @@ -1803,7 +1850,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { _start_success = true; locale = String(); - ClassDB::set_current_api(ClassDB::API_NONE); //no more api is registered at this point + ClassDB::set_current_api(ClassDB::API_NONE); //no more APIs are registered at this point print_verbose("CORE API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_CORE))); print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR))); @@ -1819,14 +1866,13 @@ bool Main::start() { ERR_FAIL_COND_V(!_start_success, false); bool hasicon = false; - String doc_tool; - List<String> removal_docs; String positional_arg; String game_path; String script; bool check_only = false; #ifdef TOOLS_ENABLED + String doc_tool_path; bool doc_base = true; String _export_preset; bool export_debug = false; @@ -1836,8 +1882,9 @@ bool Main::start() { main_timer_sync.init(OS::get_singleton()->get_ticks_usec()); List<String> args = OS::get_singleton()->get_cmdline_args(); - // parameters that do not have an argument to the right for (int i = 0; i < args.size(); i++) { + // First check parameters that do not have an argument to the right. + // Doctest Unit Testing Handler // Designed to override and pass arguments to the unit test handler. if (args[i] == "--check-only") { @@ -1867,16 +1914,18 @@ bool Main::start() { game_path = args[i]; } } - //parameters that have an argument to the right + // Then parameters that have an argument to the right. else if (i < (args.size() - 1)) { bool parsed_pair = true; if (args[i] == "-s" || args[i] == "--script") { script = args[i + 1]; #ifdef TOOLS_ENABLED } else if (args[i] == "--doctool") { - doc_tool = args[i + 1]; - for (int j = i + 2; j < args.size(); j++) { - removal_docs.push_back(args[j]); + doc_tool_path = args[i + 1]; + if (doc_tool_path.begins_with("-")) { + // Assuming other command line arg, so default to cwd. + doc_tool_path = "."; + parsed_pair = false; } } else if (args[i] == "--export") { editor = true; //needs editor @@ -1898,15 +1947,21 @@ bool Main::start() { i++; } } +#ifdef TOOLS_ENABLED + // Handle case where no path is given to --doctool. + else if (args[i] == "--doctool") { + doc_tool_path = "."; + } +#endif } #ifdef TOOLS_ENABLED - if (doc_tool != "") { - Engine::get_singleton()->set_editor_hint( - true); // Needed to instance editor-only classes for their default values + if (doc_tool_path != "") { + // Needed to instance editor-only classes for their default values + Engine::get_singleton()->set_editor_hint(true); { - DirAccessRef da = DirAccess::open(doc_tool); + DirAccessRef da = DirAccess::open(doc_tool_path); ERR_FAIL_COND_V_MSG(!da, false, "Argument supplied to --doctool must be a valid directory path."); } @@ -1935,8 +1990,8 @@ bool Main::start() { for (int i = 0; i < _doc_data_class_path_count; i++) { // Custom modules are always located by absolute path. String path = _doc_data_class_paths[i].path; - if (path.is_rel_path()) { - path = doc_tool.plus_file(path); + if (path.is_relative_path()) { + path = doc_tool_path.plus_file(path); } String name = _doc_data_class_paths[i].name; doc_data_classes[name] = path; @@ -1953,7 +2008,7 @@ bool Main::start() { } } - String index_path = doc_tool.plus_file("doc/classes"); + String index_path = doc_tool_path.plus_file("doc/classes"); // Create the main documentation directory if it doesn't exist DirAccess *da = DirAccess::create_for_path(index_path); da->make_dir_recursive(index_path); @@ -1976,16 +2031,31 @@ bool Main::start() { return false; } + if (dump_extension_api) { + NativeExtensionAPIDump::generate_extension_json_file("extension_api.json"); + return false; + } #endif if (script == "" && game_path == "" && String(GLOBAL_GET("application/run/main_scene")) != "") { game_path = GLOBAL_GET("application/run/main_scene"); } +#ifdef TOOLS_ENABLED + if (!editor && !project_manager && !cmdline_tool && script == "" && game_path == "") { + // If we end up here, it means we didn't manage to detect what we want to run. + // Let's throw an error gently. The code leading to this is pretty brittle so + // this might end up triggered by valid usage, in which case we'll have to + // fine-tune further. + OS::get_singleton()->alert("Couldn't detect whether to run the editor, the project manager or a specific project. Aborting."); + ERR_FAIL_V_MSG(false, "Couldn't detect whether to run the editor, the project manager or a specific project. Aborting."); + } +#endif + MainLoop *main_loop = nullptr; if (editor) { main_loop = memnew(SceneTree); - }; + } String main_loop_type = GLOBAL_DEF("application/run/main_loop_type", "SceneTree"); if (script != "") { @@ -1999,17 +2069,16 @@ bool Main::start() { return false; } - if (script_res->can_instance()) { + if (script_res->can_instantiate()) { StringName instance_type = script_res->get_instance_base_type(); - Object *obj = ClassDB::instance(instance_type); + Object *obj = ClassDB::instantiate(instance_type); MainLoop *script_loop = Object::cast_to<MainLoop>(obj); if (!script_loop) { if (obj) { memdelete(obj); } - ERR_FAIL_V_MSG(false, - vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", - script)); + OS::get_singleton()->alert(vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", script)); + ERR_FAIL_V_MSG(false, vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", script)); } script_loop->set_initialize_script(script_res); @@ -2022,13 +2091,13 @@ bool Main::start() { String script_path = ScriptServer::get_global_class_path(main_loop_type); Ref<Script> script_res = ResourceLoader::load(script_path); StringName script_base = ScriptServer::get_global_class_native_base(main_loop_type); - Object *obj = ClassDB::instance(script_base); + Object *obj = ClassDB::instantiate(script_base); MainLoop *script_loop = Object::cast_to<MainLoop>(obj); if (!script_loop) { if (obj) { memdelete(obj); } - DisplayServer::get_singleton()->alert("Error: Invalid MainLoop script base type: " + script_base); + OS::get_singleton()->alert("Error: Invalid MainLoop script base type: " + script_base); ERR_FAIL_V_MSG(false, vformat("The global class %s does not inherit from SceneTree or MainLoop.", main_loop_type)); } script_loop->set_initialize_script(script_res); @@ -2042,10 +2111,10 @@ bool Main::start() { if (!main_loop) { if (!ClassDB::class_exists(main_loop_type)) { - DisplayServer::get_singleton()->alert("Error: MainLoop type doesn't exist: " + main_loop_type); + OS::get_singleton()->alert("Error: MainLoop type doesn't exist: " + main_loop_type); return false; } else { - Object *ml = ClassDB::instance(main_loop_type); + Object *ml = ClassDB::instantiate(main_loop_type); ERR_FAIL_COND_V_MSG(!ml, false, "Can't instance MainLoop type."); main_loop = Object::cast_to<MainLoop>(ml); @@ -2070,7 +2139,7 @@ bool Main::start() { bool embed_subwindows = GLOBAL_DEF("display/window/subwindows/embed_subwindows", false); - if (single_window || (!project_manager && !editor && embed_subwindows)) { + if (OS::get_singleton()->is_single_window() || (!project_manager && !editor && embed_subwindows)) { sml->get_root()->set_embed_subwindows_hint(true); } ResourceLoader::add_custom_loaders(); @@ -2079,11 +2148,11 @@ bool Main::start() { if (!project_manager && !editor) { // game if (game_path != "" || script != "") { //autoload - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); //first pass, add the constants so they exist before any script is loaded - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->get(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.get(); if (info.is_singleton) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { @@ -2094,22 +2163,22 @@ bool Main::start() { //second pass, load into global constants List<Node *> to_add; - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->get(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.get(); RES res = ResourceLoader::load(info.path); ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path); Node *n = nullptr; if (res->is_class("PackedScene")) { Ref<PackedScene> ps = res; - n = ps->instance(); + n = ps->instantiate(); } else if (res->is_class("Script")) { Ref<Script> script_res = res; StringName ibt = script_res->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "Node"); ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path); - Object *obj = ClassDB::instance(ibt); + Object *obj = ClassDB::instantiate(ibt); ERR_CONTINUE_MSG(obj == nullptr, "Cannot instance script for autoload, expected 'Node' inheritance, got: " + @@ -2132,8 +2201,8 @@ bool Main::start() { } } - for (List<Node *>::Element *E = to_add.front(); E; E = E->next()) { - sml->get_root()->add_child(E->get()); + for (Node *E : to_add) { + sml->get_root()->add_child(E); } } } @@ -2155,7 +2224,7 @@ bool Main::start() { //standard helpers that can be changed from main config String stretch_mode = GLOBAL_DEF_BASIC("display/window/stretch/mode", "disabled"); - String stretch_aspect = GLOBAL_DEF_BASIC("display/window/stretch/aspect", "ignore"); + String stretch_aspect = GLOBAL_DEF_BASIC("display/window/stretch/aspect", "keep"); Size2i stretch_size = Size2i(GLOBAL_DEF_BASIC("display/window/size/width", 0), GLOBAL_DEF_BASIC("display/window/size/height", 0)); @@ -2194,6 +2263,10 @@ bool Main::start() { DisplayServer::get_singleton()->window_set_title(appname); #endif + // Define a very small minimum window size to prevent bugs such as GH-37242. + // It can still be overridden by the user in a script. + DisplayServer::get_singleton()->window_set_min_size(Size2i(64, 64)); + bool snap_controls = GLOBAL_DEF("gui/common/snap_controls_to_pixels", true); sml->get_root()->set_snap_controls_to_pixels(snap_controls); @@ -2214,7 +2287,7 @@ bool Main::start() { "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport")); - GLOBAL_DEF_BASIC("display/window/stretch/aspect", "ignore"); + GLOBAL_DEF_BASIC("display/window/stretch/aspect", "keep"); ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/aspect", PropertyInfo(Variant::STRING, "display/window/stretch/aspect", @@ -2235,7 +2308,7 @@ bool Main::start() { ProjectSettings::get_singleton()->set_custom_property_info( "rendering/textures/canvas_textures/default_texture_filter", PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM, - "Nearest,Linear,MipmapLinear,MipmapNearest")); + "Nearest,Linear,Linear Mipmap,Nearest Mipmap")); GLOBAL_DEF_BASIC("rendering/textures/canvas_textures/default_texture_repeat", 0); ProjectSettings::get_singleton()->set_custom_property_info( "rendering/textures/canvas_textures/default_texture_repeat", @@ -2296,6 +2369,9 @@ bool Main::start() { } } DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_EDITOR); + if (!debug_server_uri.is_empty()) { + EditorDebuggerNode::get_singleton()->start(debug_server_uri); + } } #endif if (!editor) { @@ -2312,7 +2388,7 @@ bool Main::start() { Node *scene = nullptr; Ref<PackedScene> scenedata = ResourceLoader::load(local_game_path); if (scenedata.is_valid()) { - scene = scenedata->instance(); + scene = scenedata->instantiate(); } ERR_FAIL_COND_V_MSG(!scene, false, "Failed loading scene: " + local_game_path); @@ -2337,7 +2413,7 @@ bool Main::start() { String iconpath = GLOBAL_DEF("application/config/icon", "Variant()"); if ((iconpath != "") && (!hasicon)) { Ref<Image> icon; - icon.instance(); + icon.instantiate(); if (ImageLoader::load_image(iconpath, icon) == OK) { DisplayServer::get_singleton()->set_icon(icon); hasicon = true; @@ -2347,14 +2423,13 @@ bool Main::start() { } #ifdef TOOLS_ENABLED - if (project_manager || (script == "" && game_path == "" && !editor)) { + if (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); - project_manager = true; } if (project_manager || editor) { @@ -2366,15 +2441,13 @@ bool Main::start() { } // Load SSL Certificates from Editor Settings (or builtin) - Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting( - "network/ssl/editor_ssl_certificates") - . - operator String()); + Crypto::load_default_certificates( + EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String()); } #endif } - if (!hasicon) { + if (!hasicon && OS::get_singleton()->get_bundle_icon_path().is_empty()) { Ref<Image> icon = memnew(Image(app_icon_png)); DisplayServer::get_singleton()->set_icon(icon); } @@ -2399,6 +2472,7 @@ uint32_t Main::frames = 0; uint32_t Main::frame = 0; bool Main::force_redraw_requested = false; int Main::iterating = 0; +bool Main::agile_input_event_flushing = false; bool Main::is_iterating() { return iterating > 0; @@ -2421,12 +2495,12 @@ bool Main::iteration() { uint64_t ticks_elapsed = ticks - last_ticks; - int physics_fps = Engine::get_singleton()->get_iterations_per_second(); - float physics_step = 1.0 / physics_fps; + int physics_ticks_per_second = Engine::get_singleton()->get_physics_ticks_per_second(); + float physics_step = 1.0 / physics_ticks_per_second; float time_scale = Engine::get_singleton()->get_time_scale(); - MainFrameTime advance = main_timer_sync.advance(physics_step, physics_fps); + MainFrameTime advance = main_timer_sync.advance(physics_step, physics_ticks_per_second); double process_step = advance.process_step; double scaled_step = process_step * time_scale; @@ -2448,9 +2522,13 @@ bool Main::iteration() { bool exit = false; - Engine::get_singleton()->_in_physics = true; - for (int iters = 0; iters < advance.physics_steps; ++iters) { + if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) { + Input::get_singleton()->flush_buffered_events(); + } + + Engine::get_singleton()->_in_physics = true; + uint64_t physics_begin = OS::get_singleton()->get_ticks_usec(); PhysicsServer3D::get_singleton()->sync(); @@ -2479,9 +2557,13 @@ bool Main::iteration() { physics_process_ticks = MAX(physics_process_ticks, OS::get_singleton()->get_ticks_usec() - physics_begin); // keep the largest one for reference physics_process_max = MAX(OS::get_singleton()->get_ticks_usec() - physics_begin, physics_process_max); Engine::get_singleton()->_physics_frames++; + + Engine::get_singleton()->_in_physics = false; } - Engine::get_singleton()->_in_physics = false; + if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) { + Input::get_singleton()->flush_buffered_events(); + } uint64_t process_begin = OS::get_singleton()->get_ticks_usec(); @@ -2526,10 +2608,10 @@ bool Main::iteration() { if (frame > 1000000) { if (editor || project_manager) { if (print_fps) { - print_line("Editor FPS: " + itos(frames)); + print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(1))); } } else if (GLOBAL_GET("debug/settings/stdout/print_fps") || print_fps) { - print_line("Game FPS: " + itos(frames)); + print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(1))); } Engine::get_singleton()->_fps = frames; @@ -2544,6 +2626,11 @@ bool Main::iteration() { iterating--; + // Needed for OSs using input buffering regardless accumulation (like Android) + if (Input::get_singleton()->is_using_input_buffering() && !agile_input_event_flushing) { + Input::get_singleton()->flush_buffered_events(); + } + if (fixed_fps != -1) { return exit; } @@ -2605,26 +2692,32 @@ void Main::cleanup(bool p_force) { // Sync pending commands that may have been queued from a different thread during ScriptServer finalization RenderingServer::get_singleton()->sync(); - //clear global shader variables before scene and other graphics stuff is deinitialized. + //clear global shader variables before scene and other graphics stuff are deinitialized. rendering_server->global_variables_clear(); + if (xr_server) { + // Now that we're unregistering properly in plugins we need to keep access to xr_server for a little longer + // We do however unset our primary interface + xr_server->set_primary_interface(Ref<XRInterface>()); + } + + unregister_driver_types(); + #ifdef TOOLS_ENABLED EditorNode::unregister_editor_types(); #endif - if (xr_server) { - // cleanup now before we pull the rug from underneath... - memdelete(xr_server); - } - ImageLoader::cleanup(); - unregister_driver_types(); unregister_module_types(); unregister_platform_apis(); unregister_scene_types(); unregister_server_types(); + if (xr_server) { + memdelete(xr_server); + } + if (audio_server) { audio_server->finish(); memdelete(audio_server); @@ -2640,10 +2733,6 @@ void Main::cleanup(bool p_force) { finalize_navigation_server(); finalize_display(); - if (tsman) { - memdelete(tsman); - } - if (input) { memdelete(input); } @@ -2660,9 +2749,15 @@ void Main::cleanup(bool p_force) { if (input_map) { memdelete(input_map); } + if (time_singleton) { + memdelete(time_singleton); + } if (translation_server) { memdelete(translation_server); } + if (tsman) { + memdelete(tsman); + } if (globals) { memdelete(globals); } diff --git a/main/main.h b/main/main.h index f4fff6b97e..4911ff42b4 100644 --- a/main/main.h +++ b/main/main.h @@ -42,9 +42,11 @@ class Main { static uint32_t frame; static bool force_redraw_requested; static int iterating; + static bool agile_input_event_flushing; public: static bool is_project_manager(); + static bool is_cmdline_tool(); static int test_entrypoint(int argc, char *argv[], bool &tests_need_run); static Error setup(const char *execpath, int argc, char *argv[], bool p_second_phase = true); static Error setup2(Thread::ID p_main_tid_override = 0); diff --git a/main/main_builders.py b/main/main_builders.py index aa91201c3e..c880bfa3c4 100644 --- a/main/main_builders.py +++ b/main/main_builders.py @@ -17,6 +17,7 @@ def make_splash(target, source, env): g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") g.write("#ifndef BOOT_SPLASH_H\n") g.write("#define BOOT_SPLASH_H\n") + # Use a neutral gray color to better fit various kinds of projects. g.write("static const Color boot_splash_bg_color = Color(0.14, 0.14, 0.14);\n") g.write("static const unsigned char boot_splash_png[] = {\n") for i in range(len(buf)): @@ -36,7 +37,9 @@ def make_splash_editor(target, source, env): g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") g.write("#ifndef BOOT_SPLASH_EDITOR_H\n") g.write("#define BOOT_SPLASH_EDITOR_H\n") - g.write("static const Color boot_splash_editor_bg_color = Color(0.14, 0.14, 0.14);\n") + # The editor splash background color is taken from the default editor theme's background color. + # This helps achieve a visually "smoother" transition between the splash screen and the editor. + g.write("static const Color boot_splash_editor_bg_color = Color(0.125, 0.145, 0.192);\n") g.write("static const unsigned char boot_splash_editor_png[] = {\n") for i in range(len(buf)): g.write(str(buf[i]) + ",\n") diff --git a/main/main_timer_sync.cpp b/main/main_timer_sync.cpp index 93448d0904..42023e5a2f 100644 --- a/main/main_timer_sync.cpp +++ b/main/main_timer_sync.cpp @@ -30,7 +30,7 @@ #include "main_timer_sync.h" -void MainFrameTime::clamp_process_step(float min_process_step, float max_process_step) { +void MainFrameTime::clamp_process_step(double min_process_step, double max_process_step) { if (process_step < min_process_step) { process_step = min_process_step; } else if (process_step > max_process_step) { @@ -43,25 +43,25 @@ void MainFrameTime::clamp_process_step(float min_process_step, float max_process // returns the fraction of p_physics_step required for the timer to overshoot // before advance_core considers changing the physics_steps return from // the typical values as defined by typical_physics_steps -float MainTimerSync::get_physics_jitter_fix() { +double MainTimerSync::get_physics_jitter_fix() { return Engine::get_singleton()->get_physics_jitter_fix(); } // gets our best bet for the average number of physics steps per render frame // return value: number of frames back this data is consistent -int MainTimerSync::get_average_physics_steps(float &p_min, float &p_max) { +int MainTimerSync::get_average_physics_steps(double &p_min, double &p_max) { p_min = typical_physics_steps[0]; p_max = p_min + 1; for (int i = 1; i < CONTROL_STEPS; ++i) { - const float typical_lower = typical_physics_steps[i]; - const float current_min = typical_lower / (i + 1); + const double typical_lower = typical_physics_steps[i]; + const double current_min = typical_lower / (i + 1); if (current_min > p_max) { - return i; // bail out of further restrictions would void the interval + return i; // bail out if further restrictions would void the interval } else if (current_min > p_min) { p_min = current_min; } - const float current_max = (typical_lower + 1) / (i + 1); + const double current_max = (typical_lower + 1) / (i + 1); if (current_max < p_min) { return i; } else if (current_max < p_max) { @@ -73,14 +73,14 @@ int MainTimerSync::get_average_physics_steps(float &p_min, float &p_max) { } // advance physics clock by p_process_step, return appropriate number of steps to simulate -MainFrameTime MainTimerSync::advance_core(float p_physics_step, int p_physics_fps, float p_process_step) { +MainFrameTime MainTimerSync::advance_core(double p_physics_step, int p_physics_ticks_per_second, double p_process_step) { MainFrameTime ret; ret.process_step = p_process_step; // simple determination of number of physics iteration time_accum += ret.process_step; - ret.physics_steps = floor(time_accum * p_physics_fps); + ret.physics_steps = floor(time_accum * p_physics_ticks_per_second); int min_typical_steps = typical_physics_steps[0]; int max_typical_steps = min_typical_steps + 1; @@ -105,9 +105,15 @@ MainFrameTime MainTimerSync::advance_core(float p_physics_step, int p_physics_fp } } +#ifdef DEBUG_ENABLED + if (max_typical_steps < 0) { + WARN_PRINT_ONCE("`max_typical_steps` is negative. This could hint at an engine bug or system timer misconfiguration."); + } +#endif + // try to keep it consistent with previous iterations if (ret.physics_steps < min_typical_steps) { - const int max_possible_steps = floor((time_accum)*p_physics_fps + get_physics_jitter_fix()); + const int max_possible_steps = floor((time_accum)*p_physics_ticks_per_second + get_physics_jitter_fix()); if (max_possible_steps < min_typical_steps) { ret.physics_steps = max_possible_steps; update_typical = true; @@ -115,7 +121,7 @@ MainFrameTime MainTimerSync::advance_core(float p_physics_step, int p_physics_fp ret.physics_steps = min_typical_steps; } } else if (ret.physics_steps > max_typical_steps) { - const int min_possible_steps = floor((time_accum)*p_physics_fps - get_physics_jitter_fix()); + const int min_possible_steps = floor((time_accum)*p_physics_ticks_per_second - get_physics_jitter_fix()); if (min_possible_steps > max_typical_steps) { ret.physics_steps = min_possible_steps; update_typical = true; @@ -124,6 +130,10 @@ MainFrameTime MainTimerSync::advance_core(float p_physics_step, int p_physics_fp } } + if (ret.physics_steps < 0) { + ret.physics_steps = 0; + } + time_accum -= ret.physics_steps * p_physics_step; // keep track of accumulated step counts @@ -146,15 +156,18 @@ MainFrameTime MainTimerSync::advance_core(float p_physics_step, int p_physics_fp } // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero -MainFrameTime MainTimerSync::advance_checked(float p_physics_step, int p_physics_fps, float p_process_step) { +MainFrameTime MainTimerSync::advance_checked(double p_physics_step, int p_physics_ticks_per_second, double p_process_step) { if (fixed_fps != -1) { p_process_step = 1.0 / fixed_fps; } + float min_output_step = p_process_step / 8; + min_output_step = MAX(min_output_step, 1E-6); + // compensate for last deficit p_process_step += time_deficit; - MainFrameTime ret = advance_core(p_physics_step, p_physics_fps, p_process_step); + MainFrameTime ret = advance_core(p_physics_step, p_physics_ticks_per_second, p_process_step); // we will do some clamping on ret.process_step and need to sync those changes to time_accum, // that's easiest if we just remember their fixed difference now @@ -163,7 +176,7 @@ MainFrameTime MainTimerSync::advance_checked(float p_physics_step, int p_physics // first, least important clamping: keep ret.process_step consistent with typical_physics_steps. // this smoothes out the process steps and culls small but quick variations. { - float min_average_physics_steps, max_average_physics_steps; + double min_average_physics_steps, max_average_physics_steps; int consistent_steps = get_average_physics_steps(min_average_physics_steps, max_average_physics_steps); if (consistent_steps > 3) { ret.clamp_process_step(min_average_physics_steps * p_physics_step, max_average_physics_steps * p_physics_step); @@ -171,15 +184,43 @@ MainFrameTime MainTimerSync::advance_checked(float p_physics_step, int p_physics } // second clamping: keep abs(time_deficit) < jitter_fix * frame_slise - float max_clock_deviation = get_physics_jitter_fix() * p_physics_step; + double max_clock_deviation = get_physics_jitter_fix() * p_physics_step; ret.clamp_process_step(p_process_step - max_clock_deviation, p_process_step + max_clock_deviation); // last clamping: make sure time_accum is between 0 and p_physics_step for consistency between physics and process ret.clamp_process_step(process_minus_accum, process_minus_accum + p_physics_step); + // all the operations above may have turned ret.p_process_step negative or zero, keep a minimal value + if (ret.process_step < min_output_step) { + ret.process_step = min_output_step; + } + // restore time_accum time_accum = ret.process_step - process_minus_accum; + // forcing ret.process_step to be positive may trigger a violation of the + // promise that time_accum is between 0 and p_physics_step +#ifdef DEBUG_ENABLED + if (time_accum < -1E-7) { + WARN_PRINT_ONCE("Intermediate value of `time_accum` is negative. This could hint at an engine bug or system timer misconfiguration."); + } +#endif + + if (time_accum > p_physics_step) { + const int extra_physics_steps = floor(time_accum * p_physics_ticks_per_second); + time_accum -= extra_physics_steps * p_physics_step; + ret.physics_steps += extra_physics_steps; + } + +#ifdef DEBUG_ENABLED + if (time_accum < -1E-7) { + WARN_PRINT_ONCE("Final value of `time_accum` is negative. It should always be between 0 and `p_physics_step`. This hints at an engine bug."); + } + if (time_accum > p_physics_step + 1E-7) { + WARN_PRINT_ONCE("Final value of `time_accum` is larger than `p_physics_step`. It should always be between 0 and `p_physics_step`. This hints at an engine bug."); + } +#endif + // track deficit time_deficit = p_process_step - ret.process_step; @@ -191,7 +232,7 @@ MainFrameTime MainTimerSync::advance_checked(float p_physics_step, int p_physics } // determine wall clock step since last iteration -float MainTimerSync::get_cpu_process_step() { +double MainTimerSync::get_cpu_process_step() { uint64_t cpu_ticks_elapsed = current_cpu_ticks_usec - last_cpu_ticks_usec; last_cpu_ticks_usec = current_cpu_ticks_usec; @@ -220,8 +261,8 @@ void MainTimerSync::set_fixed_fps(int p_fixed_fps) { } // advance one physics frame, return timesteps to take -MainFrameTime MainTimerSync::advance(float p_physics_step, int p_physics_fps) { - float cpu_process_step = get_cpu_process_step(); +MainFrameTime MainTimerSync::advance(double p_physics_step, int p_physics_ticks_per_second) { + double cpu_process_step = get_cpu_process_step(); - return advance_checked(p_physics_step, p_physics_fps, cpu_process_step); + return advance_checked(p_physics_step, p_physics_ticks_per_second, cpu_process_step); } diff --git a/main/main_timer_sync.h b/main/main_timer_sync.h index 884978bf96..d0ebcb8f96 100644 --- a/main/main_timer_sync.h +++ b/main/main_timer_sync.h @@ -34,11 +34,11 @@ #include "core/config/engine.h" struct MainFrameTime { - float process_step; // delta time to advance during process() + double process_step; // delta time to advance during process() int physics_steps; // number of times to iterate the physics engine - float interpolation_fraction; // fraction through the current physics tick + double interpolation_fraction; // fraction through the current physics tick - void clamp_process_step(float min_process_step, float max_process_step); + void clamp_process_step(double min_process_step, double max_process_step); }; class MainTimerSync { @@ -47,10 +47,10 @@ class MainTimerSync { uint64_t current_cpu_ticks_usec = 0; // logical game time since last physics timestep - float time_accum = 0; + double time_accum = 0; // current difference between wall clock time and reported sum of process_steps - float time_deficit = 0; + double time_deficit = 0; // number of frames back for keeping accumulated physics steps roughly constant. // value of 12 chosen because that is what is required to make 144 Hz monitors @@ -70,20 +70,20 @@ protected: // returns the fraction of p_physics_step required for the timer to overshoot // before advance_core considers changing the physics_steps return from // the typical values as defined by typical_physics_steps - float get_physics_jitter_fix(); + double get_physics_jitter_fix(); // gets our best bet for the average number of physics steps per render frame // return value: number of frames back this data is consistent - int get_average_physics_steps(float &p_min, float &p_max); + int get_average_physics_steps(double &p_min, double &p_max); // advance physics clock by p_process_step, return appropriate number of steps to simulate - MainFrameTime advance_core(float p_physics_step, int p_physics_fps, float p_process_step); + MainFrameTime advance_core(double p_physics_step, int p_physics_ticks_per_second, double p_process_step); // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero - MainFrameTime advance_checked(float p_physics_step, int p_physics_fps, float p_process_step); + MainFrameTime advance_checked(double p_physics_step, int p_physics_ticks_per_second, double p_process_step); // determine wall clock step since last iteration - float get_cpu_process_step(); + double get_cpu_process_step(); public: MainTimerSync(); @@ -96,7 +96,7 @@ public: void set_fixed_fps(int p_fixed_fps); // advance one frame, return timesteps to take - MainFrameTime advance(float p_physics_step, int p_physics_fps); + MainFrameTime advance(double p_physics_step, int p_physics_ticks_per_second); }; #endif // MAIN_TIMER_SYNC_H diff --git a/main/performance.cpp b/main/performance.cpp index a2e53f2ee2..f9ff34c05d 100644 --- a/main/performance.cpp +++ b/main/performance.cpp @@ -60,16 +60,12 @@ void Performance::_bind_methods() { BIND_ENUM_CONSTANT(OBJECT_RESOURCE_COUNT); BIND_ENUM_CONSTANT(OBJECT_NODE_COUNT); BIND_ENUM_CONSTANT(OBJECT_ORPHAN_NODE_COUNT); - BIND_ENUM_CONSTANT(RENDER_OBJECTS_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_VERTICES_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_MATERIAL_CHANGES_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_SHADER_CHANGES_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_SURFACE_CHANGES_IN_FRAME); - BIND_ENUM_CONSTANT(RENDER_DRAW_CALLS_IN_FRAME); + BIND_ENUM_CONSTANT(RENDER_TOTAL_OBJECTS_IN_FRAME); + BIND_ENUM_CONSTANT(RENDER_TOTAL_PRIMITIVES_IN_FRAME); + BIND_ENUM_CONSTANT(RENDER_TOTAL_DRAW_CALLS_IN_FRAME); BIND_ENUM_CONSTANT(RENDER_VIDEO_MEM_USED); BIND_ENUM_CONSTANT(RENDER_TEXTURE_MEM_USED); - BIND_ENUM_CONSTANT(RENDER_VERTEX_MEM_USED); - BIND_ENUM_CONSTANT(RENDER_USAGE_VIDEO_MEM_TOTAL); + BIND_ENUM_CONSTANT(RENDER_BUFFER_MEM_USED); BIND_ENUM_CONSTANT(PHYSICS_2D_ACTIVE_OBJECTS); BIND_ENUM_CONSTANT(PHYSICS_2D_COLLISION_PAIRS); BIND_ENUM_CONSTANT(PHYSICS_2D_ISLAND_COUNT); @@ -81,7 +77,7 @@ void Performance::_bind_methods() { BIND_ENUM_CONSTANT(MONITOR_MAX); } -float Performance::_get_node_count() const { +int Performance::_get_node_count() const { MainLoop *ml = OS::get_singleton()->get_main_loop(); SceneTree *sml = Object::cast_to<SceneTree>(ml); if (!sml) { @@ -103,16 +99,12 @@ String Performance::get_monitor_name(Monitor p_monitor) const { "object/resources", "object/nodes", "object/orphan_nodes", - "raster/objects_drawn", - "raster/vertices_drawn", - "raster/mat_changes", - "raster/shader_changes", - "raster/surface_changes", - "raster/draw_calls", + "raster/total_objects_drawn", + "raster/total_primitives_drawn", + "raster/total_draw_calls", "video/video_mem", "video/texture_mem", - "video/vertex_mem", - "video/video_mem_max", + "video/buffer_mem", "physics_2d/active_objects", "physics_2d/collision_pairs", "physics_2d/islands", @@ -126,7 +118,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const { return names[p_monitor]; } -float Performance::get_monitor(Monitor p_monitor) const { +double Performance::get_monitor(Monitor p_monitor) const { switch (p_monitor) { case TIME_FPS: return Engine::get_singleton()->get_frames_per_second(); @@ -148,26 +140,18 @@ float Performance::get_monitor(Monitor p_monitor) const { return _get_node_count(); case OBJECT_ORPHAN_NODE_COUNT: return Node::orphan_node_count; - case RENDER_OBJECTS_IN_FRAME: - return RS::get_singleton()->get_render_info(RS::INFO_OBJECTS_IN_FRAME); - case RENDER_VERTICES_IN_FRAME: - return RS::get_singleton()->get_render_info(RS::INFO_VERTICES_IN_FRAME); - case RENDER_MATERIAL_CHANGES_IN_FRAME: - return RS::get_singleton()->get_render_info(RS::INFO_MATERIAL_CHANGES_IN_FRAME); - case RENDER_SHADER_CHANGES_IN_FRAME: - return RS::get_singleton()->get_render_info(RS::INFO_SHADER_CHANGES_IN_FRAME); - case RENDER_SURFACE_CHANGES_IN_FRAME: - return RS::get_singleton()->get_render_info(RS::INFO_SURFACE_CHANGES_IN_FRAME); - case RENDER_DRAW_CALLS_IN_FRAME: - return RS::get_singleton()->get_render_info(RS::INFO_DRAW_CALLS_IN_FRAME); + case RENDER_TOTAL_OBJECTS_IN_FRAME: + return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TOTAL_OBJECTS_IN_FRAME); + case RENDER_TOTAL_PRIMITIVES_IN_FRAME: + return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TOTAL_PRIMITIVES_IN_FRAME); + case RENDER_TOTAL_DRAW_CALLS_IN_FRAME: + return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TOTAL_DRAW_CALLS_IN_FRAME); case RENDER_VIDEO_MEM_USED: - return RS::get_singleton()->get_render_info(RS::INFO_VIDEO_MEM_USED); + return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_VIDEO_MEM_USED); case RENDER_TEXTURE_MEM_USED: - return RS::get_singleton()->get_render_info(RS::INFO_TEXTURE_MEM_USED); - case RENDER_VERTEX_MEM_USED: - return RS::get_singleton()->get_render_info(RS::INFO_VERTEX_MEM_USED); - case RENDER_USAGE_VIDEO_MEM_TOTAL: - return RS::get_singleton()->get_render_info(RS::INFO_USAGE_VIDEO_MEM_TOTAL); + return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_TEXTURE_MEM_USED); + case RENDER_BUFFER_MEM_USED: + return RS::get_singleton()->get_rendering_info(RS::RENDERING_INFO_BUFFER_MEM_USED); case PHYSICS_2D_ACTIVE_OBJECTS: return PhysicsServer2D::get_singleton()->get_process_info(PhysicsServer2D::INFO_ACTIVE_OBJECTS); case PHYSICS_2D_COLLISION_PAIRS: @@ -207,10 +191,6 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const MONITOR_TYPE_QUANTITY, MONITOR_TYPE_QUANTITY, MONITOR_TYPE_QUANTITY, - MONITOR_TYPE_QUANTITY, - MONITOR_TYPE_QUANTITY, - MONITOR_TYPE_QUANTITY, - MONITOR_TYPE_MEMORY, MONITOR_TYPE_MEMORY, MONITOR_TYPE_MEMORY, MONITOR_TYPE_MEMORY, @@ -227,11 +207,11 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const return types[p_monitor]; } -void Performance::set_process_time(float p_pt) { +void Performance::set_process_time(double p_pt) { _process_time = p_pt; } -void Performance::set_physics_process_time(float p_pt) { +void Performance::set_physics_process_time(double p_pt) { _physics_process_time = p_pt; } diff --git a/main/performance.h b/main/performance.h index 122e5a4f9a..4653051ebb 100644 --- a/main/performance.h +++ b/main/performance.h @@ -43,10 +43,10 @@ class Performance : public Object { static Performance *singleton; static void _bind_methods(); - float _get_node_count() const; + int _get_node_count() const; - float _process_time; - float _physics_process_time; + double _process_time; + double _physics_process_time; class MonitorCall { Callable _callable; @@ -73,16 +73,12 @@ public: OBJECT_RESOURCE_COUNT, OBJECT_NODE_COUNT, OBJECT_ORPHAN_NODE_COUNT, - RENDER_OBJECTS_IN_FRAME, - RENDER_VERTICES_IN_FRAME, - RENDER_MATERIAL_CHANGES_IN_FRAME, - RENDER_SHADER_CHANGES_IN_FRAME, - RENDER_SURFACE_CHANGES_IN_FRAME, - RENDER_DRAW_CALLS_IN_FRAME, + RENDER_TOTAL_OBJECTS_IN_FRAME, + RENDER_TOTAL_PRIMITIVES_IN_FRAME, + RENDER_TOTAL_DRAW_CALLS_IN_FRAME, RENDER_VIDEO_MEM_USED, RENDER_TEXTURE_MEM_USED, - RENDER_VERTEX_MEM_USED, - RENDER_USAGE_VIDEO_MEM_TOTAL, + RENDER_BUFFER_MEM_USED, PHYSICS_2D_ACTIVE_OBJECTS, PHYSICS_2D_COLLISION_PAIRS, PHYSICS_2D_ISLAND_COUNT, @@ -100,13 +96,13 @@ public: MONITOR_TYPE_TIME }; - float get_monitor(Monitor p_monitor) const; + double get_monitor(Monitor p_monitor) const; String get_monitor_name(Monitor p_monitor) const; MonitorType get_monitor_type(Monitor p_monitor) const; - void set_process_time(float p_pt); - void set_physics_process_time(float p_pt); + void set_process_time(double p_pt); + void set_physics_process_time(double p_pt); void add_custom_monitor(const StringName &p_id, const Callable &p_callable, const Vector<Variant> &p_args); void remove_custom_monitor(const StringName &p_id); |