diff options
55 files changed, 1145 insertions, 950 deletions
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 2794c83e22..18ed92b57f 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -28,7 +28,7 @@ jobs: target: editor tests: true # Skip debug symbols, they're way too big with MSVC. - sconsflags: debug_symbols=no vsproj=yes windows_subsystem=console + sconsflags: debug_symbols=no vsproj=yes vsproj_gen_only=no windows_subsystem=console bin: "./bin/godot.windows.editor.x86_64.exe" - name: Template (target=template_release) diff --git a/.gitignore b/.gitignore index 51b6f66dca..f43b81f286 100644 --- a/.gitignore +++ b/.gitignore @@ -371,3 +371,4 @@ $RECYCLE.BIN/ *.msm *.msp *.lnk +*.generated.props diff --git a/SConstruct b/SConstruct index 6a4dea2c09..f0f53ddc65 100644 --- a/SConstruct +++ b/SConstruct @@ -1000,9 +1000,6 @@ if selected_platform in platform_list: # Microsoft Visual Studio Project Generation if env["vsproj"]: - if os.name != "nt": - print("Error: The `vsproj` option is only usable on Windows with Visual Studio.") - Exit(255) env["CPPPATH"] = [Dir(path) for path in env["CPPPATH"]] methods.generate_vs_project(env, ARGUMENTS, env["vsproj_name"]) methods.generate_cpp_hint_file("cpp.hint") diff --git a/core/object/object.cpp b/core/object/object.cpp index 3901c4835d..cc33d0ab8a 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1683,6 +1683,7 @@ void Object::_bind_methods() { BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE); BIND_CONSTANT(NOTIFICATION_PREDELETE); + BIND_CONSTANT(NOTIFICATION_EXTENSION_RELOADED); BIND_ENUM_CONSTANT(CONNECT_DEFERRED); BIND_ENUM_CONSTANT(CONNECT_PERSIST); diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index 5fa43f868e..f66709cc5d 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -1056,6 +1056,9 @@ <constant name="NOTIFICATION_PREDELETE" value="1"> Notification received when the object is about to be deleted. Can act as the deconstructor of some programming languages. </constant> + <constant name="NOTIFICATION_EXTENSION_RELOADED" value="2"> + Notification received when the object finishes hot reloading. This notification is only sent for extensions classes and derived. + </constant> <constant name="CONNECT_DEFERRED" value="1" enum="ConnectFlags"> Deferred connections trigger their [Callable]s on idle time (at the end of the frame), rather than instantly. </constant> diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml index 00ce6e2a8b..6fb43d77d9 100644 --- a/doc/classes/XRInterface.xml +++ b/doc/classes/XRInterface.xml @@ -102,16 +102,18 @@ Is [code]true[/code] if this interface has been initialized. </description> </method> - <method name="is_passthrough_enabled"> + <method name="is_passthrough_enabled" is_deprecated="true"> <return type="bool" /> <description> Is [code]true[/code] if passthrough is enabled. + [i]Deprecated.[/i] Check if [member environment_blend_mode] is [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND], instead. </description> </method> - <method name="is_passthrough_supported"> + <method name="is_passthrough_supported" is_deprecated="true"> <return type="bool" /> <description> Is [code]true[/code] if this interface supports passthrough. + [i]Deprecated.[/i] Check that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is supported using [method get_supported_environment_blend_modes], instead. </description> </method> <method name="set_environment_blend_mode"> @@ -144,17 +146,19 @@ [b]Note:[/b] Changing this after the interface has already been initialized can be jarring for the player, so it's recommended to recenter on the HMD with [method XRServer.center_on_hmd] (if switching to [constant XRInterface.XR_PLAY_AREA_STAGE]) or make the switch during a scene change. </description> </method> - <method name="start_passthrough"> + <method name="start_passthrough" is_deprecated="true"> <return type="bool" /> <description> Starts passthrough, will return [code]false[/code] if passthrough couldn't be started. [b]Note:[/b] The viewport used for XR must have a transparent background, otherwise passthrough may not properly render. + [i]Deprecated.[/i] Set the [member environment_blend_mode] to [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND], instead. </description> </method> - <method name="stop_passthrough"> + <method name="stop_passthrough" is_deprecated="true"> <return type="void" /> <description> Stops passthrough. + [i]Deprecated.[/i] Set the [member environment_blend_mode] to [constant XRInterface.XR_ENV_BLEND_MODE_OPAQUE], instead. </description> </method> <method name="supports_play_area_mode"> diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 0a4dfd2e0d..5e70354d22 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2790,8 +2790,7 @@ void Node3DEditorViewport::_notification(int p_what) { } Transform3D t = sp->get_global_gizmo_transform(); - VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(sp); - AABB new_aabb = vi ? vi->get_aabb() : _calculate_spatial_bounds(sp); + AABB new_aabb = _calculate_spatial_bounds(sp); exist = true; if (se->last_xform == t && se->aabb == new_aabb && !se->last_xform_dirty) { @@ -2837,7 +2836,7 @@ void Node3DEditorViewport::_notification(int p_what) { last_message = message; } - message_time -= get_physics_process_delta_time(); + message_time -= get_process_delta_time(); if (message_time < 0) { surface->queue_redraw(); } @@ -4087,35 +4086,35 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const return world_pos + world_ray * FALLBACK_DISTANCE; } -AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_top_level_transform) { +AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, const Node3D *p_top_level_parent) { AABB bounds; + if (!p_top_level_parent) { + p_top_level_parent = p_parent; + } + + if (!p_parent) { + return AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4)); + } + + Transform3D xform_to_top_level_parent_space = p_top_level_parent->get_global_transform().affine_inverse() * p_parent->get_global_transform(); + const VisualInstance3D *visual_instance = Object::cast_to<VisualInstance3D>(p_parent); if (visual_instance) { bounds = visual_instance->get_aabb(); + } else { + bounds = AABB(); } + bounds = xform_to_top_level_parent_space.xform(bounds); for (int i = 0; i < p_parent->get_child_count(); i++) { Node3D *child = Object::cast_to<Node3D>(p_parent->get_child(i)); if (child) { - AABB child_bounds = _calculate_spatial_bounds(child, false); - - if (bounds.size == Vector3() && p_parent) { - bounds = child_bounds; - } else { - bounds.merge_with(child_bounds); - } + AABB child_bounds = _calculate_spatial_bounds(child, p_top_level_parent); + bounds.merge_with(child_bounds); } } - if (bounds.size == Vector3() && !p_parent) { - bounds = AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4)); - } - - if (!p_exclude_top_level_transform) { - bounds = p_parent->get_transform().xform(bounds); - } - return bounds; } diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 455376b659..ed42e8e5ab 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -434,7 +434,7 @@ private: Point2i _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const; Vector3 _get_instance_position(const Point2 &p_pos) const; - static AABB _calculate_spatial_bounds(const Node3D *p_parent, bool p_exclude_top_level_transform = true); + static AABB _calculate_spatial_bounds(const Node3D *p_parent, const Node3D *p_top_level_parent = nullptr); Node *_sanitize_preview_node(Node *p_node) const; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 4d998118e2..f177164ec2 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -674,10 +674,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (selection.size() == 1) { - undo_redo->create_action(TTR("Move Node In Parent")); + undo_redo->create_action(TTR("Move Node in Parent")); } if (selection.size() > 1) { - undo_redo->create_action(TTR("Move Nodes In Parent")); + undo_redo->create_action(TTR("Move Nodes in Parent")); } for (int i = 0; i < selection.size(); i++) { @@ -4134,7 +4134,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root")); ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene")); ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C); - ED_SHORTCUT("scene_tree/show_in_file_system", TTR("Show In FileSystem")); + ED_SHORTCUT("scene_tree/show_in_file_system", TTR("Show in FileSystem")); ED_SHORTCUT("scene_tree/toggle_unique_name", TTR("Toggle Access as Unique Name")); ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KeyModifierMask::SHIFT | Key::KEY_DELETE); ED_SHORTCUT("scene_tree/delete", TTR("Delete"), Key::KEY_DELETE); diff --git a/main/main.cpp b/main/main.cpp index 0625874520..bc2a6107b5 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -238,6 +238,11 @@ bool profile_gpu = false; static const String NULL_DISPLAY_DRIVER("headless"); static const String NULL_AUDIO_DRIVER("Dummy"); +// The length of the longest column in the command-line help we should align to +// (excluding the 2-space left and right margins). +// Currently, this is `--export-release <preset> <path>`. +static const int OPTION_COLUMN_LENGTH = 32; + /* Helper methods */ bool Main::is_cmdline_tool() { @@ -381,159 +386,241 @@ void finalize_theme_db() { #define MAIN_PRINT(m_txt) #endif +/** + * Prints a copyright notice in the command-line help with colored text. A newline is + * automatically added at the end. + */ +void Main::print_help_copyright(const char *p_notice) { + OS::get_singleton()->print("\u001b[90m%s\u001b[0m\n", p_notice); +} + +/** + * Prints a title in the command-line help with colored text. A newline is + * automatically added at beginning and at the end. + */ +void Main::print_help_title(const char *p_title) { + OS::get_singleton()->print("\n\u001b[1;93m%s:\u001b[0m\n", p_title); +} + +/** + * Returns the option string with required and optional arguments colored separately from the rest of the option. + * This color replacement must be done *after* calling `rpad()` for the length padding to be done correctly. + */ +String Main::format_help_option(const char *p_option) { + return (String(p_option) + .rpad(OPTION_COLUMN_LENGTH) + .replace("[", "\u001b[96m[") + .replace("]", "]\u001b[0m") + .replace("<", "\u001b[95m<") + .replace(">", ">\u001b[0m")); +} + +/** + * Prints an option in the command-line help with colored text. No newline is + * added at the end. `p_availability` denotes which build types the argument is + * available in. Support in release export templates implies support in debug + * export templates and editor. Support in debug export templates implies + * support in editor. + */ +void Main::print_help_option(const char *p_option, const char *p_description, CLIOptionAvailability p_availability) { + const bool option_empty = (p_option && !p_option[0]); + if (!option_empty) { + const char *availability_badge = ""; + switch (p_availability) { + case CLI_OPTION_AVAILABILITY_EDITOR: + availability_badge = "\u001b[1;91mE"; + break; + case CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG: + availability_badge = "\u001b[1;94mD"; + break; + case CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE: + availability_badge = "\u001b[1;92mR"; + break; + case CLI_OPTION_AVAILABILITY_HIDDEN: + // Use for multiline option names (but not when the option name is empty). + availability_badge = " "; + break; + } + OS::get_singleton()->print( + " \u001b[92m%s %s\u001b[0m %s", + format_help_option(p_option).utf8().ptr(), + availability_badge, + p_description); + } else { + // Make continuation lines for descriptions faint if the option name is empty. + OS::get_singleton()->print( + " \u001b[92m%s \u001b[0m \u001b[90m%s", + format_help_option(p_option).utf8().ptr(), + p_description); + } +} + void Main::print_help(const char *p_binary) { - print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE)); - OS::get_singleton()->print("Free and open source software under the terms of the MIT license.\n"); - OS::get_singleton()->print("(c) 2014-present Godot Engine contributors.\n"); - OS::get_singleton()->print("(c) 2007-2014 Juan Linietsky, Ariel Manzur.\n"); - OS::get_singleton()->print("\n"); - OS::get_singleton()->print("Usage: %s [options] [path to scene or 'project.godot' file]\n", p_binary); - OS::get_singleton()->print("\n"); + print_line("\u001b[38;5;39m" + String(VERSION_NAME) + "\u001b[0m v" + get_full_version_string() + " - \u001b[4m" + String(VERSION_WEBSITE) + "\u001b[0m"); + print_help_copyright("Free and open source software under the terms of the MIT license."); + print_help_copyright("(c) 2014-present Godot Engine contributors. (c) 2007-present Juan Linietsky, Ariel Manzur."); - OS::get_singleton()->print("General options:\n"); - OS::get_singleton()->print(" -h, --help Display this help message.\n"); - OS::get_singleton()->print(" --version Display the version string.\n"); - OS::get_singleton()->print(" -v, --verbose Use verbose stdout mode.\n"); - OS::get_singleton()->print(" -q, --quiet Quiet mode, silences stdout messages. Errors are still displayed.\n"); - OS::get_singleton()->print("\n"); + print_help_title("Usage"); + OS::get_singleton()->print(" %s \u001b[96m[options] [path to scene or \"project.godot\" file]\u001b[0m\n", p_binary); + +#if defined(TOOLS_ENABLED) + print_help_title("Option legend (this build = editor)"); +#elif defined(DEBUG_ENABLED) + print_help_title("Option legend (this build = debug export template)"); +#else + print_help_title("Option legend (this build = release export template)"); +#endif + + OS::get_singleton()->print(" \u001b[1;92mR\u001b[0m Available in editor builds, debug export templates and release export templates.\n"); +#ifdef DEBUG_ENABLED + OS::get_singleton()->print(" \u001b[1;94mD\u001b[0m Available in editor builds and debug export templates only.\n"); +#endif +#ifdef TOOLS_ENABLED + OS::get_singleton()->print(" \u001b[1;91mE\u001b[0m Only available in editor builds.\n"); +#endif - OS::get_singleton()->print("Run options:\n"); - OS::get_singleton()->print(" --, ++ Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.\n"); + print_help_title("General options"); + print_help_option("-h, --help", "Display this help message.\n"); + print_help_option("--version", "Display the version string.\n"); + print_help_option("-v, --verbose", "Use verbose stdout mode.\n"); + print_help_option("--quiet", "Quiet mode, silences stdout messages. Errors are still displayed.\n"); + + print_help_title("Run options"); + print_help_option("--, ++", "Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.\n"); #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"); + print_help_option("-e, --editor", "Start the editor instead of running the scene.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("-p, --project-manager", "Start the project manager, even if a project is auto-detected.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--debug-server <uri>", "Start the editor debug server (<protocol>://<host/IP>[:port], e.g. tcp://127.0.0.1:6007)\n", CLI_OPTION_AVAILABILITY_EDITOR); #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"); + print_help_option("--lsp-port <port>", "Use the specified port for the GDScript language server protocol. The port should be between 1025 and 49150.\n", CLI_OPTION_AVAILABILITY_EDITOR); #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"); - OS::get_singleton()->print(" --path <directory> Path to a project (<directory> must contain a 'project.godot' file).\n"); - OS::get_singleton()->print(" -u, --upwards Scan folders upwards for project.godot file.\n"); - OS::get_singleton()->print(" --main-pack <file> Path to a pack (.pck) file to load.\n"); - OS::get_singleton()->print(" --render-thread <mode> Render thread mode ['unsafe', 'safe', 'separate'].\n"); - OS::get_singleton()->print(" --remote-fs <address> Remote filesystem (<host/IP>[:<port>] address).\n"); - OS::get_singleton()->print(" --remote-fs-password <password> Password for remote filesystem.\n"); - - OS::get_singleton()->print(" --audio-driver <driver> Audio driver ["); +#endif + print_help_option("--quit", "Quit after the first iteration.\n"); + print_help_option("--quit-after <int>", "Quit after the given number of iterations. Set to 0 to disable.\n"); + print_help_option("-l, --language <locale>", "Use a specific locale (<locale> being a two-letter code).\n"); + print_help_option("--path <directory>", "Path to a project (<directory> must contain a \"project.godot\" file).\n"); + print_help_option("-u, --upwards", "Scan folders upwards for project.godot file.\n"); + print_help_option("--main-pack <file>", "Path to a pack (.pck) file to load.\n"); + print_help_option("--render-thread <mode>", "Render thread mode (\"unsafe\", \"safe\", \"separate\").\n"); + print_help_option("--remote-fs <address>", "Remote filesystem (<host/IP>[:<port>] address).\n"); + print_help_option("--remote-fs-password <password>", "Password for remote filesystem.\n"); + + print_help_option("--audio-driver <driver>", "Audio driver ["); for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) { if (i > 0) { OS::get_singleton()->print(", "); } - OS::get_singleton()->print("'%s'", AudioDriverManager::get_driver(i)->get_name()); + OS::get_singleton()->print("\"%s\"", AudioDriverManager::get_driver(i)->get_name()); } OS::get_singleton()->print("].\n"); - OS::get_singleton()->print(" --display-driver <driver> Display driver (and rendering driver) ["); + print_help_option("--display-driver <driver>", "Display driver (and rendering driver) ["); for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { if (i > 0) { OS::get_singleton()->print(", "); } - OS::get_singleton()->print("'%s' (", DisplayServer::get_create_function_name(i)); + OS::get_singleton()->print("\"%s\" (", DisplayServer::get_create_function_name(i)); Vector<String> rd = DisplayServer::get_create_function_rendering_drivers(i); for (int j = 0; j < rd.size(); j++) { if (j > 0) { OS::get_singleton()->print(", "); } - OS::get_singleton()->print("'%s'", rd[j].utf8().get_data()); + OS::get_singleton()->print("\"%s\"", rd[j].utf8().get_data()); } 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"); - OS::get_singleton()->print(" --gpu-index <device_index> Use a specific GPU (run with --verbose to get available device list).\n"); - OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping).\n"); - OS::get_singleton()->print(" --tablet-driver <driver> Pen tablet input driver.\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(" --log-file <file> Write output/error log to the specified path instead of the default location defined by the project.\n"); - OS::get_singleton()->print(" <file> path should be absolute or relative to the project directory.\n"); - OS::get_singleton()->print(" --write-movie <file> Write a video to the specified path (usually with .avi or .png extension).\n"); - OS::get_singleton()->print(" --fixed-fps is forced when enabled, but it can be used to change movie FPS.\n"); - OS::get_singleton()->print(" --disable-vsync can speed up movie writing but makes interaction more difficult.\n"); - OS::get_singleton()->print(" --quit-after can be used to specify the number of frames to write.\n"); - - OS::get_singleton()->print("\n"); - - 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"); - OS::get_singleton()->print(" -w, --windowed Request windowed mode.\n"); - OS::get_singleton()->print(" -t, --always-on-top Request an always-on-top window.\n"); - OS::get_singleton()->print(" --resolution <W>x<H> Request window resolution.\n"); - OS::get_singleton()->print(" --position <X>,<Y> Request window position (if set, screen argument is ignored).\n"); - OS::get_singleton()->print(" --screen <N> Request window screen.\n"); - OS::get_singleton()->print(" --single-window Use a single window (no separate subwindows).\n"); - OS::get_singleton()->print(" --xr-mode <mode> Select XR (Extended Reality) mode ['default', 'off', 'on'].\n"); - OS::get_singleton()->print("\n"); - - OS::get_singleton()->print("Debug options:\n"); - OS::get_singleton()->print(" -d, --debug Debug (local stdout debugger).\n"); - OS::get_singleton()->print(" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n"); - OS::get_singleton()->print(" --profiling Enable profiling in the script debugger.\n"); - OS::get_singleton()->print(" --gpu-profile Show a GPU profile of the tasks that took the most time during frame rendering.\n"); - OS::get_singleton()->print(" --gpu-validation Enable graphics API validation layers for debugging.\n"); + print_help_option("--audio-output-latency <ms>", "Override audio output latency in milliseconds (default is 15 ms).\n"); + print_help_option("", "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"); + + print_help_option("--rendering-method <renderer>", "Renderer name. Requires driver support.\n"); + print_help_option("--rendering-driver <driver>", "Rendering driver (depends on display driver).\n"); + print_help_option("--gpu-index <device_index>", "Use a specific GPU (run with --verbose to get a list of available devices).\n"); + print_help_option("--text-driver <driver>", "Text driver (used for font rendering, bidirectional support and shaping).\n"); + print_help_option("--tablet-driver <driver>", "Pen tablet input driver.\n"); + print_help_option("--headless", "Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.\n"); + print_help_option("--log-file <file>", "Write output/error log to the specified path instead of the default location defined by the project.\n"); + print_help_option("", "<file> path should be absolute or relative to the project directory.\n"); + print_help_option("--write-movie <file>", "Write a video to the specified path (usually with .avi or .png extension).\n"); + print_help_option("", "--fixed-fps is forced when enabled, but it can be used to change movie FPS.\n"); + print_help_option("", "--disable-vsync can speed up movie writing but makes interaction more difficult.\n"); + print_help_option("", "--quit-after can be used to specify the number of frames to write.\n"); + + print_help_title("Display options"); + print_help_option("-f, --fullscreen", "Request fullscreen mode.\n"); + print_help_option("-m, --maximized", "Request a maximized window.\n"); + print_help_option("-w, --windowed", "Request windowed mode.\n"); + print_help_option("-t, --always-on-top", "Request an always-on-top window.\n"); + print_help_option("--resolution <W>x<H>", "Request window resolution.\n"); + print_help_option("--position <X>,<Y>", "Request window position.\n"); + print_help_option("--screen <N>", "Request window screen.\n"); + print_help_option("--single-window", "Use a single window (no separate subwindows).\n"); + print_help_option("--xr-mode <mode>", "Select XR (Extended Reality) mode [\"default\", \"off\", \"on\"].\n"); + + print_help_title("Debug options"); + print_help_option("-d, --debug", "Debug (local stdout debugger).\n"); + print_help_option("-b, --breakpoints", "Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n"); + print_help_option("--profiling", "Enable profiling in the script debugger.\n"); + print_help_option("--gpu-profile", "Show a GPU profile of the tasks that took the most time during frame rendering.\n"); + print_help_option("--gpu-validation", "Enable graphics API validation layers for debugging.\n"); #ifdef 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"); + print_help_option("--gpu-abort", "Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); #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"); + print_help_option("--generate-spirv-debug-info", "Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n"); + print_help_option("--remote-debug <uri>", "Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n"); + print_help_option("--single-threaded-scene", "Force scene tree to run in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n"); #if defined(DEBUG_ENABLED) - OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n"); - OS::get_singleton()->print(" --debug-paths Show path lines when running the scene.\n"); - 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(" --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"); - OS::get_singleton()->print(" --disable-crash-handler Disable crash handler when supported by the platform code.\n"); - OS::get_singleton()->print(" --fixed-fps <fps> Force a fixed number of frames per second. This setting disables real-time synchronization.\n"); - OS::get_singleton()->print(" --delta-smoothing <enable> Enable or disable frame delta smoothing ['enable', 'disable'].\n"); - OS::get_singleton()->print(" --print-fps Print the frames per second to the stdout.\n"); - OS::get_singleton()->print("\n"); + print_help_option("--debug-collisions", "Show collision shapes when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); + print_help_option("--debug-paths", "Show path lines when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); + print_help_option("--debug-navigation", "Show navigation polygons when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); + print_help_option("--debug-avoidance", "Show navigation avoidance debug visuals when running the scene.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); + print_help_option("--debug-stringnames", "Print all StringName allocations to stdout when the engine quits.\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); + print_help_option("--debug-canvas-item-redraw", "Display a rectangle each time a canvas item requests a redraw (useful to troubleshoot low processor mode).\n", CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG); - 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"); +#endif + print_help_option("--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"); + print_help_option("--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"); + print_help_option("--time-scale <scale>", "Force time scale (higher values are faster, 1.0 is normal speed).\n"); + print_help_option("--disable-vsync", "Forces disabling of vertical synchronization, even if enabled in the project settings. Does not override driver-level V-Sync enforcement.\n"); + print_help_option("--disable-render-loop", "Disable render loop so rendering only occurs when called explicitly from script.\n"); + print_help_option("--disable-crash-handler", "Disable crash handler when supported by the platform code.\n"); + print_help_option("--fixed-fps <fps>", "Force a fixed number of frames per second. This setting disables real-time synchronization.\n"); + print_help_option("--delta-smoothing <enable>", "Enable or disable frame delta smoothing [\"enable\", \"disable\"].\n"); + print_help_option("--print-fps", "Print the frames per second to the stdout.\n"); + + print_help_title("Standalone tools"); + print_help_option("-s, --script <script>", "Run a script.\n"); + print_help_option("--main-loop <main_loop_name>", "Run a MainLoop specified by its global class name.\n"); + print_help_option("--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"); - 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').\n"); - OS::get_singleton()->print(" The target directory must exist.\n"); - OS::get_singleton()->print(" --export-debug <preset> <path> Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n"); - OS::get_singleton()->print(" --export-pack <preset> <path> Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n"); - OS::get_singleton()->print(" --install-android-build-template Install the android build template. Used in conjunction with --export-release or --export-debug.\n"); + print_help_option("--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", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("", "<path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. \"builds/game.exe\").\n"); + print_help_option("", "The target directory must exist.\n"); + print_help_option("--export-debug <preset> <path>", "Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--export-pack <preset> <path>", "Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--install-android-build-template", "Install the Android build template. Used in conjunction with --export-release or --export-debug.\n", CLI_OPTION_AVAILABILITY_EDITOR); #ifndef DISABLE_DEPRECATED - OS::get_singleton()->print(" --convert-3to4 [<max_file_kb>] [<max_line_size>]\n"); - OS::get_singleton()->print(" Converts project from Godot 3.x to Godot 4.x.\n"); - OS::get_singleton()->print(" --validate-conversion-3to4 [<max_file_kb>] [<max_line_size>]\n"); - OS::get_singleton()->print(" Shows what elements will be renamed when converting project from Godot 3.x to Godot 4.x.\n"); + // Commands are long; split the description to a second line. + print_help_option("--convert-3to4 ", "\n", CLI_OPTION_AVAILABILITY_HIDDEN); + print_help_option(" [max_file_kb] [max_line_size]", "Converts project from Godot 3.x to Godot 4.x.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--validate-conversion-3to4 ", "\n", CLI_OPTION_AVAILABILITY_HIDDEN); + print_help_option(" [max_file_kb] [max_line_size]", "Shows what elements will be renamed when converting project from Godot 3.x to Godot 4.x.\n", CLI_OPTION_AVAILABILITY_EDITOR); #endif // DISABLE_DEPRECATED - 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"); + print_help_option("--doctool [path]", "Dump the engine API reference to the given <path> (defaults to current directory) in XML format, merging if existing files are found.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--no-docbase", "Disallow dumping the base types (used with --doctool).\n", CLI_OPTION_AVAILABILITY_EDITOR); #ifdef MODULE_GDSCRIPT_ENABLED - OS::get_singleton()->print(" --gdscript-docs <path> Rather than dumping the engine API, generate API reference from the inline documentation in the GDScript files found in <path> (used with --doctool).\n"); + print_help_option("--gdscript-docs <path>", "Rather than dumping the engine API, generate API reference from the inline documentation in the GDScript files found in <path> (used with --doctool).\n", CLI_OPTION_AVAILABILITY_EDITOR); #endif - 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(" --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"); + print_help_option("--build-solutions", "Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--dump-gdextension-interface", "Generate a GDExtension header file \"gdextension_interface.h\" in the current folder. This file is the base file required to implement a GDExtension.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--dump-extension-api", "Generate a JSON dump of the Godot API for GDExtension bindings named \"extension_api.json\" in the current folder.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--dump-extension-api-with-docs", "Generate JSON dump of the Godot API like the previous option, but including documentation.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--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.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("", "If incompatibilities or errors are detected, the exit code will be non-zero.\n"); + print_help_option("--benchmark", "Benchmark the run time and print it to console.\n", CLI_OPTION_AVAILABILITY_EDITOR); + print_help_option("--benchmark-file <path>", "Benchmark the run time and save it to a given file in JSON format. The path should be absolute.\n", CLI_OPTION_AVAILABILITY_EDITOR); #ifdef TESTS_ENABLED - OS::get_singleton()->print(" --test [--help] Run unit tests. Use --test --help for more information.\n"); + print_help_option("--test [--help]", "Run unit tests. Use --test --help for more information.\n", CLI_OPTION_AVAILABILITY_EDITOR); #endif #endif OS::get_singleton()->print("\n"); diff --git a/main/main.h b/main/main.h index cc0655cd02..09cc0feae6 100644 --- a/main/main.h +++ b/main/main.h @@ -39,6 +39,17 @@ template <class T> class Vector; class Main { + enum CLIOptionAvailability { + CLI_OPTION_AVAILABILITY_EDITOR, + CLI_OPTION_AVAILABILITY_TEMPLATE_DEBUG, + CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE, + CLI_OPTION_AVAILABILITY_HIDDEN, + }; + + static void print_help_copyright(const char *p_notice); + static void print_help_title(const char *p_title); + static void print_help_option(const char *p_option, const char *p_description, CLIOptionAvailability p_availability = CLI_OPTION_AVAILABILITY_TEMPLATE_RELEASE); + static String format_help_option(const char *p_option); static void print_help(const char *p_binary); static uint64_t last_ticks; static uint32_t hide_print_fps_attempts; diff --git a/methods.py b/methods.py index f36591d211..c22b1f11e4 100644 --- a/methods.py +++ b/methods.py @@ -774,161 +774,6 @@ def add_to_vs_project(env, sources): env.vs_srcs += [basename + ".cpp"] -def generate_vs_project(env, original_args, project_name="godot"): - batch_file = find_visual_c_batch_file(env) - filtered_args = original_args.copy() - # Ignore the "vsproj" option to not regenerate the VS project on every build - filtered_args.pop("vsproj", None) - # The "platform" option is ignored because only the Windows platform is currently supported for VS projects - filtered_args.pop("platform", None) - # The "target" option is ignored due to the way how targets configuration is performed for VS projects (there is a separate project configuration for each target) - filtered_args.pop("target", None) - # The "progress" option is ignored as the current compilation progress indication doesn't work in VS - filtered_args.pop("progress", None) - - if batch_file: - - class ModuleConfigs(Mapping): - # This version information (Win32, x64, Debug, Release) seems to be - # required for Visual Studio to understand that it needs to generate an NMAKE - # project. Do not modify without knowing what you are doing. - PLATFORMS = ["Win32", "x64"] - PLATFORM_IDS = ["x86_32", "x86_64"] - CONFIGURATIONS = ["editor", "template_release", "template_debug"] - DEV_SUFFIX = ".dev" if env["dev_build"] else "" - - @staticmethod - def for_every_variant(value): - return [value for _ in range(len(ModuleConfigs.CONFIGURATIONS) * len(ModuleConfigs.PLATFORMS))] - - def __init__(self): - shared_targets_array = [] - self.names = [] - self.arg_dict = { - "variant": [], - "runfile": shared_targets_array, - "buildtarget": shared_targets_array, - "cpppaths": [], - "cppdefines": [], - "cmdargs": [], - } - self.add_mode() # default - - def add_mode( - self, - name: str = "", - includes: str = "", - cli_args: str = "", - defines=None, - ): - if defines is None: - defines = [] - self.names.append(name) - self.arg_dict["variant"] += [ - f'{config}{f"_[{name}]" if name else ""}|{platform}' - for config in ModuleConfigs.CONFIGURATIONS - for platform in ModuleConfigs.PLATFORMS - ] - self.arg_dict["runfile"] += [ - f'bin\\godot.windows.{config}{ModuleConfigs.DEV_SUFFIX}{".double" if env["precision"] == "double" else ""}.{plat_id}{f".{name}" if name else ""}.exe' - for config in ModuleConfigs.CONFIGURATIONS - for plat_id in ModuleConfigs.PLATFORM_IDS - ] - self.arg_dict["cpppaths"] += ModuleConfigs.for_every_variant(env["CPPPATH"] + [includes]) - self.arg_dict["cppdefines"] += ModuleConfigs.for_every_variant(list(env["CPPDEFINES"]) + defines) - self.arg_dict["cmdargs"] += ModuleConfigs.for_every_variant(cli_args) - - def build_commandline(self, commands): - configuration_getter = ( - "$(Configuration" - + "".join([f'.Replace("{name}", "")' for name in self.names[1:]]) - + '.Replace("_[]", "")' - + ")" - ) - - common_build_prefix = [ - 'cmd /V /C set "plat=$(PlatformTarget)"', - '(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))', - 'call "' + batch_file + '" !plat!', - ] - - # Windows allows us to have spaces in paths, so we need - # to double quote off the directory. However, the path ends - # in a backslash, so we need to remove this, lest it escape the - # last double quote off, confusing MSBuild - common_build_postfix = [ - "--directory=\"$(ProjectDir.TrimEnd('\\'))\"", - "platform=windows", - f"target={configuration_getter}", - "progress=no", - ] - - for arg, value in filtered_args.items(): - common_build_postfix.append(f"{arg}={value}") - - result = " ^& ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)]) - return result - - # Mappings interface definitions - - def __iter__(self) -> Iterator[str]: - for x in self.arg_dict: - yield x - - def __len__(self) -> int: - return len(self.names) - - def __getitem__(self, k: str): - return self.arg_dict[k] - - add_to_vs_project(env, env.core_sources) - add_to_vs_project(env, env.drivers_sources) - add_to_vs_project(env, env.main_sources) - add_to_vs_project(env, env.modules_sources) - add_to_vs_project(env, env.scene_sources) - add_to_vs_project(env, env.servers_sources) - if env["tests"]: - add_to_vs_project(env, env.tests_sources) - if env.editor_build: - add_to_vs_project(env, env.editor_sources) - - for header in glob_recursive("**/*.h"): - env.vs_incs.append(str(header)) - - module_configs = ModuleConfigs() - - if env.get("module_mono_enabled"): - mono_defines = [("GD_MONO_HOT_RELOAD",)] if env.editor_build else [] - module_configs.add_mode( - "mono", - cli_args="module_mono_enabled=yes", - defines=mono_defines, - ) - - scons_cmd = "scons" - - path_to_venv = os.getenv("VIRTUAL_ENV") - path_to_scons_exe = Path(str(path_to_venv)) / "Scripts" / "scons.exe" - if path_to_venv and path_to_scons_exe.exists(): - scons_cmd = str(path_to_scons_exe) - - env["MSVSBUILDCOM"] = module_configs.build_commandline(scons_cmd) - env["MSVSREBUILDCOM"] = module_configs.build_commandline(f"{scons_cmd} vsproj=yes") - env["MSVSCLEANCOM"] = module_configs.build_commandline(f"{scons_cmd} --clean") - if not env.get("MSVS"): - env["MSVS"]["PROJECTSUFFIX"] = ".vcxproj" - env["MSVS"]["SOLUTIONSUFFIX"] = ".sln" - env.MSVSProject( - target=["#" + project_name + env["MSVSPROJECTSUFFIX"]], - incs=env.vs_incs, - srcs=env.vs_srcs, - auto_build_solution=1, - **module_configs, - ) - else: - print("Could not locate Visual Studio batch file to set up the build environment. Not generating VS project.") - - def precious_program(env, program, sources, **args): program = env.ProgramOriginal(program, sources, **args) env.Precious(program) @@ -1229,3 +1074,456 @@ def dump(env): with open(".scons_env.json", "w") as f: dump(env.Dictionary(), f, indent=4, default=non_serializable) + + +# Custom Visual Studio project generation logic that supports any platform that has a msvs.py +# script, so Visual Studio can be used to run scons for any platform, with the right defines per target. +# Invoked with scons vsproj=yes +# +# Only platforms that opt in to vs proj generation by having a msvs.py file in the platform folder are included. +# Platforms with a msvs.py file will be added to the solution, but only the current active platform+target+arch +# will have a build configuration generated, because we only know what the right defines/includes/flags/etc are +# on the active build target. +# +# Platforms that don't support an editor target will have a dummy editor target that won't do anything on build, +# but will have the files and configuration for the windows editor target. +# +# To generate build configuration files for all platforms+targets+arch combinations, users can call +# scons vsproj=yes +# for each combination of platform+target+arch. This will generate the relevant vs project files but +# skip the build process. This lets project files be quickly generated even if there are build errors. +# +# To generate AND build from the command line: +# scons vsproj=yes vsproj_gen_only=yes +def generate_vs_project(env, original_args, project_name="godot"): + # Augmented glob_recursive that also fills the dirs argument with traversed directories that have content. + def glob_recursive_2(pattern, dirs, node="."): + from SCons import Node + from SCons.Script import Glob + + results = [] + for f in Glob(str(node) + "/*", source=True): + if type(f) is Node.FS.Dir: + results += glob_recursive_2(pattern, dirs, f) + r = Glob(str(node) + "/" + pattern, source=True) + if len(r) > 0 and not str(node) in dirs: + d = "" + for part in str(node).split("\\"): + d += part + if not d in dirs: + dirs.append(d) + d += "\\" + results += r + return results + + def get_bool(args, option, default): + from SCons.Variables.BoolVariable import _text2bool + + val = args.get(option, default) + if val is not None: + try: + return _text2bool(val) + except: + return default + else: + return default + + def format_key_value(v): + if type(v) in [tuple, list]: + return v[0] if len(v) == 1 else f"{v[0]}={v[1]}" + return v + + filtered_args = original_args.copy() + + # Ignore the "vsproj" option to not regenerate the VS project on every build + filtered_args.pop("vsproj", None) + + # This flag allows users to regenerate the proj files but skip the building process. + # This lets projects be regenerated even if there are build errors. + filtered_args.pop("vsproj_gen_only", None) + + # The "progress" option is ignored as the current compilation progress indication doesn't work in VS + filtered_args.pop("progress", None) + + # We add these three manually because they might not be explicitly passed in, and it's important to always set them. + filtered_args.pop("platform", None) + filtered_args.pop("target", None) + filtered_args.pop("arch", None) + + platform = env["platform"] + target = env["target"] + arch = env["arch"] + + vs_configuration = {} + common_build_prefix = [] + confs = [] + for x in sorted(glob.glob("platform/*")): + # Only platforms that opt in to vs proj generation are included. + if not os.path.isdir(x) or not os.path.exists(x + "/msvs.py"): + continue + tmppath = "./" + x + sys.path.insert(0, tmppath) + import msvs + + vs_plats = [] + vs_confs = [] + try: + platform_name = x[9:] + vs_plats = msvs.get_platforms() + vs_confs = msvs.get_configurations() + val = [] + for plat in vs_plats: + val += [{"platform": plat[0], "architecture": plat[1]}] + + vsconf = {"platform": platform_name, "targets": vs_confs, "arches": val} + confs += [vsconf] + + # Save additional information about the configuration for the actively selected platform, + # so we can generate the platform-specific props file with all the build commands/defines/etc + if platform == platform_name: + common_build_prefix = msvs.get_build_prefix(env) + vs_configuration = vsconf + except Exception: + pass + + sys.path.remove(tmppath) + sys.modules.pop("msvs") + + headers = [] + headers_dirs = [] + for file in glob_recursive_2("*.h", headers_dirs): + headers.append(str(file).replace("/", "\\")) + for file in glob_recursive_2("*.hpp", headers_dirs): + headers.append(str(file).replace("/", "\\")) + + sources = [] + sources_dirs = [] + for file in glob_recursive_2("*.cpp", sources_dirs): + sources.append(str(file).replace("/", "\\")) + for file in glob_recursive_2("*.c", sources_dirs): + sources.append(str(file).replace("/", "\\")) + + others = [] + others_dirs = [] + for file in glob_recursive_2("*.natvis", others_dirs): + others.append(str(file).replace("/", "\\")) + for file in glob_recursive_2("*.glsl", others_dirs): + others.append(str(file).replace("/", "\\")) + + skip_filters = False + import hashlib + import json + + md5 = hashlib.md5( + json.dumps(headers + headers_dirs + sources + sources_dirs + others + others_dirs, sort_keys=True).encode( + "utf-8" + ) + ).hexdigest() + + if os.path.exists(f"{project_name}.vcxproj.filters"): + existing_filters = open(f"{project_name}.vcxproj.filters", "r").read() + match = re.search(r"(?ms)^<!-- CHECKSUM$.([0-9a-f]{32})", existing_filters) + if match is not None and md5 == match.group(1): + skip_filters = True + + import uuid + + # Don't regenerate the filters file if nothing has changed, so we keep the existing UUIDs. + if not skip_filters: + print(f"Regenerating {project_name}.vcxproj.filters") + + filters_template = open("misc/msvs/vcxproj.filters.template", "r").read() + for i in range(1, 10): + filters_template = filters_template.replace(f"%%UUID{i}%%", str(uuid.uuid4())) + + filters = "" + + for d in headers_dirs: + filters += f'<Filter Include="Header Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n' + for d in sources_dirs: + filters += f'<Filter Include="Source Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n' + for d in others_dirs: + filters += f'<Filter Include="Other Files\\{d}"><UniqueIdentifier>{{{str(uuid.uuid4())}}}</UniqueIdentifier></Filter>\n' + + filters_template = filters_template.replace("%%FILTERS%%", filters) + + filters = "" + for file in headers: + filters += ( + f'<ClInclude Include="{file}"><Filter>Header Files\\{os.path.dirname(file)}</Filter></ClInclude>\n' + ) + filters_template = filters_template.replace("%%INCLUDES%%", filters) + + filters = "" + for file in sources: + filters += ( + f'<ClCompile Include="{file}"><Filter>Source Files\\{os.path.dirname(file)}</Filter></ClCompile>\n' + ) + + filters_template = filters_template.replace("%%COMPILES%%", filters) + + filters = "" + for file in others: + filters += f'<None Include="{file}"><Filter>Other Files\\{os.path.dirname(file)}</Filter></None>\n' + filters_template = filters_template.replace("%%OTHERS%%", filters) + + filters_template = filters_template.replace("%%HASH%%", md5) + + with open(f"{project_name}.vcxproj.filters", "w") as f: + f.write(filters_template) + + envsources = [] + + envsources += env.core_sources + envsources += env.drivers_sources + envsources += env.main_sources + envsources += env.modules_sources + envsources += env.scene_sources + envsources += env.servers_sources + if env.editor_build: + envsources += env.editor_sources + envsources += env.platform_sources + + headers_active = [] + sources_active = [] + others_active = [] + for x in envsources: + fname = "" + if type(x) == type(""): + fname = env.File(x).path + else: + # Some object files might get added directly as a File object and not a list. + try: + fname = env.File(x)[0].path + except: + fname = x.path + pass + + if fname: + fname = fname.replace("\\\\", "/") + parts = os.path.splitext(fname) + basename = parts[0] + ext = parts[1] + idx = fname.find(env["OBJSUFFIX"]) + if ext in [".h", ".hpp"]: + headers_active += [fname] + elif ext in [".c", ".cpp"]: + sources_active += [fname] + elif idx > 0: + basename = fname[:idx] + if os.path.isfile(basename + ".h"): + headers_active += [basename + ".h"] + elif os.path.isfile(basename + ".hpp"): + headers_active += [basename + ".hpp"] + elif basename.endswith(".gen") and os.path.isfile(basename[:-4] + ".h"): + headers_active += [basename[:-4] + ".h"] + if os.path.isfile(basename + ".c"): + sources_active += [basename + ".c"] + elif os.path.isfile(basename + ".cpp"): + sources_active += [basename + ".cpp"] + else: + fname = os.path.relpath(os.path.abspath(fname), env.Dir("").abspath) + others_active += [fname] + + all_items = [] + properties = [] + activeItems = [] + extraItems = [] + + set_headers = set(headers_active) + set_sources = set(sources_active) + set_others = set(others_active) + for file in headers: + all_items.append(f'<ClInclude Include="{file}">') + all_items.append( + f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>" + ) + all_items.append("</ClInclude>") + if file in set_headers: + activeItems.append(file) + + for file in sources: + all_items.append(f'<ClCompile Include="{file}">') + all_items.append( + f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>" + ) + all_items.append("</ClCompile>") + if file in set_sources: + activeItems.append(file) + + for file in others: + all_items.append(f'<None Include="{file}">') + all_items.append( + f" <ExcludedFromBuild Condition=\"!$(ActiveProjectItemList.Contains(';{file};'))\">true</ExcludedFromBuild>" + ) + all_items.append("</None>") + if file in set_others: + activeItems.append(file) + + if vs_configuration: + vsconf = "" + for a in vs_configuration["arches"]: + if arch == a["architecture"]: + vsconf = f'{target}|{a["platform"]}' + break + + condition = "'$(Configuration)|$(Platform)'=='" + vsconf + "'" + properties.append("<ActiveProjectItemList>;" + ";".join(activeItems) + ";</ActiveProjectItemList>") + output = f'bin\\godot{env["PROGSUFFIX"]}' + + props_template = open("misc/msvs/props.template", "r").read() + + props_template = props_template.replace("%%VSCONF%%", vsconf) + props_template = props_template.replace("%%CONDITION%%", condition) + props_template = props_template.replace("%%PROPERTIES%%", "\n ".join(properties)) + props_template = props_template.replace("%%EXTRA_ITEMS%%", "\n ".join(extraItems)) + + props_template = props_template.replace("%%OUTPUT%%", output) + + props_template = props_template.replace( + "%%DEFINES%%", ";".join([format_key_value(v) for v in list(env["CPPDEFINES"])]) + ) + props_template = props_template.replace("%%INCLUDES%%", ";".join([str(j) for j in env["CPPPATH"]])) + props_template = props_template.replace( + "%%OPTIONS%%", + " ".join(env["CCFLAGS"]) + " " + " ".join([x for x in env["CXXFLAGS"] if not x.startswith("$")]), + ) + + # Windows allows us to have spaces in paths, so we need + # to double quote off the directory. However, the path ends + # in a backslash, so we need to remove this, lest it escape the + # last double quote off, confusing MSBuild + common_build_postfix = [ + "--directory="$(ProjectDir.TrimEnd('\\'))"", + "progress=no", + f"platform={platform}", + f"target={target}", + f"arch={arch}", + ] + + for arg, value in filtered_args.items(): + common_build_postfix.append(f"{arg}={value}") + + cmd_rebuild = [ + "vsproj=yes", + f"vsproj_name={project_name}", + ] + common_build_postfix + + cmd_clean = [ + "--clean", + ] + common_build_postfix + + commands = "scons" + if len(common_build_prefix) == 0: + commands = "echo Starting SCons && cmd /V /C " + commands + else: + common_build_prefix[0] = "echo Starting SCons && cmd /V /C " + common_build_prefix[0] + + cmd = " ^& ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)]) + props_template = props_template.replace("%%BUILD%%", cmd) + + cmd = " ^& ".join(common_build_prefix + [" ".join([commands] + cmd_rebuild)]) + props_template = props_template.replace("%%REBUILD%%", cmd) + + cmd = " ^& ".join(common_build_prefix + [" ".join([commands] + cmd_clean)]) + props_template = props_template.replace("%%CLEAN%%", cmd) + + with open(f"{project_name}.{platform}.{target}.{arch}.generated.props", "w") as f: + f.write(props_template) + + proj_uuid = str(uuid.uuid4()) + sln_uuid = str(uuid.uuid4()) + + if os.path.exists(f"{project_name}.sln"): + for line in open(f"{project_name}.sln", "r").read().splitlines(): + if line.startswith('Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")'): + proj_uuid = re.search( + r"\"{(\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b)}\"$", + line, + ).group(1) + elif line.strip().startswith("SolutionGuid ="): + sln_uuid = re.search( + r"{(\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b)}", line + ).group(1) + break + + configurations = [] + imports = [] + properties = [] + section1 = [] + section2 = [] + for conf in confs: + godot_platform = conf["platform"] + for p in conf["arches"]: + sln_plat = p["platform"] + proj_plat = sln_plat + godot_arch = p["architecture"] + + # Redirect editor configurations for non-Windows platforms to the Windows one, so the solution has all the permutations + # and VS doesn't complain about missing project configurations. + # These configurations are disabled, so they show up but won't build. + if godot_platform != "windows": + section1 += [f"editor|{sln_plat} = editor|{proj_plat}"] + section2 += [ + f"{{{proj_uuid}}}.editor|{proj_plat}.ActiveCfg = editor|{proj_plat}", + ] + + for t in conf["targets"]: + godot_target = t + + # Windows x86 is a special little flower that requires a project platform == Win32 but a solution platform == x86. + if godot_platform == "windows" and godot_target == "editor" and godot_arch == "x86_32": + sln_plat = "x86" + + configurations += [ + f'<ProjectConfiguration Include="{godot_target}|{proj_plat}">', + f" <Configuration>{godot_target}</Configuration>", + f" <Platform>{proj_plat}</Platform>", + "</ProjectConfiguration>", + ] + + if godot_platform != "windows": + configurations += [ + f'<ProjectConfiguration Include="editor|{proj_plat}">', + f" <Configuration>editor</Configuration>", + f" <Platform>{proj_plat}</Platform>", + "</ProjectConfiguration>", + ] + + p = f"{project_name}.{godot_platform}.{godot_target}.{godot_arch}.generated.props" + imports += [ + f'<Import Project="$(MSBuildProjectDirectory)\\{p}" Condition="Exists(\'$(MSBuildProjectDirectory)\\{p}\')"/>' + ] + + section1 += [f"{godot_target}|{sln_plat} = {godot_target}|{sln_plat}"] + + section2 += [ + f"{{{proj_uuid}}}.{godot_target}|{sln_plat}.ActiveCfg = {godot_target}|{proj_plat}", + f"{{{proj_uuid}}}.{godot_target}|{sln_plat}.Build.0 = {godot_target}|{proj_plat}", + ] + + section1 = sorted(section1) + section2 = sorted(section2) + + proj_template = open("misc/msvs/vcxproj.template", "r").read() + + proj_template = proj_template.replace("%%UUID%%", proj_uuid) + proj_template = proj_template.replace("%%CONFS%%", "\n ".join(configurations)) + proj_template = proj_template.replace("%%IMPORTS%%", "\n ".join(imports)) + proj_template = proj_template.replace("%%DEFAULT_ITEMS%%", "\n ".join(all_items)) + proj_template = proj_template.replace("%%PROPERTIES%%", "\n ".join(properties)) + + with open(f"{project_name}.vcxproj", "w") as f: + f.write(proj_template) + + sln_template = open("misc/msvs/sln.template", "r").read() + sln_template = sln_template.replace("%%NAME%%", project_name) + sln_template = sln_template.replace("%%UUID%%", proj_uuid) + sln_template = sln_template.replace("%%SLNUUID%%", sln_uuid) + sln_template = sln_template.replace("%%SECTION1%%", "\n ".join(section1)) + sln_template = sln_template.replace("%%SECTION2%%", "\n ".join(section2)) + with open(f"{project_name}.sln", "w") as f: + f.write(sln_template) + + if get_bool(original_args, "vsproj_gen_only", True): + sys.exit() diff --git a/misc/msvs/props.template b/misc/msvs/props.template new file mode 100644 index 0000000000..9ecd49a25e --- /dev/null +++ b/misc/msvs/props.template @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='%%VSCONF%%'"> + <NMakeBuildCommandLine>%%BUILD%%</NMakeBuildCommandLine> + <NMakeReBuildCommandLine>%%REBUILD%%</NMakeReBuildCommandLine> + <NMakeCleanCommandLine>%%CLEAN%%</NMakeCleanCommandLine> + <NMakeOutput>%%OUTPUT%%</NMakeOutput> + <NMakePreprocessorDefinitions>%%DEFINES%%</NMakePreprocessorDefinitions> + <NMakeIncludeSearchPath>%%INCLUDES%%</NMakeIncludeSearchPath> + <NMakeForcedIncludes>$(NMakeForcedIncludes)</NMakeForcedIncludes> + <NMakeAssemblySearchPath>$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath> + <NMakeForcedUsingAssemblies>$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies> + <AdditionalOptions>%%OPTIONS%%</AdditionalOptions> + </PropertyGroup> + <PropertyGroup Condition="%%CONDITION%%"> + %%PROPERTIES%% + </PropertyGroup> + <ItemGroup Condition="%%CONDITION%%"> + %%EXTRA_ITEMS%% + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/misc/msvs/sln.template b/misc/msvs/sln.template new file mode 100644 index 0000000000..7d05548c6e --- /dev/null +++ b/misc/msvs/sln.template @@ -0,0 +1,20 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34221.43 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%%NAME%%", "%%NAME%%.vcxproj", "{%%UUID%%}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + %%SECTION1%% + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + %%SECTION2%% + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {%%SLNUUID%%} + EndGlobalSection +EndGlobal diff --git a/misc/msvs/vcxproj.filters.template b/misc/msvs/vcxproj.filters.template new file mode 100644 index 0000000000..d57eeee811 --- /dev/null +++ b/misc/msvs/vcxproj.filters.template @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>%%UUID1%%</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>%%UUID2%%</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>%%UUID3%%</UniqueIdentifier> + </Filter> + <Filter Include="Scripts"> + <UniqueIdentifier>%%UUID4%%</UniqueIdentifier> + </Filter> + %%FILTERS%% + </ItemGroup> + <ItemGroup> + %%COMPILES%% + </ItemGroup> + <ItemGroup> + %%INCLUDES%% + </ItemGroup> + <ItemGroup> + %%OTHERS%% + </ItemGroup> +</Project> +<!-- CHECKSUM +%%HASH%% +-->
\ No newline at end of file diff --git a/misc/msvs/vcxproj.template b/misc/msvs/vcxproj.template new file mode 100644 index 0000000000..a1cf22bfb9 --- /dev/null +++ b/misc/msvs/vcxproj.template @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + %%CONFS%% + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{%%UUID%%}</ProjectGuid> + <RootNamespace>godot</RootNamespace> + <Keyword>MakeFileProj</Keyword> + <VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName> + </PropertyGroup> + <PropertyGroup> + %%PROPERTIES%% + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration"> + <ConfigurationType>Makefile</ConfigurationType> + <UseOfMfc>false</UseOfMfc> + <PlatformToolset>v143</PlatformToolset> + <OutDir>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutDir> + <IntDir>obj\$(Platform)\$(Configuration)\</IntDir> + <LayoutDir>$(OutDir)\Layout</LayoutDir> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + <ActiveProjectItemList></ActiveProjectItemList> + </PropertyGroup> + %%IMPORTS%% + <ItemGroup Condition="'$(IncludeListImported)'==''"> + %%DEFAULT_ITEMS%% + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/misc/scripts/black_format.sh b/misc/scripts/black_format.sh index 3a64284eb6..48dc14c734 100755 --- a/misc/scripts/black_format.sh +++ b/misc/scripts/black_format.sh @@ -20,7 +20,7 @@ fi # A diff has been created, notify the user, clean up, and exit. printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" # Perl commands replace trailing spaces with `·` and tabs with `<TAB>`. -printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' +printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n" exit 1 diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh index 40d94d4276..8b59519606 100755 --- a/misc/scripts/clang_format.sh +++ b/misc/scripts/clang_format.sh @@ -47,7 +47,7 @@ fi # A diff has been created, notify the user, clean up, and exit. printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" # Perl commands replace trailing spaces with `·` and tabs with `<TAB>`. -printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' +printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n" exit 1 diff --git a/misc/scripts/clang_tidy.sh b/misc/scripts/clang_tidy.sh index c4811b903c..0c6998b491 100755 --- a/misc/scripts/clang_tidy.sh +++ b/misc/scripts/clang_tidy.sh @@ -27,7 +27,7 @@ fi # A diff has been created, notify the user, clean up, and exit. printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" # Perl commands replace trailing spaces with `·` and tabs with `<TAB>`. -printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' +printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n" exit 1 diff --git a/misc/scripts/dotnet_format.sh b/misc/scripts/dotnet_format.sh index cac00f5cb1..e2b4ba5e43 100755 --- a/misc/scripts/dotnet_format.sh +++ b/misc/scripts/dotnet_format.sh @@ -31,7 +31,7 @@ fi # A diff has been created, notify the user, clean up, and exit. printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" # Perl commands replace trailing spaces with `·` and tabs with `<TAB>`. -printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' +printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n" exit 1 diff --git a/misc/scripts/file_format.sh b/misc/scripts/file_format.sh index 94a3affbd7..ad58657883 100755 --- a/misc/scripts/file_format.sh +++ b/misc/scripts/file_format.sh @@ -82,7 +82,7 @@ then # A diff has been created, notify the user, clean up, and exit. printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" # Perl commands replace trailing spaces with `·` and tabs with `<TAB>`. - printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' + printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' fi printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n" diff --git a/misc/scripts/header_guards.sh b/misc/scripts/header_guards.sh index ce0b3f334d..a79ccd4bee 100755 --- a/misc/scripts/header_guards.sh +++ b/misc/scripts/header_guards.sh @@ -81,7 +81,7 @@ fi # A diff has been created, notify the user, clean up, and exit. printf "\n\e[1;33m*** The following changes must be made to comply with the formatting rules:\e[0m\n\n" # Perl commands replace trailing spaces with `·` and tabs with `<TAB>`. -printf "$diff\n" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' +printf "%s\n" "$diff" | perl -pe 's/(.*[^ ])( +)(\e\[m)$/my $spaces="·" x length($2); sprintf("$1$spaces$3")/ge' | perl -pe 's/(.*[^\t])(\t+)(\e\[m)$/my $tabs="<TAB>" x length($2); sprintf("$1$tabs$3")/ge' printf "\n\e[1;91m*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\e[0m\n" exit 1 diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp index 12b4ac540d..02e3a11964 100644 --- a/modules/multiplayer/multiplayer_synchronizer.cpp +++ b/modules/multiplayer/multiplayer_synchronizer.cpp @@ -49,11 +49,11 @@ void MultiplayerSynchronizer::_stop() { } #endif root_node_cache = ObjectID(); - reset(); Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; if (node) { get_multiplayer()->object_configuration_remove(node, this); } + reset(); } void MultiplayerSynchronizer::_start() { diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp index 31307e648d..35ff62dd06 100644 --- a/modules/multiplayer/scene_replication_interface.cpp +++ b/modules/multiplayer/scene_replication_interface.cpp @@ -189,9 +189,6 @@ void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) { spawned_nodes.insert(oid); if (_has_authority(spawner)) { - if (tobj.net_id == 0) { - tobj.net_id = ++last_net_id; - } _update_spawn_visibility(0, oid); } } @@ -249,6 +246,7 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c uint32_t net_id = pending_sync_net_ids[0]; pending_sync_net_ids.pop_front(); peers_info[pending_spawn_remote].recv_sync_ids[net_id] = sync->get_instance_id(); + sync->set_net_id(net_id); // Try to apply spawn state (before ready). if (pending_buffer_size > 0) { @@ -472,9 +470,13 @@ Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpa ERR_FAIL_COND_V(!multiplayer || !p_node || !p_spawner, ERR_BUG); const ObjectID oid = p_node->get_instance_id(); - const TrackedNode *tnode = tracked_nodes.getptr(oid); + TrackedNode *tnode = tracked_nodes.getptr(oid); ERR_FAIL_NULL_V(tnode, ERR_INVALID_PARAMETER); + if (tnode->net_id == 0) { + // Ensure the node has an ID. + tnode->net_id = ++last_net_id; + } uint32_t nid = tnode->net_id; ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED); diff --git a/modules/openxr/doc_classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml index e795311651..1bc3a1a3fc 100644 --- a/modules/openxr/doc_classes/OpenXRAPIExtension.xml +++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml @@ -75,6 +75,12 @@ Returns the id of the system, which is a [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html]XrSystemId[/url] cast to an integer. </description> </method> + <method name="is_environment_blend_mode_alpha_supported"> + <return type="int" enum="OpenXRAPIExtension.OpenXRAlphaBlendModeSupport" /> + <description> + Returns [enum OpenXRAPIExtension.OpenXRAlphaBlendModeSupport] denoting if [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is really support, emulated or not supported at all. + </description> + </method> <method name="is_initialized"> <return type="bool" /> <description> @@ -94,6 +100,20 @@ Returns [code]true[/code] if OpenXR is enabled. </description> </method> + <method name="register_composition_layer_provider"> + <return type="void" /> + <param index="0" name="extension" type="OpenXRExtensionWrapperExtension" /> + <description> + Registers the given extension as a composition layer provider. + </description> + </method> + <method name="set_emulate_environment_blend_mode_alpha_blend"> + <return type="void" /> + <param index="0" name="enabled" type="bool" /> + <description> + If set to [code]true[/code], an OpenXR extension is loaded which is capable of emulating the [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] blend mode. + </description> + </method> <method name="transform_from_pose"> <return type="Transform3D" /> <param index="0" name="pose" type="const void*" /> @@ -101,6 +121,13 @@ Creates a [Transform3D] from an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html]XrPosef[/url]. </description> </method> + <method name="unregister_composition_layer_provider"> + <return type="void" /> + <param index="0" name="extension" type="OpenXRExtensionWrapperExtension" /> + <description> + Unregisters the given extension as a composition layer provider. + </description> + </method> <method name="xr_result"> <return type="bool" /> <param index="0" name="result" type="int" /> @@ -111,4 +138,15 @@ </description> </method> </methods> + <constants> + <constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE" value="0" enum="OpenXRAlphaBlendModeSupport"> + Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] isn't supported at all. + </constant> + <constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL" value="1" enum="OpenXRAlphaBlendModeSupport"> + Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is really supported. + </constant> + <constant name="OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING" value="2" enum="OpenXRAlphaBlendModeSupport"> + Means that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is emulated. + </constant> + </constants> </class> diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml index fa93704a0a..b923d9244d 100644 --- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml +++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml @@ -9,6 +9,12 @@ <tutorials> </tutorials> <methods> + <method name="_get_composition_layer" qualifiers="virtual"> + <return type="int" /> + <description> + Returns a pointer to a [code]XrCompositionLayerBaseHeader[/code] struct to provide a composition layer. This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider]. + </description> + </method> <method name="_get_requested_extensions" qualifiers="virtual"> <return type="Dictionary" /> <description> diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp index 23238cabb9..5ad7a97eca 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp @@ -39,6 +39,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() { GDVIRTUAL_BIND(_set_session_create_and_get_next_pointer, "next_pointer"); GDVIRTUAL_BIND(_set_swapchain_create_info_and_get_next_pointer, "next_pointer"); GDVIRTUAL_BIND(_set_hand_joint_locations_and_get_next_pointer, "hand_index", "next_pointer"); + GDVIRTUAL_BIND(_get_composition_layer); GDVIRTUAL_BIND(_on_register_metadata); GDVIRTUAL_BIND(_on_before_instance_created); GDVIRTUAL_BIND(_on_instance_created, "instance"); @@ -128,6 +129,16 @@ void *OpenXRExtensionWrapperExtension::set_hand_joint_locations_and_get_next_poi return nullptr; } +XrCompositionLayerBaseHeader *OpenXRExtensionWrapperExtension::get_composition_layer() { + uint64_t pointer; + + if (GDVIRTUAL_CALL(_get_composition_layer, pointer)) { + return reinterpret_cast<XrCompositionLayerBaseHeader *>(pointer); + } + + return nullptr; +} + void OpenXRExtensionWrapperExtension::on_register_metadata() { GDVIRTUAL_CALL(_on_register_metadata); } diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h index 6acf229e16..4d8b19f4fd 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h @@ -39,7 +39,7 @@ #include "core/os/thread_safe.h" #include "core/variant/native_ptr.h" -class OpenXRExtensionWrapperExtension : public Object, OpenXRExtensionWrapper { +class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider { GDCLASS(OpenXRExtensionWrapperExtension, Object); protected: @@ -59,6 +59,7 @@ public: virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override; virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override; virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) override; + virtual XrCompositionLayerBaseHeader *get_composition_layer() override; //TODO workaround as GDExtensionPtr<void> return type results in build error in godot-cpp GDVIRTUAL1R(uint64_t, _set_system_properties_and_get_next_pointer, GDExtensionPtr<void>); @@ -66,6 +67,7 @@ public: GDVIRTUAL1R(uint64_t, _set_session_create_and_get_next_pointer, GDExtensionPtr<void>); GDVIRTUAL1R(uint64_t, _set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>); GDVIRTUAL2R(uint64_t, _set_hand_joint_locations_and_get_next_pointer, int, GDExtensionPtr<void>); + GDVIRTUAL0R(uint64_t, _get_composition_layer); virtual void on_register_metadata() override; virtual void on_before_instance_created() override; diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp deleted file mode 100644 index 3da0ffd9c7..0000000000 --- a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/**************************************************************************/ -/* openxr_fb_passthrough_extension_wrapper.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "openxr_fb_passthrough_extension_wrapper.h" - -#include "core/os/os.h" -#include "scene/main/viewport.h" -#include "scene/main/window.h" - -using namespace godot; - -OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::singleton = nullptr; - -OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::get_singleton() { - return singleton; -} - -OpenXRFbPassthroughExtensionWrapper::OpenXRFbPassthroughExtensionWrapper() { - singleton = this; -} - -OpenXRFbPassthroughExtensionWrapper::~OpenXRFbPassthroughExtensionWrapper() { - cleanup(); -} - -HashMap<String, bool *> OpenXRFbPassthroughExtensionWrapper::get_requested_extensions() { - HashMap<String, bool *> request_extensions; - - request_extensions[XR_FB_PASSTHROUGH_EXTENSION_NAME] = &fb_passthrough_ext; - request_extensions[XR_FB_TRIANGLE_MESH_EXTENSION_NAME] = &fb_triangle_mesh_ext; - - return request_extensions; -} - -void OpenXRFbPassthroughExtensionWrapper::cleanup() { - fb_passthrough_ext = false; - fb_triangle_mesh_ext = false; -} - -Viewport *OpenXRFbPassthroughExtensionWrapper::get_main_viewport() { - MainLoop *main_loop = OS::get_singleton()->get_main_loop(); - if (!main_loop) { - print_error("Unable to retrieve main loop"); - return nullptr; - } - - SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop); - if (!scene_tree) { - print_error("Unable to retrieve scene tree"); - return nullptr; - } - - Viewport *viewport = scene_tree->get_root()->get_viewport(); - return viewport; -} - -void OpenXRFbPassthroughExtensionWrapper::on_instance_created(const XrInstance instance) { - if (fb_passthrough_ext) { - bool result = initialize_fb_passthrough_extension(instance); - if (!result) { - print_error("Failed to initialize fb_passthrough extension"); - fb_passthrough_ext = false; - } - } - - if (fb_triangle_mesh_ext) { - bool result = initialize_fb_triangle_mesh_extension(instance); - if (!result) { - print_error("Failed to initialize fb_triangle_mesh extension"); - fb_triangle_mesh_ext = false; - } - } - - if (fb_passthrough_ext) { - OpenXRAPI::get_singleton()->register_composition_layer_provider(this); - } -} - -bool OpenXRFbPassthroughExtensionWrapper::is_passthrough_enabled() { - return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && passthrough_layer != XR_NULL_HANDLE; -} - -bool OpenXRFbPassthroughExtensionWrapper::start_passthrough() { - if (passthrough_handle == XR_NULL_HANDLE) { - return false; - } - - if (is_passthrough_enabled()) { - return true; - } - - // Start the passthrough feature - XrResult result = xrPassthroughStartFB(passthrough_handle); - if (!is_valid_passthrough_result(result, "Failed to start passthrough")) { - stop_passthrough(); - return false; - } - - // Create the passthrough layer - XrPassthroughLayerCreateInfoFB passthrough_layer_config = { - XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB, - nullptr, - passthrough_handle, - XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB, - XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB, - }; - result = xrCreatePassthroughLayerFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_layer_config, &passthrough_layer); - if (!is_valid_passthrough_result(result, "Failed to create the passthrough layer")) { - stop_passthrough(); - return false; - } - - // Check if the the viewport has transparent background - Viewport *viewport = get_main_viewport(); - if (viewport && !viewport->has_transparent_background()) { - print_error("Main viewport doesn't have transparent background! Passthrough may not properly render."); - } - - return true; -} - -void OpenXRFbPassthroughExtensionWrapper::on_session_created(const XrSession session) { - if (fb_passthrough_ext) { - // Create the passthrough feature and start it. - XrPassthroughCreateInfoFB passthrough_create_info = { - XR_TYPE_PASSTHROUGH_CREATE_INFO_FB, - nullptr, - 0, - }; - - XrResult result = xrCreatePassthroughFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_create_info, &passthrough_handle); - if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to create passthrough")) { - passthrough_handle = XR_NULL_HANDLE; - return; - } - } -} - -XrCompositionLayerBaseHeader *OpenXRFbPassthroughExtensionWrapper::get_composition_layer() { - if (is_passthrough_enabled()) { - composition_passthrough_layer.layerHandle = passthrough_layer; - return (XrCompositionLayerBaseHeader *)&composition_passthrough_layer; - } else { - return nullptr; - } -} - -void OpenXRFbPassthroughExtensionWrapper::stop_passthrough() { - if (!fb_passthrough_ext) { - return; - } - - XrResult result; - if (passthrough_layer != XR_NULL_HANDLE) { - // Destroy the layer - result = xrDestroyPassthroughLayerFB(passthrough_layer); - OpenXRAPI::get_singleton()->xr_result(result, "Unable to destroy passthrough layer"); - passthrough_layer = XR_NULL_HANDLE; - } - - if (passthrough_handle != XR_NULL_HANDLE) { - result = xrPassthroughPauseFB(passthrough_handle); - OpenXRAPI::get_singleton()->xr_result(result, "Unable to stop passthrough feature"); - } -} - -void OpenXRFbPassthroughExtensionWrapper::on_session_destroyed() { - if (fb_passthrough_ext) { - stop_passthrough(); - - XrResult result; - if (passthrough_handle != XR_NULL_HANDLE) { - result = xrDestroyPassthroughFB(passthrough_handle); - OpenXRAPI::get_singleton()->xr_result(result, "Unable to destroy passthrough feature"); - passthrough_handle = XR_NULL_HANDLE; - } - } -} - -void OpenXRFbPassthroughExtensionWrapper::on_instance_destroyed() { - if (fb_passthrough_ext) { - OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this); - } - cleanup(); -} - -bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_passthrough_extension(const XrInstance p_instance) { - ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false); - - EXT_INIT_XR_FUNC_V(xrCreatePassthroughFB); - EXT_INIT_XR_FUNC_V(xrDestroyPassthroughFB); - EXT_INIT_XR_FUNC_V(xrPassthroughStartFB); - EXT_INIT_XR_FUNC_V(xrPassthroughPauseFB); - EXT_INIT_XR_FUNC_V(xrCreatePassthroughLayerFB); - EXT_INIT_XR_FUNC_V(xrDestroyPassthroughLayerFB); - EXT_INIT_XR_FUNC_V(xrPassthroughLayerPauseFB); - EXT_INIT_XR_FUNC_V(xrPassthroughLayerResumeFB); - EXT_INIT_XR_FUNC_V(xrPassthroughLayerSetStyleFB); - EXT_INIT_XR_FUNC_V(xrCreateGeometryInstanceFB); - EXT_INIT_XR_FUNC_V(xrDestroyGeometryInstanceFB); - EXT_INIT_XR_FUNC_V(xrGeometryInstanceSetTransformFB); - - return true; -} - -bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_triangle_mesh_extension(const XrInstance p_instance) { - ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false); - - EXT_INIT_XR_FUNC_V(xrCreateTriangleMeshFB); - EXT_INIT_XR_FUNC_V(xrDestroyTriangleMeshFB); - EXT_INIT_XR_FUNC_V(xrTriangleMeshGetVertexBufferFB); - EXT_INIT_XR_FUNC_V(xrTriangleMeshGetIndexBufferFB); - EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginUpdateFB); - EXT_INIT_XR_FUNC_V(xrTriangleMeshEndUpdateFB); - EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginVertexBufferUpdateFB); - EXT_INIT_XR_FUNC_V(xrTriangleMeshEndVertexBufferUpdateFB); - - return true; -} diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h deleted file mode 100644 index 045e424202..0000000000 --- a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h +++ /dev/null @@ -1,231 +0,0 @@ -/**************************************************************************/ -/* openxr_fb_passthrough_extension_wrapper.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H -#define OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H - -#include "../openxr_api.h" -#include "../util.h" -#include "openxr_composition_layer_provider.h" -#include "openxr_extension_wrapper.h" - -#include <map> - -class Viewport; - -// Wrapper for the set of Facebook XR passthrough extensions. -class OpenXRFbPassthroughExtensionWrapper : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider { -public: - OpenXRFbPassthroughExtensionWrapper(); - ~OpenXRFbPassthroughExtensionWrapper(); - - virtual HashMap<String, bool *> get_requested_extensions() override; - - void on_instance_created(const XrInstance instance) override; - - void on_session_created(const XrSession session) override; - - void on_session_destroyed() override; - - void on_instance_destroyed() override; - - XrCompositionLayerBaseHeader *get_composition_layer() override; - - bool is_passthrough_supported() { - return fb_passthrough_ext; - } - - bool is_passthrough_enabled(); - - bool start_passthrough(); - - void stop_passthrough(); - - static OpenXRFbPassthroughExtensionWrapper *get_singleton(); - -private: - // Create a passthrough feature - EXT_PROTO_XRRESULT_FUNC3(xrCreatePassthroughFB, - (XrSession), session, - (const XrPassthroughCreateInfoFB *), create_info, - (XrPassthroughFB *), feature_out) - - // Destroy a previously created passthrough feature - EXT_PROTO_XRRESULT_FUNC1(xrDestroyPassthroughFB, (XrPassthroughFB), feature) - - //*** Passthrough feature state management functions ********* - // Start the passthrough feature - EXT_PROTO_XRRESULT_FUNC1(xrPassthroughStartFB, (XrPassthroughFB), passthrough) - // Pause the passthrough feature - EXT_PROTO_XRRESULT_FUNC1(xrPassthroughPauseFB, (XrPassthroughFB), passthrough) - - EXT_PROTO_XRRESULT_FUNC3(xrCreatePassthroughLayerFB, (XrSession), session, - (const XrPassthroughLayerCreateInfoFB *), config, - (XrPassthroughLayerFB *), layer_out) - - EXT_PROTO_XRRESULT_FUNC1(xrDestroyPassthroughLayerFB, (XrPassthroughLayerFB), layer) - - EXT_PROTO_XRRESULT_FUNC1(xrPassthroughLayerPauseFB, (XrPassthroughLayerFB), layer) - EXT_PROTO_XRRESULT_FUNC1(xrPassthroughLayerResumeFB, (XrPassthroughLayerFB), layer) - - // Set the style of an existing passthrough layer. If the enabled feature set - // doesn’t change, this is a lightweight operation that can be called in every - // frame to animate the style. Changes that may incur a bigger cost: - // - Enabling/disabling the color mapping, or changing the type of mapping - // (monochromatic to RGBA or back). - // - Changing `textureOpacityFactor` from 0 to non-zero or vice versa - // - Changing `edgeColor[3]` from 0 to non-zero or vice versa - // NOTE: For XR_FB_passthrough, all color values are treated as linear. - EXT_PROTO_XRRESULT_FUNC2(xrPassthroughLayerSetStyleFB, - (XrPassthroughLayerFB), layer, - (const XrPassthroughStyleFB *), style) - - // Create a geometry instance to be used as a projection surface for passthrough. - // A geometry instance assigns a triangle mesh as part of the specified layer's - // projection surface. - // The operation is only valid if the passthrough layer's purpose has been set to - // `XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB`. Otherwise, the call this function will - // result in an error. In the specified layer, Passthrough will be visible where the view - // is covered by the user-specified geometries. - // - // A triangle mesh object can be instantiated multiple times - in the same or different layers' - // projection surface. Each instantiation has its own transformation, which - // can be updated using `xrGeometryInstanceSetTransformFB`. - EXT_PROTO_XRRESULT_FUNC3(xrCreateGeometryInstanceFB, - (XrSession), session, - (const XrGeometryInstanceCreateInfoFB *), create_info, - (XrGeometryInstanceFB *), out_geometry_instance) - - // Destroys a previously created geometry instance from passthrough rendering. - // This removes the geometry instance from passthrough rendering. - // The operation has no effect on other instances or the underlying mesh. - EXT_PROTO_XRRESULT_FUNC1(xrDestroyGeometryInstanceFB, (XrGeometryInstanceFB), instance) - - // Update the transformation of a passthrough geometry instance. - EXT_PROTO_XRRESULT_FUNC2(xrGeometryInstanceSetTransformFB, - (XrGeometryInstanceFB), instance, - (const XrGeometryInstanceTransformFB *), transformation) - - // Create a triangle mesh geometry object. - // Depending on the behavior flags, the mesh could be created immutable (data is assigned - // at creation and cannot be changed) or mutable (the mesh is created empty and can be updated - // by calling begin/end update functions). - EXT_PROTO_XRRESULT_FUNC3(xrCreateTriangleMeshFB, - (XrSession), session, - (const XrTriangleMeshCreateInfoFB *), create_info, - (XrTriangleMeshFB *), out_triangle_mesh) - - // Destroy an `XrTriangleMeshFB` object along with its data. The mesh buffers must not be - // accessed anymore after their parent mesh object has been destroyed. - EXT_PROTO_XRRESULT_FUNC1(xrDestroyTriangleMeshFB, (XrTriangleMeshFB), mesh) - - // Retrieve a pointer to the vertex buffer. The vertex buffer is structured as an array of 3 floats - // per vertex representing x, y, and z: `[x0, y0, z0, x1, y1, z1, ...]`. The size of the buffer is - // `maxVertexCount * 3` floats. The application must call `xrTriangleMeshBeginUpdateFB` or - // `xrTriangleMeshBeginVertexBufferUpdateFB` before making modifications to the vertex - // buffer. The buffer location is guaranteed to remain constant over the lifecycle of the mesh - // object. - EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshGetVertexBufferFB, - (XrTriangleMeshFB), mesh, - (XrVector3f **), out_vertex_buffer) - - // Retrieve the index buffer that defines the topology of the triangle mesh. Each triplet of - // consecutive elements point to three vertices in the vertex buffer and thus form a triangle. The - // size of each element is `indexElementSize` bytes, and thus the size of the buffer is - // `maxTriangleCount * 3 * indexElementSize` bytes. The application must call - // `xrTriangleMeshBeginUpdateFB` before making modifications to the index buffer. The buffer - // location is guaranteed to remain constant over the lifecycle of the mesh object. - EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshGetIndexBufferFB, - (XrTriangleMeshFB), mesh, - (uint32_t **), out_index_buffer) - - // Begin updating the mesh buffer data. The application must call this function before it makes any - // modifications to the buffers retrieved by `xrTriangleMeshGetVertexBufferFB` and - // `xrTriangleMeshGetIndexBufferFB`. If only the vertex buffer needs to be updated, - // `xrTriangleMeshBeginVertexBufferUpdateFB` can be used instead. To commit the - // modifications, the application must call `xrTriangleMeshEndUpdateFB`. - EXT_PROTO_XRRESULT_FUNC1(xrTriangleMeshBeginUpdateFB, (XrTriangleMeshFB), mesh) - - // Signal the API that the application has finished updating the mesh buffers after a call to - // `xrTriangleMeshBeginUpdateFB`. `vertexCount` and `triangleCount` specify the actual - // number of primitives that make up the mesh after the update. They must be larger than zero but - // smaller or equal to the maximum counts defined at create time. Buffer data beyond these counts - // is ignored. - EXT_PROTO_XRRESULT_FUNC3(xrTriangleMeshEndUpdateFB, - (XrTriangleMeshFB), mesh, - (uint32_t), vertexCount, - (uint32_t), triangle_count) - - // Update the vertex positions of a triangle mesh. Can only be called once the mesh topology has - // been set using `xrTriangleMeshBeginUpdateFB`/`xrTriangleMeshEndUpdateFB`. The - // vertex count is defined by the last invocation to `xrTriangleMeshEndUpdateFB`. Once the - // modification is done, `xrTriangleMeshEndVertexBufferUpdateFB` must be called. - EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshBeginVertexBufferUpdateFB, - (XrTriangleMeshFB), mesh, - (uint32_t *), out_vertex_count) - - // Signal the API that the contents of the vertex buffer data has been updated - // after a call to `xrTriangleMeshBeginVertexBufferUpdateFB`. - EXT_PROTO_XRRESULT_FUNC1(xrTriangleMeshEndVertexBufferUpdateFB, (XrTriangleMeshFB), mesh) - - bool initialize_fb_passthrough_extension(const XrInstance instance); - - bool initialize_fb_triangle_mesh_extension(const XrInstance instance); - - void cleanup(); - - // TODO: Temporary workaround (https://github.com/GodotVR/godot_openxr/issues/138) - // Address a bug in the passthrough api where XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB is - // returned even when the operation is valid on Meta Quest devices. - // The issue should be addressed on that platform in OS release v37. - inline bool is_valid_passthrough_result(XrResult result, const char *format) { - return OpenXRAPI::get_singleton()->xr_result(result, format) || result == XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB; - } - - Viewport *get_main_viewport(); - - static OpenXRFbPassthroughExtensionWrapper *singleton; - - bool fb_passthrough_ext = false; // required for any passthrough functionality - bool fb_triangle_mesh_ext = false; // only use for projected passthrough - - XrPassthroughFB passthrough_handle = XR_NULL_HANDLE; - XrPassthroughLayerFB passthrough_layer = XR_NULL_HANDLE; - - XrCompositionLayerPassthroughFB composition_passthrough_layer = { - XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB, - nullptr, - XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, - XR_NULL_HANDLE, - XR_NULL_HANDLE, - }; -}; - -#endif // OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 491a419525..80ddfe703f 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -56,7 +56,6 @@ #include "extensions/openxr_composition_layer_depth_extension.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h" #include "extensions/openxr_fb_foveation_extension.h" -#include "extensions/openxr_fb_passthrough_extension_wrapper.h" #include "extensions/openxr_fb_update_swapchain_extension.h" #ifdef ANDROID_ENABLED @@ -3122,11 +3121,30 @@ bool OpenXRAPI::is_environment_blend_mode_supported(XrEnvironmentBlendMode p_ble } bool OpenXRAPI::set_environment_blend_mode(XrEnvironmentBlendMode p_blend_mode) { + if (emulate_environment_blend_mode_alpha_blend && p_blend_mode == XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND) { + requested_environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND; + environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + return true; + } // We allow setting this when not initialized and will check if it is supported when initializing. // After OpenXR is initialized we verify we're setting a supported blend mode. - if (!is_initialized() || is_environment_blend_mode_supported(p_blend_mode)) { + else if (!is_initialized() || is_environment_blend_mode_supported(p_blend_mode)) { + requested_environment_blend_mode = p_blend_mode; environment_blend_mode = p_blend_mode; return true; } return false; } + +void OpenXRAPI::set_emulate_environment_blend_mode_alpha_blend(bool p_enabled) { + emulate_environment_blend_mode_alpha_blend = p_enabled; +} + +OpenXRAPI::OpenXRAlphaBlendModeSupport OpenXRAPI::is_environment_blend_mode_alpha_blend_supported() { + if (is_environment_blend_mode_supported(XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND)) { + return OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL; + } else if (emulate_environment_blend_mode_alpha_blend) { + return OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING; + } + return OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE; +} diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 5e5a3d4663..d39e6e0b2e 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -104,8 +104,10 @@ private: // blend mode XrEnvironmentBlendMode environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + XrEnvironmentBlendMode requested_environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; uint32_t num_supported_environment_blend_modes = 0; XrEnvironmentBlendMode *supported_environment_blend_modes = nullptr; + bool emulate_environment_blend_mode_alpha_blend = false; // state XrInstance instance = XR_NULL_HANDLE; @@ -429,7 +431,16 @@ public: const XrEnvironmentBlendMode *get_supported_environment_blend_modes(uint32_t &count); bool is_environment_blend_mode_supported(XrEnvironmentBlendMode p_blend_mode) const; bool set_environment_blend_mode(XrEnvironmentBlendMode p_blend_mode); - XrEnvironmentBlendMode get_environment_blend_mode() const { return environment_blend_mode; } + XrEnvironmentBlendMode get_environment_blend_mode() const { return requested_environment_blend_mode; } + + enum OpenXRAlphaBlendModeSupport { + OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE = 0, + OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL = 1, + OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING = 2, + }; + + void set_emulate_environment_blend_mode_alpha_blend(bool p_enabled); + OpenXRAlphaBlendModeSupport is_environment_blend_mode_alpha_blend_supported(); OpenXRAPI(); ~OpenXRAPI(); diff --git a/modules/openxr/openxr_api_extension.cpp b/modules/openxr/openxr_api_extension.cpp index f0f0835f78..d9e282e218 100644 --- a/modules/openxr/openxr_api_extension.cpp +++ b/modules/openxr/openxr_api_extension.cpp @@ -30,6 +30,8 @@ #include "openxr_api_extension.h" +#include "extensions/openxr_extension_wrapper_extension.h" + void OpenXRAPIExtension::_bind_methods() { ClassDB::bind_method(D_METHOD("get_instance"), &OpenXRAPIExtension::get_instance); ClassDB::bind_method(D_METHOD("get_system_id"), &OpenXRAPIExtension::get_system_id); @@ -48,6 +50,16 @@ void OpenXRAPIExtension::_bind_methods() { ClassDB::bind_method(D_METHOD("get_play_space"), &OpenXRAPIExtension::get_play_space); ClassDB::bind_method(D_METHOD("get_next_frame_time"), &OpenXRAPIExtension::get_next_frame_time); ClassDB::bind_method(D_METHOD("can_render"), &OpenXRAPIExtension::can_render); + + ClassDB::bind_method(D_METHOD("register_composition_layer_provider", "extension"), &OpenXRAPIExtension::register_composition_layer_provider); + ClassDB::bind_method(D_METHOD("unregister_composition_layer_provider", "extension"), &OpenXRAPIExtension::unregister_composition_layer_provider); + + ClassDB::bind_method(D_METHOD("set_emulate_environment_blend_mode_alpha_blend", "enabled"), &OpenXRAPIExtension::set_emulate_environment_blend_mode_alpha_blend); + ClassDB::bind_method(D_METHOD("is_environment_blend_mode_alpha_supported"), &OpenXRAPIExtension::is_environment_blend_mode_alpha_blend_supported); + + BIND_ENUM_CONSTANT(OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE); + BIND_ENUM_CONSTANT(OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL); + BIND_ENUM_CONSTANT(OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING); } uint64_t OpenXRAPIExtension::get_instance() { @@ -126,5 +138,25 @@ bool OpenXRAPIExtension::can_render() { return OpenXRAPI::get_singleton()->can_render(); } +void OpenXRAPIExtension::register_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + OpenXRAPI::get_singleton()->register_composition_layer_provider(p_extension); +} + +void OpenXRAPIExtension::unregister_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + OpenXRAPI::get_singleton()->unregister_composition_layer_provider(p_extension); +} + +void OpenXRAPIExtension::set_emulate_environment_blend_mode_alpha_blend(bool p_enabled) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + OpenXRAPI::get_singleton()->set_emulate_environment_blend_mode_alpha_blend(p_enabled); +} + +OpenXRAPIExtension::OpenXRAlphaBlendModeSupport OpenXRAPIExtension::is_environment_blend_mode_alpha_blend_supported() { + ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE); + return (OpenXRAPIExtension::OpenXRAlphaBlendModeSupport)OpenXRAPI::get_singleton()->is_environment_blend_mode_alpha_blend_supported(); +} + OpenXRAPIExtension::OpenXRAPIExtension() { } diff --git a/modules/openxr/openxr_api_extension.h b/modules/openxr/openxr_api_extension.h index 98f87c7aa1..82344c1d06 100644 --- a/modules/openxr/openxr_api_extension.h +++ b/modules/openxr/openxr_api_extension.h @@ -38,6 +38,8 @@ #include "core/os/thread_safe.h" #include "core/variant/native_ptr.h" +class OpenXRExtensionWrapperExtension; + class OpenXRAPIExtension : public RefCounted { GDCLASS(OpenXRAPIExtension, RefCounted); @@ -70,7 +72,21 @@ public: int64_t get_next_frame_time(); bool can_render(); + void register_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension); + void unregister_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension); + + enum OpenXRAlphaBlendModeSupport { + OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE = 0, + OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL = 1, + OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING = 2, + }; + + void set_emulate_environment_blend_mode_alpha_blend(bool p_enabled); + OpenXRAlphaBlendModeSupport is_environment_blend_mode_alpha_blend_supported(); + OpenXRAPIExtension(); }; +VARIANT_ENUM_CAST(OpenXRAPIExtension::OpenXRAlphaBlendModeSupport); + #endif // OPENXR_API_EXTENSION_H diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index ebcd331f3d..05c53ad52f 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -1158,21 +1158,19 @@ void OpenXRInterface::end_frame() { } bool OpenXRInterface::is_passthrough_supported() { - return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_supported(); + return get_supported_environment_blend_modes().find(XR_ENV_BLEND_MODE_ALPHA_BLEND); } bool OpenXRInterface::is_passthrough_enabled() { - return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_enabled(); + return get_environment_blend_mode() == XR_ENV_BLEND_MODE_ALPHA_BLEND; } bool OpenXRInterface::start_passthrough() { - return passthrough_wrapper != nullptr && passthrough_wrapper->start_passthrough(); + return set_environment_blend_mode(XR_ENV_BLEND_MODE_ALPHA_BLEND); } void OpenXRInterface::stop_passthrough() { - if (passthrough_wrapper) { - passthrough_wrapper->stop_passthrough(); - } + set_environment_blend_mode(XR_ENV_BLEND_MODE_OPAQUE); } Array OpenXRInterface::get_supported_environment_blend_modes() { @@ -1204,6 +1202,11 @@ Array OpenXRInterface::get_supported_environment_blend_modes() { WARN_PRINT("Unsupported blend mode found: " + String::num_int64(int64_t(env_blend_modes[i]))); } } + + if (openxr_api->is_environment_blend_mode_alpha_blend_supported() == OpenXRAPI::OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING) { + modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND); + } + return modes; } @@ -1422,8 +1425,6 @@ OpenXRInterface::OpenXRInterface() { _set_default_pos(head_transform, 1.0, 0); _set_default_pos(transform_for_view[0], 1.0, 1); _set_default_pos(transform_for_view[1], 1.0, 2); - - passthrough_wrapper = OpenXRFbPassthroughExtensionWrapper::get_singleton(); } OpenXRInterface::~OpenXRInterface() { diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index ca95fdf04d..aee9751d6b 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -32,7 +32,6 @@ #define OPENXR_INTERFACE_H #include "action_map/openxr_action_map.h" -#include "extensions/openxr_fb_passthrough_extension_wrapper.h" #include "extensions/openxr_hand_tracking_extension.h" #include "openxr_api.h" @@ -49,7 +48,6 @@ private: OpenXRAPI *openxr_api = nullptr; bool initialized = false; XRInterface::TrackingStatus tracking_state; - OpenXRFbPassthroughExtensionWrapper *passthrough_wrapper = nullptr; // At a minimum we need a tracker for our head Ref<XRPositionalTracker> head; diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index 04411a0c57..a0a93c8694 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -44,7 +44,6 @@ #include "extensions/openxr_composition_layer_depth_extension.h" #include "extensions/openxr_eye_gaze_interaction.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h" -#include "extensions/openxr_fb_passthrough_extension_wrapper.h" #include "extensions/openxr_hand_tracking_extension.h" #include "extensions/openxr_htc_controller_extension.h" #include "extensions/openxr_htc_vive_tracker_extension.h" @@ -114,7 +113,6 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCControllerExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRHuaweiControllerExtension)); - OpenXRAPI::register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRDisplayRefreshRateExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRWMRControllerExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRML2ControllerExtension)); diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 7aaf70e625..6010d4ba76 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -7,6 +7,8 @@ from pathlib import Path from platform_methods import run_in_subprocess import platform_windows_builders +sources = [] + common_win = [ "godot_windows.cpp", "crash_handler_windows.cpp", @@ -43,7 +45,8 @@ res_file = "godot_res.rc" res_target = "godot_res" + env["OBJSUFFIX"] res_obj = env.RES(res_target, res_file) -sources = common_win + res_obj +env.add_source_files(sources, common_win) +sources += res_obj prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"]) arrange_program_clean(prog) @@ -65,6 +68,7 @@ if env["windows_subsystem"] == "gui": prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"]) arrange_program_clean(prog_wrap) env_wrap.Depends(prog_wrap, prog) + sources += common_win_wrap + res_wrap_obj # Microsoft Visual Studio Project Generation if env["vsproj"]: @@ -134,3 +138,5 @@ if not os.getenv("VCINSTALLDIR"): env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw)) if env["windows_subsystem"] == "gui": env.AddPostAction(prog_wrap, run_in_subprocess(platform_windows_builders.make_debug_mingw)) + +env.platform_sources += sources diff --git a/platform/windows/msvs.py b/platform/windows/msvs.py new file mode 100644 index 0000000000..2d5ebe811a --- /dev/null +++ b/platform/windows/msvs.py @@ -0,0 +1,20 @@ +import methods + + +# Tuples with the name of the arch that will be used in VS, mapped to our internal arch names. +# For Windows platforms, Win32 is what VS wants. For other platforms, it can be different. +def get_platforms(): + return [("Win32", "x86_32"), ("x64", "x86_64")] + + +def get_configurations(): + return ["editor", "template_debug", "template_release"] + + +def get_build_prefix(env): + batch_file = methods.find_visual_c_batch_file(env) + return [ + "set "plat=$(PlatformTarget)"", + "(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))", + f"call "{batch_file}" !plat!", + ] diff --git a/thirdparty/README.md b/thirdparty/README.md index 1988744340..0bf1ad5b06 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -862,7 +862,7 @@ instead of `miniz.h` as an external dependency. ## thorvg - Upstream: https://github.com/thorvg/thorvg -- Version: 0.12.3 (9d79f0ccef632fd3b43b8ea02def529b6a8d2288, 2024) +- Version: 0.12.4 (331839d49368e19ca15f35abee5ac541dbf23637, 2024) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index ef65b1500d..e50971e297 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -9,5 +9,5 @@ // For internal debugging: //#define THORVG_LOG_ENABLED -#define THORVG_VERSION_STRING "0.12.3" +#define THORVG_VERSION_STRING "0.12.4" #endif diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp index f618a3c827..67c87ba149 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp @@ -51,8 +51,8 @@ #define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++. #include <cstring> -#include <math.h> #include <ctype.h> +#include "tvgMath.h" #include "tvgShape.h" #include "tvgSvgLoaderCommon.h" #include "tvgSvgPath.h" @@ -471,9 +471,16 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm } case 'a': case 'A': { - _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], arr[0], arr[1], arr[2], arr[3], arr[4]); - *cur = *curCtl = {arr[5], arr[6]}; - *isQuadratic = false; + if (mathZero(arr[0]) || mathZero(arr[1])) { + Point p = {arr[5], arr[6]}; + cmds->push(PathCommand::LineTo); + pts->push(p); + *cur = {arr[5], arr[6]}; + } else if (!mathEqual(cur->x, arr[5]) || !mathEqual(cur->y, arr[6])) { + _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], fabsf(arr[0]), fabsf(arr[1]), arr[2], arr[3], arr[4]); + *cur = *curCtl = {arr[5], arr[6]}; + *isQuadratic = false; + } break; } default: { diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index 4645c4a58b..c187af38d5 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -63,7 +63,7 @@ struct SwTask : Task return region; } - virtual bool dispose() = 0; + virtual void dispose() = 0; virtual bool clip(SwRleData* target) = 0; virtual SwRleData* rle() = 0; @@ -196,10 +196,9 @@ struct SwShapeTask : SwTask shapeDelOutline(&shape, mpool, tid); } - bool dispose() override + void dispose() override { shapeFree(&shape); - return true; } }; @@ -250,10 +249,9 @@ struct SwSceneTask : SwTask } } - bool dispose() override + void dispose() override { rleFree(sceneRle); - return true; } }; @@ -318,10 +316,9 @@ struct SwImageTask : SwTask imageDelOutline(&image, mpool, tid); } - bool dispose() override + void dispose() override { imageFree(&image); - return true; } }; @@ -703,17 +700,15 @@ ColorSpace SwRenderer::colorSpace() } -bool SwRenderer::dispose(RenderData data) +void SwRenderer::dispose(RenderData data) { auto task = static_cast<SwTask*>(data); - if (!task) return true; + if (!task) return; task->done(); task->dispose(); if (task->pushed) task->disposed = true; else delete(task); - - return true; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h index 83d942388f..02359e4a39 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h @@ -43,7 +43,7 @@ public: bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; bool postRender() override; - bool dispose(RenderData data) override; + void dispose(RenderData data) override; RenderRegion region(RenderData data) override; RenderRegion viewport() override; bool viewport(const RenderRegion& vp) override; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp index 45322c07b4..d3b715eab8 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp @@ -122,7 +122,9 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans Line cur = {dash.ptCur, *to}; auto len = _lineLength(cur.pt1, cur.pt2); - if (len < dash.curLen) { + if (mathZero(len)) { + _outlineMoveTo(*dash.outline, &dash.ptCur, transform); + } else if (len < dash.curLen) { dash.curLen -= len; if (!dash.curOpGap) { if (dash.move) { @@ -179,7 +181,9 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to}; auto len = bezLength(cur); - if (len < dash.curLen) { + if (mathZero(len)) { + _outlineMoveTo(*dash.outline, &dash.ptCur, transform); + } else if (len < dash.curLen) { dash.curLen -= len; if (!dash.curOpGap) { if (dash.move) { diff --git a/thirdparty/thorvg/src/renderer/tvgCanvas.h b/thirdparty/thorvg/src/renderer/tvgCanvas.h index 25181de47e..2d87a7e0a1 100644 --- a/thirdparty/thorvg/src/renderer/tvgCanvas.h +++ b/thirdparty/thorvg/src/renderer/tvgCanvas.h @@ -33,14 +33,30 @@ struct Canvas::Impl bool refresh = false; //if all paints should be updated by force. bool drawing = false; //on drawing condition? - Impl(RenderMethod* pRenderer):renderer(pRenderer) + Impl(RenderMethod* pRenderer) : renderer(pRenderer) { + renderer->ref(); } ~Impl() { - clear(true); - delete(renderer); + //make it sure any deffered jobs + if (renderer) { + renderer->sync(); + renderer->clear(); + } + + clearPaints(); + + if (renderer && (renderer->unref() == 0)) delete(renderer); + } + + void clearPaints() + { + for (auto paint : paints) { + if (P(paint)->unref() == 0) delete(paint); + } + paints.clear(); } Result push(unique_ptr<Paint> paint) @@ -62,15 +78,8 @@ struct Canvas::Impl if (!renderer || !renderer->clear()) return Result::InsufficientCondition; //Free paints - if (free) { - for (auto paint : paints) { - P(paint)->unref(); - if (paint->pImpl->dispose(*renderer) && P(paint)->refCnt == 0) { - delete(paint); - } - } - paints.clear(); - } + if (free) clearPaints(); + drawing = false; return Result::Success; @@ -94,7 +103,7 @@ struct Canvas::Impl //Optimize Me: Can we skip the searching? for (auto paint2 : paints) { if (paint2 == paint) { - paint->pImpl->update(*renderer, nullptr, clips, 255, flag); + paint->pImpl->update(renderer, nullptr, clips, 255, flag); return Result::Success; } } @@ -102,7 +111,7 @@ struct Canvas::Impl //Update all retained paint nodes } else { for (auto paint : paints) { - paint->pImpl->update(*renderer, nullptr, clips, 255, flag); + paint->pImpl->update(renderer, nullptr, clips, 255, flag); } } @@ -117,7 +126,7 @@ struct Canvas::Impl bool rendered = false; for (auto paint : paints) { - if (paint->pImpl->render(*renderer)) rendered = true; + if (paint->pImpl->render(renderer)) rendered = true; } if (!rendered || !renderer->postRender()) return Result::InsufficientCondition; diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp index 563db3b44a..13ec4183d6 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp @@ -106,7 +106,7 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, } -RenderRegion Paint::Impl::bounds(RenderMethod& renderer) const +RenderRegion Paint::Impl::bounds(RenderMethod* renderer) const { RenderRegion ret; PAINT_METHOD(ret, bounds(renderer)); @@ -114,16 +114,6 @@ RenderRegion Paint::Impl::bounds(RenderMethod& renderer) const } -bool Paint::Impl::dispose(RenderMethod& renderer) -{ - if (compData) compData->target->pImpl->dispose(renderer); - - bool ret; - PAINT_METHOD(ret, dispose(renderer)); - return ret; -} - - Iterator* Paint::Impl::iterator() { Iterator* ret; @@ -198,7 +188,7 @@ bool Paint::Impl::translate(float x, float y) } -bool Paint::Impl::render(RenderMethod& renderer) +bool Paint::Impl::render(RenderMethod* renderer) { Compositor* cmp = nullptr; @@ -210,27 +200,33 @@ bool Paint::Impl::render(RenderMethod& renderer) if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer)); if (region.w == 0 || region.h == 0) return true; - cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method)); - if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) { + cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method)); + if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) { compData->target->pImpl->render(renderer); } } - if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity); + if (cmp) renderer->beginComposite(cmp, compData->method, compData->target->pImpl->opacity); - renderer.blend(blendMethod); + renderer->blend(blendMethod); bool ret; PAINT_METHOD(ret, render(renderer)); - if (cmp) renderer.endComposite(cmp); + if (cmp) renderer->endComposite(cmp); return ret; } -RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) +RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) { + if (this->renderer != renderer) { + if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!"); + renderer->ref(); + this->renderer = renderer; + } + if (renderFlag & RenderUpdateFlag::Transform) { if (!rTransform) return nullptr; rTransform->update(); @@ -265,9 +261,9 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT if (tryFastTrack) { RenderRegion viewport2; if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) { - viewport = renderer.viewport(); + viewport = renderer->viewport(); viewport2.intersect(viewport); - renderer.viewport(viewport2); + renderer->viewport(viewport2); target->pImpl->ctxFlag |= ContextFlag::FastTrack; } } @@ -289,7 +285,7 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper)); /* 3. Composition Post Processing */ - if (compFastTrack) renderer.viewport(viewport); + if (compFastTrack) renderer->viewport(viewport); else if (childClipper) clips.pop(); return rd; diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.h b/thirdparty/thorvg/src/renderer/tvgPaint.h index 4ec9fa7f7e..c7eb68b198 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.h +++ b/thirdparty/thorvg/src/renderer/tvgPaint.h @@ -50,16 +50,15 @@ namespace tvg Paint* paint = nullptr; RenderTransform* rTransform = nullptr; Composite* compData = nullptr; - BlendMethod blendMethod = BlendMethod::Normal; //uint8_t + RenderMethod* renderer = nullptr; + BlendMethod blendMethod = BlendMethod::Normal; //uint8_t uint8_t renderFlag = RenderUpdateFlag::None; uint8_t ctxFlag = ContextFlag::Invalid; uint8_t id; uint8_t opacity = 255; - uint8_t refCnt = 0; + uint8_t refCnt = 0; //reference count - Impl(Paint* pnt) : paint(pnt) - { - } + Impl(Paint* pnt) : paint(pnt) {} ~Impl() { @@ -68,18 +67,19 @@ namespace tvg free(compData); } delete(rTransform); + if (renderer && (renderer->unref() == 0)) delete(renderer); } uint8_t ref() { if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!"); - return (++refCnt); + return ++refCnt; } uint8_t unref() { if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!"); - return (--refCnt); + return --refCnt; } bool transform(const Matrix& m) @@ -131,15 +131,14 @@ namespace tvg return true; } - RenderRegion bounds(RenderMethod& renderer) const; - bool dispose(RenderMethod& renderer); + RenderRegion bounds(RenderMethod* renderer) const; Iterator* iterator(); bool rotate(float degree); bool scale(float factor); bool translate(float x, float y); bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking); - RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); - bool render(RenderMethod& renderer); + RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); + bool render(RenderMethod* renderer); Paint* duplicate(); }; } diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.cpp b/thirdparty/thorvg/src/renderer/tvgPicture.cpp index 7f120593f4..cfa1bccd08 100644 --- a/thirdparty/thorvg/src/renderer/tvgPicture.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPicture.cpp @@ -69,18 +69,18 @@ bool Picture::Impl::needComposition(uint8_t opacity) } -bool Picture::Impl::render(RenderMethod &renderer) +bool Picture::Impl::render(RenderMethod* renderer) { bool ret = false; - if (surface) return renderer.renderImage(rd); + if (surface) return renderer->renderImage(rd); else if (paint) { Compositor* cmp = nullptr; if (needComp) { - cmp = renderer.target(bounds(renderer), renderer.colorSpace()); - renderer.beginComposite(cmp, CompositeMethod::None, 255); + cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + renderer->beginComposite(cmp, CompositeMethod::None, 255); } ret = paint->pImpl->render(renderer); - if (cmp) renderer.endComposite(cmp); + if (cmp) renderer->endComposite(cmp); } return ret; } @@ -95,9 +95,9 @@ bool Picture::Impl::size(float w, float h) } -RenderRegion Picture::Impl::bounds(RenderMethod& renderer) +RenderRegion Picture::Impl::bounds(RenderMethod* renderer) { - if (rd) return renderer.region(rd); + if (rd) return renderer->region(rd); if (paint) return paint->pImpl->bounds(renderer); return {0, 0, 0, 0}; } diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.h b/thirdparty/thorvg/src/renderer/tvgPicture.h index 26a171ba66..91c16eb44e 100644 --- a/thirdparty/thorvg/src/renderer/tvgPicture.h +++ b/thirdparty/thorvg/src/renderer/tvgPicture.h @@ -70,9 +70,9 @@ struct Picture::Impl RenderTransform resizeTransform(const RenderTransform* pTransform); bool needComposition(uint8_t opacity); - bool render(RenderMethod &renderer); + bool render(RenderMethod* renderer); bool size(float w, float h); - RenderRegion bounds(RenderMethod& renderer); + RenderRegion bounds(RenderMethod* renderer); Result load(ImageLoader* ploader); Impl(Picture* p) : picture(p) @@ -82,24 +82,21 @@ struct Picture::Impl ~Impl() { LoaderMgr::retrieve(loader); + if (surface) { + if (auto renderer = PP(picture)->renderer) { + renderer->dispose(rd); + } + } delete(paint); } - bool dispose(RenderMethod& renderer) - { - if (paint) paint->pImpl->dispose(renderer); - else if (surface) renderer.dispose(rd); - rd = nullptr; - return true; - } - - RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) { auto flag = load(); if (surface) { auto transform = resizeTransform(pTransform); - rd = renderer.prepare(surface, &rm, rd, &transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag)); + rd = renderer->prepare(surface, &rm, rd, &transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag)); } else if (paint) { if (resizing) { loader->resize(paint, w, h); diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h index 3437f9cbff..210382efb4 100644 --- a/thirdparty/thorvg/src/renderer/tvgRender.h +++ b/thirdparty/thorvg/src/renderer/tvgRender.h @@ -261,7 +261,23 @@ struct RenderShape class RenderMethod { +private: + uint32_t refCnt = 0; //reference count + Key key; + public: + uint32_t ref() + { + ScopedLock lock(key); + return (++refCnt); + } + + uint32_t unref() + { + ScopedLock lock(key); + return (--refCnt); + } + virtual ~RenderMethod() {} virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; @@ -270,7 +286,7 @@ public: virtual bool renderShape(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0; virtual bool postRender() = 0; - virtual bool dispose(RenderData data) = 0; + virtual void dispose(RenderData data) = 0; virtual RenderRegion region(RenderData data) = 0; virtual RenderRegion viewport() = 0; virtual bool viewport(const RenderRegion& vp) = 0; @@ -322,7 +338,7 @@ static inline uint8_t CHANNEL_SIZE(ColorSpace cs) } } -static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, CompositeMethod method) +static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod* renderer, CompositeMethod method) { switch(method) { case CompositeMethod::AlphaMask: @@ -335,7 +351,7 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi //TODO: Optimize Luma/InvLuma colorspace to Grayscale8 case CompositeMethod::LumaMask: case CompositeMethod::InvLumaMask: - return renderer.colorSpace(); + return renderer->colorSpace(); default: TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method); return ColorSpace::Unsupported; diff --git a/thirdparty/thorvg/src/renderer/tvgScene.h b/thirdparty/thorvg/src/renderer/tvgScene.h index 1a5600c231..2267a2f71b 100644 --- a/thirdparty/thorvg/src/renderer/tvgScene.h +++ b/thirdparty/thorvg/src/renderer/tvgScene.h @@ -59,7 +59,6 @@ struct SceneIterator : Iterator struct Scene::Impl { list<Paint*> paints; - RenderMethod* renderer = nullptr; //keep it for explicit clear RenderData rd = nullptr; Scene* scene = nullptr; uint8_t opacity; //for composition @@ -74,19 +73,10 @@ struct Scene::Impl for (auto paint : paints) { if (P(paint)->unref() == 0) delete(paint); } - } - bool dispose(RenderMethod& renderer) - { - for (auto paint : paints) { - paint->pImpl->dispose(renderer); + if (auto renderer = PP(scene)->renderer) { + renderer->dispose(rd); } - - renderer.dispose(rd); - this->renderer = nullptr; - this->rd = nullptr; - - return true; } bool needComposition(uint8_t opacity) @@ -111,7 +101,7 @@ struct Scene::Impl return true; } - RenderData update(RenderMethod &renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) + RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) { if ((needComp = needComposition(opacity))) { /* Overriding opacity value. If this scene is half-translucent, @@ -120,14 +110,12 @@ struct Scene::Impl opacity = 255; } - this->renderer = &renderer; - if (clipper) { Array<RenderData> rds(paints.size()); for (auto paint : paints) { rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true)); } - rd = renderer.prepare(rds, rd, transform, clips, opacity, flag); + rd = renderer->prepare(rds, rd, transform, clips, opacity, flag); return rd; } else { for (auto paint : paints) { @@ -137,13 +125,13 @@ struct Scene::Impl } } - bool render(RenderMethod& renderer) + bool render(RenderMethod* renderer) { Compositor* cmp = nullptr; if (needComp) { - cmp = renderer.target(bounds(renderer), renderer.colorSpace()); - renderer.beginComposite(cmp, CompositeMethod::None, opacity); + cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + renderer->beginComposite(cmp, CompositeMethod::None, opacity); needComp = false; } @@ -151,12 +139,12 @@ struct Scene::Impl if (!paint->pImpl->render(renderer)) return false; } - if (cmp) renderer.endComposite(cmp); + if (cmp) renderer->endComposite(cmp); return true; } - RenderRegion bounds(RenderMethod& renderer) const + RenderRegion bounds(RenderMethod* renderer) const { if (paints.empty()) return {0, 0, 0, 0}; @@ -226,14 +214,10 @@ struct Scene::Impl void clear(bool free) { - auto dispose = renderer ? true : false; - for (auto paint : paints) { - if (dispose) free &= P(paint)->dispose(*renderer); if (P(paint)->unref() == 0 && free) delete(paint); } paints.clear(); - renderer = nullptr; } Iterator* iterator() diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h index a7f1226690..1a7a29a999 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.h +++ b/thirdparty/thorvg/src/renderer/tvgShape.h @@ -41,25 +41,25 @@ struct Shape::Impl { } - bool dispose(RenderMethod& renderer) + ~Impl() { - renderer.dispose(rd); - rd = nullptr; - return true; + if (auto renderer = PP(shape)->renderer) { + renderer->dispose(rd); + } } - bool render(RenderMethod& renderer) + bool render(RenderMethod* renderer) { Compositor* cmp = nullptr; bool ret; if (needComp) { - cmp = renderer.target(bounds(renderer), renderer.colorSpace()); - renderer.beginComposite(cmp, CompositeMethod::None, opacity); + cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + renderer->beginComposite(cmp, CompositeMethod::None, opacity); needComp = false; } - ret = renderer.renderShape(rd); - if (cmp) renderer.endComposite(cmp); + ret = renderer->renderShape(rd); + if (cmp) renderer->endComposite(cmp); return ret; } @@ -83,7 +83,7 @@ struct Shape::Impl return true; } - RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) { if ((needComp = needComposition(opacity))) { /* Overriding opacity value. If this scene is half-translucent, @@ -92,14 +92,14 @@ struct Shape::Impl opacity = 255; } - rd = renderer.prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper); + rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper); flag = RenderUpdateFlag::None; return rd; } - RenderRegion bounds(RenderMethod& renderer) + RenderRegion bounds(RenderMethod* renderer) { - return renderer.region(rd); + return renderer->region(rd); } bool bounds(float* x, float* y, float* w, float* h, bool stroking) diff --git a/thirdparty/thorvg/src/renderer/tvgText.h b/thirdparty/thorvg/src/renderer/tvgText.h index b9f7ef6079..f4fb12259a 100644 --- a/thirdparty/thorvg/src/renderer/tvgText.h +++ b/thirdparty/thorvg/src/renderer/tvgText.h @@ -35,7 +35,6 @@ struct Text::Impl { - RenderData rd = nullptr; FontLoader* loader = nullptr; Shape* paint = nullptr; char* utf8 = nullptr; @@ -92,12 +91,13 @@ struct Text::Impl return Result::Success; } - RenderRegion bounds(RenderMethod& renderer) + RenderRegion bounds(RenderMethod* renderer) { - return renderer.region(rd); + if (paint) return P(paint)->bounds(renderer); + else return {0, 0, 0, 0}; } - bool render(RenderMethod& renderer) + bool render(RenderMethod* renderer) { if (paint) return PP(paint)->render(renderer); return false; @@ -120,7 +120,7 @@ struct Text::Impl return false; } - RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) { if (!load()) return nullptr; @@ -142,8 +142,7 @@ struct Text::Impl P(static_cast<RadialGradient*>(fill))->fr *= scale; } } - rd = PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper); - return rd; + return PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper); } bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking) @@ -153,13 +152,6 @@ struct Text::Impl return true; } - bool dispose(RenderMethod& renderer) - { - renderer.dispose(rd); - this->rd = nullptr; - return true; - } - Paint* duplicate() { load(); diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index 4f485d0921..be82de797d 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION=0.12.3 +VERSION=0.12.4 cd thirdparty/thorvg/ || true rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/ |