diff options
Diffstat (limited to 'main/main.cpp')
| -rw-r--r-- | main/main.cpp | 417 |
1 files changed, 319 insertions, 98 deletions
diff --git a/main/main.cpp b/main/main.cpp index ef997e71a7..7b46957904 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -70,6 +70,7 @@ #include "servers/movie_writer/movie_writer.h" #include "servers/movie_writer/movie_writer_mjpeg.h" #include "servers/navigation_server_2d.h" +#include "servers/navigation_server_2d_dummy.h" #include "servers/navigation_server_3d.h" #include "servers/navigation_server_3d_dummy.h" #include "servers/physics_server_2d.h" @@ -114,7 +115,10 @@ #ifdef MODULE_GDSCRIPT_ENABLED #include "modules/gdscript/gdscript.h" -#endif +#if defined(TOOLS_ENABLED) && !defined(GDSCRIPT_NO_LSP) +#include "modules/gdscript/language_server/gdscript_language_server.h" +#endif // TOOLS_ENABLED && !GDSCRIPT_NO_LSP +#endif // MODULE_GDSCRIPT_ENABLED /* Static members */ @@ -209,8 +213,11 @@ static bool debug_collisions = false; static bool debug_paths = false; static bool debug_navigation = false; static bool debug_avoidance = false; +static bool debug_canvas_item_redraw = false; #endif +static int max_fps = -1; static int frame_delay = 0; +static int audio_output_latency = 0; static bool disable_render_loop = false; static int fixed_fps = -1; static MovieWriter *movie_writer = nullptr; @@ -219,6 +226,7 @@ static bool print_fps = false; #ifdef TOOLS_ENABLED static bool dump_gdextension_interface = false; static bool dump_extension_api = false; +static bool include_docs_in_extension_api_dump = false; static bool validate_extension_api = false; static String validate_extension_api_file; #endif @@ -287,7 +295,7 @@ void initialize_physics() { // Physics server not found, Use the default physics physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server(); } - ERR_FAIL_COND(!physics_server_3d); + ERR_FAIL_NULL(physics_server_3d); physics_server_3d->init(); // 2D Physics server @@ -297,7 +305,7 @@ void initialize_physics() { // Physics server not found, Use the default physics physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server(); } - ERR_FAIL_COND(!physics_server_2d); + ERR_FAIL_NULL(physics_server_2d); physics_server_2d->init(); } @@ -318,6 +326,7 @@ void finalize_display() { void initialize_navigation_server() { ERR_FAIL_COND(navigation_server_3d != nullptr); + ERR_FAIL_COND(navigation_server_2d != nullptr); // Init 3D Navigation Server navigation_server_3d = NavigationServer3DManager::new_default_server(); @@ -330,16 +339,27 @@ void initialize_navigation_server() { // Should be impossible, but make sure it's not null. ERR_FAIL_NULL_MSG(navigation_server_3d, "Failed to initialize NavigationServer3D."); + navigation_server_3d->init(); // Init 2D Navigation Server - navigation_server_2d = memnew(NavigationServer2D); + navigation_server_2d = NavigationServer2DManager::new_default_server(); + if (!navigation_server_2d) { + WARN_PRINT_ONCE("No NavigationServer2D implementation has been registered! Falling back to a dummy implementation: navigation features will be unavailable."); + navigation_server_2d = memnew(NavigationServer2DDummy); + } + ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D."); + navigation_server_2d->init(); } void finalize_navigation_server() { + ERR_FAIL_NULL(navigation_server_3d); + navigation_server_3d->finish(); memdelete(navigation_server_3d); navigation_server_3d = nullptr; + ERR_FAIL_NULL(navigation_server_2d); + navigation_server_2d->finish(); memdelete(navigation_server_2d); navigation_server_2d = nullptr; } @@ -382,7 +402,10 @@ void Main::print_help(const char *p_binary) { 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 +#if defined(MODULE_GDSCRIPT_ENABLED) && !defined(GDSCRIPT_NO_LSP) + OS::get_singleton()->print(" --lsp-port <port> Use the specified port for the language server protocol. The port must be between 0 to 65535.\n"); +#endif // MODULE_GDSCRIPT_ENABLED && !GDSCRIPT_NO_LSP +#endif // TOOLS_ENABLED OS::get_singleton()->print(" --quit Quit after the first iteration.\n"); OS::get_singleton()->print(" --quit-after <int> Quit after the given number of iterations. Set to 0 to disable.\n"); OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n"); @@ -418,6 +441,8 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(")"); } OS::get_singleton()->print("].\n"); + OS::get_singleton()->print(" --audio-output-latency <ms> Override audio output latency in milliseconds (default is 15 ms).\n"); + OS::get_singleton()->print(" Lower values make sound playback more reactive but increase CPU usage, and may result in audio cracking if the CPU can't keep up.\n"); OS::get_singleton()->print(" --rendering-method <renderer> Renderer name. Requires driver support.\n"); OS::get_singleton()->print(" --rendering-driver <driver> Rendering driver (depends on display driver).\n"); @@ -453,6 +478,7 @@ void Main::print_help(const char *p_binary) { #if DEBUG_ENABLED OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n"); #endif + OS::get_singleton()->print(" --generate-spirv-debug-info Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n"); OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n"); OS::get_singleton()->print(" --single-threaded-scene Scene tree runs in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n"); #if defined(DEBUG_ENABLED) @@ -461,8 +487,10 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n"); OS::get_singleton()->print(" --debug-avoidance Show navigation avoidance debug visuals when running the scene.\n"); OS::get_singleton()->print(" --debug-stringnames Print all StringName allocations to stdout when the engine quits.\n"); + OS::get_singleton()->print(" --debug-canvas-item-redraw Display a rectangle each time a canvas item requests a redraw (useful to troubleshoot low processor mode).\n"); #endif - OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n"); + OS::get_singleton()->print(" --max-fps <fps> Set a maximum number of frames per second rendered (can be used to limit power usage). A value of 0 results in unlimited framerate.\n"); + OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds). Do not use as a FPS limiter; use --max-fps instead.\n"); OS::get_singleton()->print(" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n"); OS::get_singleton()->print(" --disable-vsync Forces disabling of vertical synchronization, even if enabled in the project settings. Does not override driver-level V-Sync enforcement.\n"); OS::get_singleton()->print(" --disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.\n"); @@ -474,6 +502,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print("Standalone tools:\n"); OS::get_singleton()->print(" -s, --script <script> Run a script.\n"); + OS::get_singleton()->print(" --main-loop <main_loop_name> Run a MainLoop specified by its global class name.\n"); OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n"); #ifdef TOOLS_ENABLED OS::get_singleton()->print(" --export-release <preset> <path> Export the project in release mode using the given preset and output path. The preset name should match one defined in export_presets.cfg.\n"); @@ -495,7 +524,8 @@ void Main::print_help(const char *p_binary) { 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-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.\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(" --validate-extension-api <path> Validate an extension API file dumped (with the option above) from a previous version of the engine to ensure API compatibility. If incompatibilities or errors are detected, the return code will be non zero.\n"); + OS::get_singleton()->print(" --dump-extension-api-with-docs Generate JSON dump of the Godot API like the previous option, but including documentation.\n"); + OS::get_singleton()->print(" --validate-extension-api <path> Validate an extension API file dumped (with one of the two previous options) from a previous version of the engine to ensure API compatibility. If incompatibilities or errors are detected, the return code will be non zero.\n"); OS::get_singleton()->print(" --benchmark Benchmark the run time and print it to console.\n"); OS::get_singleton()->print(" --benchmark-file <path> Benchmark the run time and save it to a given file in JSON format. The path should be absolute.\n"); #ifdef TESTS_ENABLED @@ -558,9 +588,15 @@ Error Main::test_setup() { ResourceLoader::load_path_remaps(); + // Initialize ThemeDB early so that scene types can register their theme items. + // Default theme will be initialized later, after modules and ScriptServer are ready. + initialize_theme_db(); + register_scene_types(); register_driver_types(); + register_scene_singletons(); + initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE); GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE); @@ -576,9 +612,9 @@ Error Main::test_setup() { register_platform_apis(); // Theme needs modules to be initialized so that sub-resources can be loaded. - initialize_theme_db(); - theme_db->initialize_theme(); - register_scene_singletons(); + theme_db->initialize_theme_noproject(); + + initialize_navigation_server(); ERR_FAIL_COND_V(TextServerManager::get_singleton()->get_interface_count() == 0, ERR_CANT_CREATE); @@ -638,6 +674,8 @@ void Main::test_cleanup() { finalize_theme_db(); + finalize_navigation_server(); + GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS); uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS); unregister_server_types(); @@ -707,8 +745,7 @@ int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) { * responsible for the initialization of all low level singletons and core types, and parsing * command line arguments to configure things accordingly. * If p_second_phase is true, it will chain into setup2() (default behavior). This is - * disabled on some platforms (Android, iOS, UWP) which trigger the second step in their - * own time. + * disabled on some platforms (Android, iOS) which trigger the second step in their own time. * * - setup2(p_main_tid_override) registers high level servers and singletons, displays the * boot splash, then registers higher level types (scene, editor, etc.). @@ -856,6 +893,15 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get()); } } + // If gpu is specified, both editor and debug instances started from editor will inherit. + if (I->get() == "--gpu-index") { + if (I->next()) { + forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->get()); + forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get()); + forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->get()); + forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->next()->get()); + } + } #endif if (adding_user_args) { @@ -914,6 +960,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->print("Missing audio driver argument, aborting.\n"); goto error; } + } else if (I->get() == "--audio-output-latency") { + if (I->next()) { + audio_output_latency = I->next()->get().to_int(); + N = I->next()->next(); + } else { + OS::get_singleton()->print("Missing audio output latency argument, aborting.\n"); + goto error; + } } else if (I->get() == "--text-driver") { if (I->next()) { text_driver = I->next()->get(); @@ -976,13 +1030,11 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; } } else if (I->get() == "-f" || I->get() == "--fullscreen") { // force fullscreen - init_fullscreen = true; + window_mode = DisplayServer::WINDOW_MODE_FULLSCREEN; } else if (I->get() == "-m" || I->get() == "--maximized") { // force maximized window - init_maximized = true; window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED; - } else if (I->get() == "-w" || I->get() == "--windowed") { // force windowed window init_windowed = true; @@ -1000,6 +1052,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "--gpu-abort") { Engine::singleton->abort_on_gpu_errors = true; #endif + } else if (I->get() == "--generate-spirv-debug-info") { + Engine::singleton->generate_spirv_debug_info = true; } else if (I->get() == "--tablet-driver") { if (I->next()) { tablet_driver = I->next()->get(); @@ -1011,19 +1065,19 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "--delta-smoothing") { if (I->next()) { String string = I->next()->get(); - bool recognised = false; + bool recognized = false; if (string == "enable") { OS::get_singleton()->set_delta_smoothing(true); delta_smoothing_override = true; - recognised = true; + recognized = true; } if (string == "disable") { OS::get_singleton()->set_delta_smoothing(false); delta_smoothing_override = false; - recognised = true; + recognized = true; } - if (!recognised) { - OS::get_singleton()->print("Delta-smoothing argument not recognised, aborting.\n"); + if (!recognized) { + OS::get_singleton()->print("Delta-smoothing argument not recognized, aborting.\n"); goto error; } N = I->next()->next(); @@ -1150,6 +1204,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph rtm = OS::RENDER_THREAD_UNSAFE; } else if (I->next()->get() == "separate") { rtm = OS::RENDER_SEPARATE_THREAD; + } else { + OS::get_singleton()->print("Unknown render thread mode, aborting.\nValid options are 'unsafe', 'safe' and 'separate'.\n"); + goto error; } N = I->next()->next(); @@ -1202,6 +1259,17 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // run the project instead of a cmdline tool. // Needs full refactoring to fix properly. main_args.push_back(I->get()); + } else if (I->get() == "--dump-extension-api-with-docs") { + // Register as an editor instance to use low-end fallback if relevant. + editor = true; + cmdline_tool = true; + dump_extension_api = true; + include_docs_in_extension_api_dump = true; + print_line("Dumping Extension API including documentation"); + // Hack. Not needed but otherwise we end up detecting that this should + // run the project instead of a cmdline tool. + // Needs full refactoring to fix properly. + main_args.push_back(I->get()); } else if (I->get() == "--validate-extension-api") { // Register as an editor instance to use low-end fallback if relevant. editor = true; @@ -1271,6 +1339,19 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph audio_driver = NULL_AUDIO_DRIVER; display_driver = NULL_DISPLAY_DRIVER; main_args.push_back(I->get()); +#ifdef MODULE_GDSCRIPT_ENABLED + } else if (I->get() == "--gdscript-docs") { + if (I->next()) { + project_path = I->next()->get(); + // Will be handled in start() + main_args.push_back(I->get()); + main_args.push_back(I->next()->get()); + N = I->next()->next(); + } else { + OS::get_singleton()->print("Missing relative or absolute path to project for --gdscript-docs, aborting.\n"); + goto error; + } +#endif // MODULE_GDSCRIPT_ENABLED #endif // TOOLS_ENABLED } else if (I->get() == "--path") { // set path of project to start or edit @@ -1325,6 +1406,16 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; } + } else if (I->get() == "--max-fps") { // set maximum rendered FPS + + if (I->next()) { + max_fps = I->next()->get().to_int(); + N = I->next()->next(); + } else { + OS::get_singleton()->print("Missing maximum FPS argument, aborting.\n"); + goto error; + } + } else if (I->get() == "--frame-delay") { // force frame delay if (I->next()) { @@ -1366,6 +1457,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph debug_navigation = true; } else if (I->get() == "--debug-avoidance") { debug_avoidance = true; + } else if (I->get() == "--debug-canvas-item-redraw") { + debug_canvas_item_redraw = true; } else if (I->get() == "--debug-stringnames") { StringName::set_debug_stringnames(true); #endif @@ -1453,7 +1546,21 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->print("Missing <path> argument for --benchmark-file <path>.\n"); goto error; } - +#if defined(TOOLS_ENABLED) && defined(MODULE_GDSCRIPT_ENABLED) && !defined(GDSCRIPT_NO_LSP) + } else if (I->get() == "--lsp-port") { + if (I->next()) { + int port_override = I->next()->get().to_int(); + if (port_override < 0 || port_override > 65535) { + OS::get_singleton()->print("<port> argument for --lsp-port <port> must be between 0 and 65535.\n"); + goto error; + } + GDScriptLanguageServer::port_override = port_override; + N = I->next()->next(); + } else { + OS::get_singleton()->print("Missing <port> argument for --lsp-port <port>.\n"); + goto error; + } +#endif // TOOLS_ENABLED && MODULE_GDSCRIPT_ENABLED && !GDSCRIPT_NO_LSP } else if (I->get() == "--" || I->get() == "++") { adding_user_args = true; } else { @@ -1518,6 +1625,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } } +#ifdef TOOLS_ENABLED + if (editor) { + Engine::get_singleton()->set_editor_hint(true); + Engine::get_singleton()->set_extension_reloading_enabled(true); + } +#endif + // Initialize user data dir. OS::get_singleton()->ensure_user_data_dir(); @@ -1545,9 +1659,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #ifdef TOOLS_ENABLED if (editor) { packed_data->set_disabled(true); - Engine::get_singleton()->set_editor_hint(true); main_args.push_back("--editor"); - if (!init_windowed) { + if (!init_windowed && !init_fullscreen) { init_maximized = true; window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED; } @@ -1607,7 +1720,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } if (bool(GLOBAL_GET("application/run/disable_stderr"))) { CoreGlobals::print_error_enabled = false; - }; + } if (quiet_stdout) { CoreGlobals::print_line_enabled = false; @@ -1626,27 +1739,43 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph String default_driver = driver_hints.get_slice(",", 0); // For now everything defaults to vulkan when available. This can change in future updates. - GLOBAL_DEF("rendering/rendering_device/driver", default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.windows", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.linuxbsd", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.android", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.ios", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.macos", PROPERTY_HINT_ENUM, driver_hints), default_driver); - - driver_hints = ""; + GLOBAL_DEF_RST_NOVAL("rendering/rendering_device/driver", default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.windows", PROPERTY_HINT_ENUM, driver_hints), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.linuxbsd", PROPERTY_HINT_ENUM, driver_hints), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.android", PROPERTY_HINT_ENUM, driver_hints), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.ios", PROPERTY_HINT_ENUM, driver_hints), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/rendering_device/driver.macos", PROPERTY_HINT_ENUM, driver_hints), default_driver); + } + + { + String driver_hints = ""; + String driver_hints_angle = ""; + String driver_hints_egl = ""; #ifdef GLES3_ENABLED - driver_hints += "opengl3"; + driver_hints = "opengl3"; + driver_hints_angle = "opengl3,opengl3_angle"; + driver_hints_egl = "opengl3,opengl3_es"; +#endif + + String default_driver = driver_hints.get_slice(",", 0); + String default_driver_macos = default_driver; +#if defined(GLES3_ENABLED) && defined(EGL_STATIC) && defined(MACOS_ENABLED) + default_driver_macos = "opengl3_angle"; // Default to ANGLE if it's built-in. #endif - default_driver = driver_hints.get_slice(",", 0); + GLOBAL_DEF_RST_NOVAL("rendering/gl_compatibility/driver", default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.windows", PROPERTY_HINT_ENUM, driver_hints_angle), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.linuxbsd", PROPERTY_HINT_ENUM, driver_hints_egl), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.web", PROPERTY_HINT_ENUM, driver_hints), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.android", PROPERTY_HINT_ENUM, driver_hints), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.ios", PROPERTY_HINT_ENUM, driver_hints), default_driver); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.macos", PROPERTY_HINT_ENUM, driver_hints_angle), default_driver_macos); + + GLOBAL_DEF_RST("rendering/gl_compatibility/nvidia_disable_threaded_optimization", true); + GLOBAL_DEF_RST("rendering/gl_compatibility/fallback_to_angle", true); + GLOBAL_DEF_RST("rendering/gl_compatibility/fallback_to_native", true); - GLOBAL_DEF("rendering/gl_compatibility/driver", default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.windows", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.linuxbsd", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.web", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.android", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.ios", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.macos", PROPERTY_HINT_ENUM, driver_hints), default_driver); + GLOBAL_DEF_RST(PropertyInfo(Variant::ARRAY, "rendering/gl_compatibility/force_angle_on_devices", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::DICTIONARY, PROPERTY_HINT_NONE, String())), Array()); } // Start with RenderingDevice-based backends. Should be included if any RD driver present. @@ -1720,7 +1849,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // Set a default renderer if none selected. Try to choose one that matches the driver. if (rendering_method.is_empty()) { - if (rendering_driver == "opengl3") { + if (rendering_driver == "opengl3" || rendering_driver == "opengl3_angle" || rendering_driver == "opengl3_es") { rendering_method = "gl_compatibility"; } else { rendering_method = "forward_plus"; @@ -1738,6 +1867,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #ifdef GLES3_ENABLED if (rendering_method == "gl_compatibility") { available_drivers.push_back("opengl3"); + available_drivers.push_back("opengl3_angle"); + available_drivers.push_back("opengl3_es"); } #endif if (available_drivers.is_empty()) { @@ -1830,21 +1961,31 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } window_mode = (DisplayServer::WindowMode)(GLOBAL_GET("display/window/size/mode").operator int()); int initial_position_type = GLOBAL_GET("display/window/size/initial_position_type").operator int(); - if (initial_position_type == 0) { + if (initial_position_type == 0) { // Absolute. if (!init_use_custom_pos) { init_custom_pos = GLOBAL_GET("display/window/size/initial_position").operator Vector2i(); init_use_custom_pos = true; } - } else if (initial_position_type == 1) { + } else if (initial_position_type == 1) { // Center of Primary Screen. if (!init_use_custom_screen) { init_screen = DisplayServer::SCREEN_PRIMARY; init_use_custom_screen = true; } - } else if (initial_position_type == 2) { + } else if (initial_position_type == 2) { // Center of Other Screen. if (!init_use_custom_screen) { init_screen = GLOBAL_GET("display/window/size/initial_screen").operator int(); init_use_custom_screen = true; } + } else if (initial_position_type == 3) { // Center of Screen With Mouse Pointer. + if (!init_use_custom_screen) { + init_screen = DisplayServer::SCREEN_WITH_MOUSE_FOCUS; + init_use_custom_screen = true; + } + } else if (initial_position_type == 4) { // Center of Screen With Keyboard Focus. + if (!init_use_custom_screen) { + init_screen = DisplayServer::SCREEN_WITH_KEYBOARD_FOCUS; + init_use_custom_screen = true; + } } } @@ -1874,7 +2015,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } if (rtm >= 0 && rtm < 3) { - if (editor) { + if (editor || project_manager) { + // Editor and project manager cannot run with rendering in a separate thread (they will crash on startup). rtm = OS::RENDER_THREAD_SAFE; } OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm); @@ -1942,6 +2084,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_max_physics_steps_per_frame(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/max_physics_steps_per_frame", PROPERTY_HINT_RANGE, "1,100,1"), 8)); Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0)); + Engine::get_singleton()->set_audio_output_latency(GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15)); + // Use a safer default output_latency for web to avoid audio cracking on low-end devices, especially mobile. + GLOBAL_DEF_RST("audio/driver/output_latency.web", 50); GLOBAL_DEF("debug/settings/stdout/print_fps", false); GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false); @@ -1951,10 +2096,22 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout"); } +#if defined(MACOS_ENABLED) || defined(IOS_ENABLED) + OS::get_singleton()->set_environment("MVK_CONFIG_LOG_LEVEL", OS::get_singleton()->_verbose_stdout ? "3" : "1"); // 1 = Errors only, 3 = Info +#endif + + if (max_fps >= 0) { + Engine::get_singleton()->set_max_fps(max_fps); + } + if (frame_delay == 0) { frame_delay = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), 0); } + if (audio_output_latency >= 1) { + Engine::get_singleton()->set_audio_output_latency(audio_output_latency); + } + OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false)); OS::get_singleton()->set_low_processor_usage_mode_sleep_usec( GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater"), 6900)); // Roughly 144 FPS @@ -1975,10 +2132,17 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/form_factor", PROPERTY_HINT_ENUM, "Head Mounted,Handheld"), "0"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1"); // "Mono,Stereo,Quad,Observer" GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/reference_space", PROPERTY_HINT_ENUM, "Local,Stage"), "1"); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/environment_blend_mode", PROPERTY_HINT_ENUM, "Opaque,Additive,Alpha"), "0"); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/foveation_level", PROPERTY_HINT_ENUM, "Off,Low,Medium,High"), "0"); + GLOBAL_DEF_BASIC("xr/openxr/foveation_dynamic", false); GLOBAL_DEF_BASIC("xr/openxr/submit_depth_buffer", false); GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true); + // OpenXR project extensions settings. + GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", true); + GLOBAL_DEF_BASIC("xr/openxr/extensions/eye_gaze_interaction", false); + #ifdef TOOLS_ENABLED // Disabled for now, using XR inside of the editor we'll be working on during the coming months. @@ -2060,6 +2224,25 @@ error: return exit_code; } +Error _parse_resource_dummy(void *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { + VariantParser::Token token; + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_NUMBER && token.type != VariantParser::TK_STRING) { + r_err_str = "Expected number (old style sub-resource index) or String (ext-resource ID)"; + return ERR_PARSE_ERROR; + } + + r_res.unref(); + + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { + r_err_str = "Expected ')'"; + return ERR_PARSE_ERROR; + } + + return OK; +} + Error Main::setup2() { Thread::make_main_thread(); // Make whatever thread call this the main thread. set_current_thread_safe_for_nodes(true); @@ -2091,6 +2274,8 @@ Error Main::setup2() { // Editor setting class is not available, load config directly. if (!init_use_custom_screen && (editor || project_manager) && EditorPaths::get_singleton()->are_paths_valid()) { Ref<DirAccess> dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); + ERR_FAIL_COND_V(dir.is_null(), FAILED); + String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres"; String config_file_path = EditorPaths::get_singleton()->get_config_dir().path_join(config_file_name); if (dir->file_exists(config_file_name)) { @@ -2107,12 +2292,16 @@ Error Main::setup2() { int lines = 0; String error_text; + VariantParser::ResourceParser rp_new; + rp_new.ext_func = _parse_resource_dummy; + rp_new.sub_func = _parse_resource_dummy; + while (true) { assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); - err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); + err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp_new, true); if (err == ERR_FILE_EOF) { break; } @@ -2208,6 +2397,10 @@ Error Main::setup2() { } } + if (OS::get_singleton()->_render_thread_mode == OS::RENDER_SEPARATE_THREAD) { + WARN_PRINT("The Multi-Threaded rendering thread model is experimental, and has known issues which can lead to project crashes. Use the Single-Safe option in the project settings instead."); + } + /* Initialize Pen Tablet Driver */ { @@ -2387,6 +2580,9 @@ Error Main::setup2() { id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true))); } + GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false); + GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false); + MAIN_PRINT("Main: Load Translations and Remaps"); translation_server->setup(); //register translations, load them, etc. @@ -2464,9 +2660,15 @@ Error Main::setup2() { OS::get_singleton()->benchmark_begin_measure("scene"); + // Initialize ThemeDB early so that scene types can register their theme items. + // Default theme will be initialized later, after modules and ScriptServer are ready. + initialize_theme_db(); + register_scene_types(); register_driver_types(); + register_scene_singletons(); + initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE); GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE); @@ -2484,11 +2686,6 @@ Error Main::setup2() { register_platform_apis(); - // Theme needs modules to be initialized so that sub-resources can be loaded. - // Default theme is initialized later, after ScriptServer is ready. - initialize_theme_db(); - register_scene_singletons(); - GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/mouse_cursor/custom_image", PROPERTY_HINT_FILE, "*.png,*.webp"), String()); GLOBAL_DEF_BASIC("display/mouse_cursor/custom_image_hotspot", Vector2()); GLOBAL_DEF_BASIC("display/mouse_cursor/tooltip_position_offset", Point2(10, 10)); @@ -2566,6 +2763,7 @@ bool Main::start() { String positional_arg; String game_path; String script; + String main_loop_type; bool check_only = false; #ifdef TOOLS_ENABLED @@ -2629,6 +2827,8 @@ bool Main::start() { bool parsed_pair = true; if (args[i] == "-s" || args[i] == "--script") { script = args[i + 1]; + } else if (args[i] == "--main-loop") { + main_loop_type = args[i + 1]; #ifdef TOOLS_ENABLED } else if (args[i] == "--doctool") { doc_tool_path = args[i + 1]; @@ -2670,41 +2870,16 @@ bool Main::start() { } uint64_t minimum_time_msec = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/boot_splash/minimum_display_time", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:ms"), 0); + if (Engine::get_singleton()->is_editor_hint()) { + minimum_time_msec = 0; + } #ifdef TOOLS_ENABLED #ifdef MODULE_GDSCRIPT_ENABLED - if (!doc_tool_path.is_empty() && !gdscript_docs_path.is_empty()) { - DocTools docs; - Error err; - - Vector<String> paths = get_files_with_extension(gdscript_docs_path, "gd"); - ERR_FAIL_COND_V_MSG(paths.size() == 0, false, "Couldn't find any GDScript files under the given directory: " + gdscript_docs_path); - - for (const String &path : paths) { - Ref<GDScript> gdscript = ResourceLoader::load(path); - for (const DocData::ClassDoc &class_doc : gdscript->get_documentation()) { - docs.add_doc(class_doc); - } - } - - if (doc_tool_path == ".") { - doc_tool_path = "./docs"; - } - - Ref<DirAccess> da = DirAccess::create_for_path(doc_tool_path); - err = da->make_dir_recursive(doc_tool_path); - ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create GDScript docs directory: " + doc_tool_path + ": " + itos(err)); - - HashMap<String, String> doc_data_classes; - err = docs.save_classes(doc_tool_path, doc_data_classes, false); - ERR_FAIL_COND_V_MSG(err != OK, false, "Error saving GDScript docs:" + itos(err)); - - OS::get_singleton()->set_exit_code(EXIT_SUCCESS); - return false; - } -#endif // MODULE_GDSCRIPT_ENABLED - + if (!doc_tool_path.is_empty() && gdscript_docs_path.is_empty()) { +#else if (!doc_tool_path.is_empty()) { +#endif // Needed to instance editor-only classes for their default values Engine::get_singleton()->set_editor_hint(true); @@ -2724,6 +2899,7 @@ bool Main::start() { // Default values should be synced with mono_gd/gd_mono.cpp. GLOBAL_DEF("dotnet/project/assembly_name", ""); GLOBAL_DEF("dotnet/project/solution_directory", ""); + GLOBAL_DEF(PropertyInfo(Variant::INT, "dotnet/project/assembly_reload_attempts", PROPERTY_HINT_RANGE, "1,16,1,or_greater"), 3); #endif Error err; @@ -2795,7 +2971,8 @@ bool Main::start() { } if (dump_extension_api) { - GDExtensionAPIDump::generate_extension_json_file("extension_api.json"); + Engine::get_singleton()->set_editor_hint(true); // "extension_api.json" should always contains editor singletons. + GDExtensionAPIDump::generate_extension_json_file("extension_api.json", include_docs_in_extension_api_dump); } if (dump_gdextension_interface || dump_extension_api) { @@ -2847,7 +3024,9 @@ bool Main::start() { if (editor) { main_loop = memnew(SceneTree); } - String main_loop_type = GLOBAL_GET("application/run/main_loop_type"); + if (main_loop_type.is_empty()) { + main_loop_type = GLOBAL_GET("application/run/main_loop_type"); + } if (!script.is_empty()) { Ref<Script> script_res = ResourceLoader::load(script); @@ -2874,7 +3053,7 @@ bool Main::start() { 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); + script_loop->set_script(script_res); main_loop = script_loop; } else { return false; @@ -2897,7 +3076,7 @@ bool Main::start() { 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); + script_loop->set_script(script_res); main_loop = script_loop; } } @@ -2912,7 +3091,7 @@ bool Main::start() { return false; } else { Object *ml = ClassDB::instantiate(main_loop_type); - ERR_FAIL_COND_V_MSG(!ml, false, "Can't instance MainLoop type."); + ERR_FAIL_NULL_V_MSG(ml, false, "Can't instance MainLoop type."); main_loop = Object::cast_to<MainLoop>(ml); if (!main_loop) { @@ -2922,6 +3101,8 @@ bool Main::start() { } } + OS::get_singleton()->set_main_loop(main_loop); + SceneTree *sml = Object::cast_to<SceneTree>(main_loop); if (sml) { #ifdef DEBUG_ENABLED @@ -2933,19 +3114,17 @@ bool Main::start() { } if (debug_navigation) { sml->set_debug_navigation_hint(true); + NavigationServer3D::get_singleton()->set_debug_navigation_enabled(true); } if (debug_avoidance) { - sml->set_debug_avoidance_hint(true); + NavigationServer3D::get_singleton()->set_debug_avoidance_enabled(true); } if (debug_navigation || debug_avoidance) { NavigationServer3D::get_singleton()->set_active(true); NavigationServer3D::get_singleton()->set_debug_enabled(true); - if (debug_navigation) { - NavigationServer3D::get_singleton()->set_debug_navigation_enabled(true); - } - if (debug_avoidance) { - NavigationServer3D::get_singleton()->set_debug_avoidance_enabled(true); - } + } + if (debug_canvas_item_redraw) { + RenderingServer::get_singleton()->canvas_item_set_debug_redraw(true); } #endif @@ -3036,6 +3215,38 @@ bool Main::start() { } #ifdef TOOLS_ENABLED +#ifdef MODULE_GDSCRIPT_ENABLED + if (!doc_tool_path.is_empty() && !gdscript_docs_path.is_empty()) { + DocTools docs; + Error err; + + Vector<String> paths = get_files_with_extension(gdscript_docs_path, "gd"); + ERR_FAIL_COND_V_MSG(paths.size() == 0, false, "Couldn't find any GDScript files under the given directory: " + gdscript_docs_path); + + for (const String &path : paths) { + Ref<GDScript> gdscript = ResourceLoader::load(path); + for (const DocData::ClassDoc &class_doc : gdscript->get_documentation()) { + docs.add_doc(class_doc); + } + } + + if (doc_tool_path == ".") { + doc_tool_path = "./docs"; + } + + Ref<DirAccess> da = DirAccess::create_for_path(doc_tool_path); + err = da->make_dir_recursive(doc_tool_path); + ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create GDScript docs directory: " + doc_tool_path + ": " + itos(err)); + + HashMap<String, String> doc_data_classes; + err = docs.save_classes(doc_tool_path, doc_data_classes, false); + ERR_FAIL_COND_V_MSG(err != OK, false, "Error saving GDScript docs:" + itos(err)); + + OS::get_singleton()->set_exit_code(EXIT_SUCCESS); + return false; + } +#endif // MODULE_GDSCRIPT_ENABLED + EditorNode *editor_node = nullptr; if (editor) { OS::get_singleton()->benchmark_begin_measure("editor"); @@ -3061,6 +3272,7 @@ bool Main::start() { Size2i stretch_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); real_t stretch_scale = GLOBAL_GET("display/window/stretch/scale"); + String stretch_scale_mode = GLOBAL_GET("display/window/stretch/scale_mode"); Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED; if (stretch_mode == "canvas_items") { @@ -3080,8 +3292,14 @@ bool Main::start() { cs_aspect = Window::CONTENT_SCALE_ASPECT_EXPAND; } + Window::ContentScaleStretch cs_stretch = Window::CONTENT_SCALE_STRETCH_FRACTIONAL; + if (stretch_scale_mode == "integer") { + cs_stretch = Window::CONTENT_SCALE_STRETCH_INTEGER; + } + sml->get_root()->set_content_scale_mode(cs_sm); sml->get_root()->set_content_scale_aspect(cs_aspect); + sml->get_root()->set_content_scale_stretch(cs_stretch); sml->get_root()->set_content_scale_size(stretch_size); sml->get_root()->set_content_scale_factor(stretch_scale); @@ -3140,6 +3358,8 @@ bool Main::start() { if (sep == -1) { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V(da.is_null(), false); + local_game_path = da->get_current_dir().path_join(local_game_path); } else { Ref<DirAccess> da = DirAccess::open(local_game_path.substr(0, sep)); @@ -3188,7 +3408,7 @@ bool Main::start() { scene = scenedata->instantiate(); } - ERR_FAIL_COND_V_MSG(!scene, false, "Failed loading scene: " + local_game_path); + ERR_FAIL_NULL_V_MSG(scene, false, "Failed loading scene: " + local_game_path + "."); sml->add_current_scene(scene); #ifdef MACOS_ENABLED @@ -3246,8 +3466,6 @@ bool Main::start() { DisplayServer::get_singleton()->set_icon(icon); } - OS::get_singleton()->set_main_loop(main_loop); - if (movie_writer) { movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path()); } @@ -3337,6 +3555,9 @@ bool Main::iteration() { // process all our active interfaces XRServer::get_singleton()->_process(); + NavigationServer2D::get_singleton()->sync(); + NavigationServer3D::get_singleton()->sync(); + 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(); |
