From 980b03a4108aed1b0d8121395e70d2724e21c74c Mon Sep 17 00:00:00 2001 From: DmitriySalnikov Date: Tue, 14 Nov 2023 22:08:41 +0300 Subject: [PATCH 1/3] added support for hot-reload (fixed some crashes) creating not locked `.pdb` files fixed assignment of `config` instance as default value in docs reload is disabled by default --- .github/workflows/gdextension_build.yml | 3 - .vscode/c_cpp_properties.json | 4 +- .../debug_draw_3d/debug_draw_3d.gdextension | 3 +- dev_build_godot_cpp.bat | 2 +- examples_dd3d/DebugDrawDemoScene.tscn | 6 +- godot-cpp | 2 +- lib_utils.py | 61 +++++++-- src/2d/debug_draw_2d.cpp | 6 - src/2d/debug_draw_2d.h | 1 - src/3d/debug_draw_3d.cpp | 1 - src/3d/debug_geometry_container.cpp | 10 -- src/debug_draw_manager.cpp | 27 ++-- src/debug_draw_manager.h | 2 + src/dev_debug_draw_3d_Library.vcxproj | 7 +- src/editor/asset_library_update_checker.cpp | 129 +++++++++++++++--- src/editor/asset_library_update_checker.h | 12 +- src/editor/editor_menu_extensions.cpp | 5 + src/editor/editor_menu_extensions.h | 1 + src/register_types.cpp | 4 +- 19 files changed, 212 insertions(+), 74 deletions(-) diff --git a/.github/workflows/gdextension_build.yml b/.github/workflows/gdextension_build.yml index d49bcd93..6e65766c 100644 --- a/.github/workflows/gdextension_build.yml +++ b/.github/workflows/gdextension_build.yml @@ -115,9 +115,6 @@ jobs: arch: [arm32, arm64, x86_32, x86_64] target: [template_debug, template_release] - env: - ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/23.2.8568313 - steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index d500d26d..5820f945 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,13 +9,13 @@ "${workspaceFolder}/godot-cpp/gen/include" ], "defines": [ - "_DEBUG", "DEBUG_ENABLED", "UNICODE", "_UNICODE", "GDEXTENSION_LIBRARY", "NOMINMAX", - "TYPED_METHOD_BIND" + "TYPED_METHOD_BIND", + "HOT_RELOAD_ENABLED" ], "cStandard": "c17", "cppStandard": "c++17", diff --git a/addons/debug_draw_3d/debug_draw_3d.gdextension b/addons/debug_draw_3d/debug_draw_3d.gdextension index d55a3be6..984f2ead 100644 --- a/addons/debug_draw_3d/debug_draw_3d.gdextension +++ b/addons/debug_draw_3d/debug_draw_3d.gdextension @@ -1,7 +1,8 @@ [configuration] entry_symbol = "debug_draw_3d_library_init" -compatibility_minimum = "4.1" +compatibility_minimum = "4.1.3" +reloadable = false [dependencies] diff --git a/dev_build_godot_cpp.bat b/dev_build_godot_cpp.bat index e747d24d..89eee7fd 100644 --- a/dev_build_godot_cpp.bat +++ b/dev_build_godot_cpp.bat @@ -4,7 +4,7 @@ set api= git apply --ignore-space-change --ignore-whitespace ../patches/godot_cpp_exclude_unused_classes.patch git apply --ignore-space-change --ignore-whitespace ../patches/unity_build.patch -git apply --ignore-space-change --ignore-whitespace ../patches/debug_string.patch +::git apply --ignore-space-change --ignore-whitespace ../patches/debug_string.patch title win x64 debug dev scons platform=windows target=editor arch=x86_64 dev_build=yes debug_symbols=yes generate_bindings=yes %api% diff --git a/examples_dd3d/DebugDrawDemoScene.tscn b/examples_dd3d/DebugDrawDemoScene.tscn index d9379c71..ad2cd213 100644 --- a/examples_dd3d/DebugDrawDemoScene.tscn +++ b/examples_dd3d/DebugDrawDemoScene.tscn @@ -282,10 +282,10 @@ transform = Transform3D(10.7186, 0, 0, 0, 3.9777, 0, 0, 0, 7.05487, 10.6302, 1.9 [node name="LinePathAnim" type="AnimationPlayer" parent="."] root_node = NodePath("../LinePath") -autoplay = "New Anim" libraries = { "": SubResource("AnimationLibrary_nj4nv") } +autoplay = "New Anim" [node name="LinePath" type="Node3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.67362, -8) @@ -451,11 +451,11 @@ transform = Transform3D(0.935992, 0.352021, 0, -0.352021, 0.935992, 0, 0, 0, 1, target_position = Vector3(0, -3.464, 0) [node name="AnimationPlayer" type="AnimationPlayer" parent="HitTest"] -autoplay = "New Anim" -playback_process_mode = 0 +callback_mode_process = 0 libraries = { "": SubResource("AnimationLibrary_vh8ml") } +autoplay = "New Anim" [node name="LagTest" type="CSGBox3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10.4482, -1.49353, -0.450473) diff --git a/godot-cpp b/godot-cpp index 631cd5fe..c4b7b08c 160000 --- a/godot-cpp +++ b/godot-cpp @@ -1 +1 @@ -Subproject commit 631cd5fe37d4e6df6e5eb66eb4435feca12708cc +Subproject commit c4b7b08c917e4dd41e4a53d28660b7358e60d7b1 diff --git a/lib_utils.py b/lib_utils.py index 1479d96e..8cf0f075 100644 --- a/lib_utils.py +++ b/lib_utils.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import os import json from patches import unity_tools from pathlib import Path @@ -11,12 +12,12 @@ def setup_options(env, arguments, gen_help): from SCons.Variables import Variables, BoolVariable, EnumVariable, PathVariable + opts = Variables([], arguments) opts.Add(BoolVariable("force_enabled_dd3d", "Keep the rendering code in the release build", False)) opts.Add(BoolVariable("lto", "Link-time optimization", False)) - opts.Add(PathVariable("addon_output_dir", "Path to the output directory", - output_dir, PathVariable.PathIsDirCreate)) + opts.Add(PathVariable("addon_output_dir", "Path to the output directory", output_dir, PathVariable.PathIsDirCreate)) opts.Update(env) gen_help(env, opts) @@ -38,6 +39,38 @@ def setup_defines_and_flags(env): env.AppendUnique(LINKFLAGS=["-flto"]) +def is_file_locked(file_path): + if not os.path.exists(file_path): + return False + try: + with open(file_path, "a") as f: + pass + except IOError: + return True + return False + + +def msvc_pdb_rename(env, lib_full_name): + new_name = (Path(env["addon_output_dir"]) / lib_full_name).as_posix() + max_files = 256 + + onlyfiles = [f for f in os.listdir(Path(env["addon_output_dir"])) if os.path.isfile(os.path.join(Path(env["addon_output_dir"]), f))] + for of in onlyfiles: + if of.endswith(".pdb") and of.startswith(lib_full_name): + try: + os.remove(Path(env["addon_output_dir"]) / of) + except: + pass + + pdb_name = "" + for s in range(max_files): + pdb_name = "{}_{}.pdb".format(new_name, s) + if not is_file_locked(pdb_name): + break + + env.Append(LINKFLAGS=["/PDB:" + pdb_name]) + + def get_sources(src): res = [src_folder + "/" + file for file in src] res = unity_tools.generate_unity_build(res, "dd3d_") @@ -75,14 +108,22 @@ def get_library_object(env, arguments=None, gen_help=None): if "release" in env["target"] and env["force_enabled_dd3d"]: additional_tags += ".enabled" - library_full_name = "lib" + lib_name + ".{}.{}.{}{}{}".format( - env["platform"], env["target"], env["arch"], additional_tags,env["SHLIBSUFFIX"]) - - env.Default(env.SharedLibrary( - target=env.File(Path(env["addon_output_dir"]) / library_full_name), - source=get_sources(src), - SHLIBSUFFIX=env["SHLIBSUFFIX"] - )) + library_full_name = "lib{}.{}.{}.{}{}".format( + lib_name, env["platform"], env["target"], env["arch"], additional_tags + ) + + # using the library with `reloadable = true` and with the debugger block the PDB file, + # so it needs to be renamed to something not blocked + if env.get("is_msvc", False) and env["target"] != "template_release": + msvc_pdb_rename(env, library_full_name) + + env.Default( + env.SharedLibrary( + target=env.File(Path(env["addon_output_dir"]) / (library_full_name + env["SHLIBSUFFIX"])), + source=get_sources(src), + SHLIBSUFFIX=env["SHLIBSUFFIX"], + ) + ) # Needed for easy reuse of this library in other build scripts # TODO: not tested at the moment. Probably need some changes in the C++ code diff --git a/src/2d/debug_draw_2d.cpp b/src/2d/debug_draw_2d.cpp index 8543650e..6e022165 100644 --- a/src/2d/debug_draw_2d.cpp +++ b/src/2d/debug_draw_2d.cpp @@ -73,8 +73,6 @@ DebugDraw2D::~DebugDraw2D() { UNASSIGN_SINGLETON(DebugDraw2D); #ifndef DISABLE_DEBUG_RENDERING - _font.unref(); - data_graphs.reset(); grouped_text.reset(); @@ -84,18 +82,14 @@ DebugDraw2D::~DebugDraw2D() { custom_canvas->queue_redraw(); if (!IS_EDITOR_HINT()) { - if (UtilityFunctions::is_instance_valid(_canvas_layer)) - _canvas_layer->queue_free(); if (UtilityFunctions::is_instance_valid(default_canvas)) default_canvas->queue_free(); - _canvas_layer = nullptr; default_canvas = nullptr; } #endif root_node = nullptr; - config.unref(); } void DebugDraw2D::process(double delta) { diff --git a/src/2d/debug_draw_2d.h b/src/2d/debug_draw_2d.h index abd95f2f..0d4add68 100644 --- a/src/2d/debug_draw_2d.h +++ b/src/2d/debug_draw_2d.h @@ -32,7 +32,6 @@ class DebugDraw2D : public Object { DebugDrawManager *root_node = nullptr; // 2d - CanvasLayer *_canvas_layer = nullptr; bool _canvas_need_update = true; Ref _font; diff --git a/src/3d/debug_draw_3d.cpp b/src/3d/debug_draw_3d.cpp index 8c03ec07..da762efd 100644 --- a/src/3d/debug_draw_3d.cpp +++ b/src/3d/debug_draw_3d.cpp @@ -103,7 +103,6 @@ DebugDraw3D::~DebugDraw3D() { dgc.reset(); #endif - config.unref(); root_node = nullptr; } diff --git a/src/3d/debug_geometry_container.cpp b/src/3d/debug_geometry_container.cpp index 4dbdf793..be408c9b 100644 --- a/src/3d/debug_geometry_container.cpp +++ b/src/3d/debug_geometry_container.cpp @@ -60,16 +60,6 @@ DebugGeometryContainer::~DebugGeometryContainer() { geometry_pool.clear_pool(); geometry_pool.clear_pool(); - - RenderingServer *rs = RS(); - rs->free_rid(immediate_mesh_storage.instance); - immediate_mesh_storage.mesh.unref(); - immediate_mesh_storage.material.unref(); - - for (auto &i : multi_mesh_storage) { - rs->free_rid(i.instance); - i.mesh.unref(); - } } void DebugGeometryContainer::CreateMMI(InstanceType type, const String &name, Ref mesh) { diff --git a/src/debug_draw_manager.cpp b/src/debug_draw_manager.cpp index 7ab2ec01..3928e2a1 100644 --- a/src/debug_draw_manager.cpp +++ b/src/debug_draw_manager.cpp @@ -24,6 +24,7 @@ void DebugDrawManager::_bind_methods() { #define REG_CLASS_NAME DebugDrawManager // ClassDB::bind_method(D_METHOD(NAMEOF(get_title)), &DebugDrawGraph::get_title); + ClassDB::bind_method(D_METHOD(NAMEOF(_add_to_tree)), &DebugDrawManager::_add_to_tree); ClassDB::bind_method(D_METHOD(NAMEOF(_integrate_into_engine)), &DebugDrawManager::_integrate_into_engine); ClassDB::bind_method(D_METHOD(NAMEOF(_on_scene_changed)), &DebugDrawManager::_on_scene_changed); @@ -76,7 +77,7 @@ void DebugDrawManager::_on_scene_changed(bool _is_scene_null) { #endif } -void DebugDrawManager::_integrate_into_engine() { +void DebugDrawManager::_add_to_tree() { // Assigned here because it is created inside the `GDExtension Initialize` function. // 1. Create in Initialize and assign Singleton // 2. Call class constructor after Initialize to get default values of properties @@ -86,6 +87,11 @@ void DebugDrawManager::_integrate_into_engine() { SCENE_ROOT()->add_child(this); SCENE_ROOT()->move_child(this, 0); + + // Then wait for `_ready` to continue +} + +void DebugDrawManager::_integrate_into_engine() { set_process(true); // Need to be call 'deferred' @@ -152,10 +158,7 @@ void DebugDrawManager::_integrate_into_engine() { f->store_string(Utils::get_scene_tree_as_string(res->get_parent()->get_parent())); */ } else { - // Create canvas item and canvas layer - auto _canvas_layer = memnew(CanvasLayer); - _canvas_layer->set_layer(64); - debug_draw_2d_singleton->_canvas_layer = _canvas_layer; + set_layer(64); auto default_canvas = memnew(Control); default_canvas->set_name("DebugDrawDefaultCanvas"); @@ -163,8 +166,7 @@ void DebugDrawManager::_integrate_into_engine() { ((Control *)default_canvas)->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); debug_draw_2d_singleton->default_canvas = default_canvas; - add_child(_canvas_layer); - _canvas_layer->add_child(default_canvas); + add_child(default_canvas); } debug_draw_2d_singleton->set_custom_canvas(debug_draw_2d_singleton->custom_canvas); @@ -220,7 +222,7 @@ void DebugDrawManager::init() { Engine::get_singleton()->register_singleton(NAMEOF(DebugDraw2D), debug_draw_2d_singleton); Engine::get_singleton()->register_singleton("Dbg2", debug_draw_2d_singleton); - call_deferred(NAMEOF(_integrate_into_engine)); + call_deferred(NAMEOF(_add_to_tree)); } void DebugDrawManager::_process(double delta) { @@ -248,10 +250,17 @@ void DebugDrawManager::_process(double delta) { } } +void DebugDrawManager::_ready() { + // HACK Call deferred to avoid setting the instance of `config` as a standard parameter value + call_deferred(NAMEOF(_integrate_into_engine)); +} + void DebugDrawManager::_exit_tree() { is_closing = true; - Engine::get_singleton()->unregister_singleton(NAMEOF(DebugDrawManager)); + if (Engine::get_singleton()->has_singleton(NAMEOF(DebugDrawManager))) { + Engine::get_singleton()->unregister_singleton(NAMEOF(DebugDrawManager)); + } if (debug_draw_3d_singleton) { Engine::get_singleton()->unregister_singleton(NAMEOF(DebugDraw3D)); diff --git a/src/debug_draw_manager.h b/src/debug_draw_manager.h index a2b8d4ad..f18f64b3 100644 --- a/src/debug_draw_manager.h +++ b/src/debug_draw_manager.h @@ -29,6 +29,7 @@ class DebugDrawManager : public CanvasLayer { void _connect_scene_changed(); void _on_scene_changed(bool _is_scene_null); + void _add_to_tree(); void _integrate_into_engine(); #ifdef TOOLS_ENABLED @@ -49,5 +50,6 @@ class DebugDrawManager : public CanvasLayer { void init(); virtual void _process(double delta) override; + virtual void _ready() override; virtual void _exit_tree() override; }; \ No newline at end of file diff --git a/src/dev_debug_draw_3d_Library.vcxproj b/src/dev_debug_draw_3d_Library.vcxproj index a60777b2..f264ee34 100644 --- a/src/dev_debug_draw_3d_Library.vcxproj +++ b/src/dev_debug_draw_3d_Library.vcxproj @@ -89,7 +89,7 @@ Level3 false true - WIN64;NOMINMAX;TYPED_METHOD_BIND;DEBUG_ENABLED;GDEXTENSION_LIBRARY;TOOLS_ENABLED;%(PreprocessorDefinitions) + WIN64;NOMINMAX;TYPED_METHOD_BIND;HOT_RELOAD_ENABLED;DEBUG_ENABLED;GDEXTENSION_LIBRARY;TOOLS_ENABLED;%(PreprocessorDefinitions) true Sync true @@ -111,6 +111,7 @@ UseLinkTimeCodeGeneration true %(IgnoreSpecificDefaultLibraries) + $(OutDir)$(TargetName)_vs.pdb @@ -118,7 +119,7 @@ Level3 false true - WIN64;NOMINMAX;TYPED_METHOD_BIND;DEBUG_ENABLED;GDEXTENSION_LIBRARY;TOOLS_ENABLED;DEV_ENABLED;%(PreprocessorDefinitions) + WIN64;NOMINMAX;TYPED_METHOD_BIND;HOT_RELOAD_ENABLED;DEBUG_ENABLED;GDEXTENSION_LIBRARY;TOOLS_ENABLED;DEV_ENABLED;%(PreprocessorDefinitions) true Sync @@ -139,6 +140,7 @@ Default true %(IgnoreSpecificDefaultLibraries) + $(OutDir)$(TargetName)_vs.pdb @@ -166,6 +168,7 @@ $(OutDir)$(TargetName)$(TargetExt) %(IgnoreSpecificDefaultLibraries) UseLinkTimeCodeGeneration + $(OutDir)$(TargetName)_vs.pdb diff --git a/src/editor/asset_library_update_checker.cpp b/src/editor/asset_library_update_checker.cpp index 863ffc69..dad67855 100644 --- a/src/editor/asset_library_update_checker.cpp +++ b/src/editor/asset_library_update_checker.cpp @@ -9,7 +9,6 @@ GODOT_WARNING_DISABLE() #include #include -#include GODOT_WARNING_RESTORE() using namespace godot; @@ -23,12 +22,13 @@ void AssetLibraryUpdateChecker::_bind_methods() { #undef REG_CLASS_NAME } -void AssetLibraryUpdateChecker::request_completed(int result, int response_code, PackedStringArray headers, PackedByteArray body) { - request->queue_free(); - request = nullptr; - - if (response_code != 200) - return; +#include +void AssetLibraryUpdateChecker::request_completed(PackedByteArray body) { + if (http_thread.joinable()) { + is_thread_closing = true; + http_thread.join(); + http.unref(); + } Dictionary dict = JSON::parse_string(body.get_string_from_utf8()); String ver = dict["version_string"]; @@ -97,14 +97,103 @@ void AssetLibraryUpdateChecker::request_completed(int result, int response_code, } void AssetLibraryUpdateChecker::init() { - if (!IS_EDITOR_HINT()) - return; + http.instantiate(); + + http_thread = std::thread([&]() { + Error err = Error::OK; + + if (http.is_valid() && !is_thread_closing) { + http->connect_to_host(godot_domain); + + err = http->poll(); + if (err != Error::OK) { + PRINT_ERROR(FMT_STR("DebugDraw Updater: Failed to initialize connection. Error: {0}", UtilityFunctions::error_string(err))); + return; + } + } else { + return; + } - request = memnew(HTTPRequest); - SCENE_ROOT()->add_child(request); + HTTPClient::Status prev_status = HTTPClient::STATUS_DISCONNECTED; + while (http.is_valid() && !is_thread_closing) { + err = http->poll(); + if (err != Error::OK) { + PRINT_ERROR(FMT_STR("DebugDraw Updater: Failed to connect. Error: {0}", UtilityFunctions::error_string(err))); + return; + } + + HTTPClient::Status status = http->get_status(); + switch (status) { + case godot::HTTPClient::STATUS_DISCONNECTED: + case godot::HTTPClient::STATUS_CONNECTION_ERROR: + case godot::HTTPClient::STATUS_CANT_RESOLVE: + case godot::HTTPClient::STATUS_CANT_CONNECT: + case godot::HTTPClient::STATUS_TLS_HANDSHAKE_ERROR: + PRINT_ERROR(FMT_STR("DebugDraw Updater: Connection error: {0}", status)); + return; + case godot::HTTPClient::STATUS_RESOLVING: + case godot::HTTPClient::STATUS_CONNECTING: + case godot::HTTPClient::STATUS_REQUESTING: + case godot::HTTPClient::STATUS_BODY: + default: + if (status != prev_status) { + DEV_PRINT(FMT_STR("DebugDraw Updater: Connecting status: {0}", status)); + } + break; + case godot::HTTPClient::STATUS_CONNECTED: + goto connected; + } + prev_status = status; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + if (http.is_null() || is_thread_closing) { + return; + } - request->connect("request_completed", Callable(this, NAMEOF(request_completed))); - request->request(godot_asset_api + String::num_int64(addon_id)); + connected: + + String request_url = "/asset-library/api/asset/" + String::num_int64(addon_id); + err = http->request(HTTPClient::METHOD_GET, request_url, PackedStringArray()); + if (err != Error::OK) { + PRINT_ERROR(FMT_STR("DebugDraw Updater: Failed to create a request. Error: {0}", UtilityFunctions::error_string(err))); + return; + } + + while (http.is_valid() && !is_thread_closing) { + + err = http->poll(); + if (err != Error::OK) { + PRINT_ERROR(FMT_STR("DebugDraw Updater: Failed to get a response from \"{0}\". Error: {1}", godot_domain + request_url, UtilityFunctions::error_string(err))); + return; + } + + HTTPClient::ResponseCode code = (HTTPClient::ResponseCode)http->get_response_code(); + if (code == HTTPClient::ResponseCode::RESPONSE_OK) { + PackedByteArray res; + PackedByteArray tmp = http->read_response_body_chunk(); + while (!tmp.is_empty() && !is_thread_closing) { + res.append_array(tmp); + if (http->get_status() != HTTPClient::STATUS_BODY) { + break; + } + tmp = http->read_response_body_chunk(); + } + + call_deferred(NAMEOF(request_completed), res); + return; + } else { + if (code != 0) { + PRINT_ERROR(FMT_STR("DebugDraw Updater: Failed to get a response from \"{0}\". Code: {1}", godot_domain + request_url, code)); + return; + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + DEV_PRINT(FMT_STR("DebugDraw Updater: Thread finished")); + }); } AssetLibraryUpdateChecker::AssetLibraryUpdateChecker() { @@ -114,21 +203,23 @@ AssetLibraryUpdateChecker::AssetLibraryUpdateChecker() { root_settings_section = String(Utils::root_settings_section) + "updates/"; changes_page = "https://github.com/DmitriySalnikov/godot_debug_draw_3d/releases"; - godot_asset_api = "https://godotengine.org/asset-library/api/asset/"; - godot_asset_page = "https://godotengine.org/asset-library/asset/"; + godot_domain = "https://godotengine.org"; + godot_asset_api = godot_domain + "/asset-library/api/asset/"; + godot_asset_page = godot_domain + "/asset-library/asset/"; DEFINE_SETTING_READ_ONLY(root_settings_section + "addon_version", DD3D_VERSION_STR, Variant::STRING); DEFINE_SETTING_READ_ONLY(root_settings_section + "addon_page", godot_asset_page + String::num_int64(addon_id), Variant::STRING); DEFINE_SETTING_AND_GET(bool check_updates, root_settings_section + "check_for_updates", true, Variant::BOOL); - if (check_updates) + if (IS_EDITOR_HINT() && check_updates) call_deferred(NAMEOF(init)); } AssetLibraryUpdateChecker::~AssetLibraryUpdateChecker() { - if (UtilityFunctions::is_instance_valid(request)) - request->queue_free(); - request = nullptr; + if (http_thread.joinable()) { + is_thread_closing = true; + http_thread.join(); + } } #endif diff --git a/src/editor/asset_library_update_checker.h b/src/editor/asset_library_update_checker.h index af065331..de8e2833 100644 --- a/src/editor/asset_library_update_checker.h +++ b/src/editor/asset_library_update_checker.h @@ -5,8 +5,10 @@ #include "utils/compiler.h" +#include + GODOT_WARNING_DISABLE() -#include +#include #include GODOT_WARNING_RESTORE() @@ -16,13 +18,17 @@ class AssetLibraryUpdateChecker : public RefCounted { GDCLASS(AssetLibraryUpdateChecker, RefCounted) private: - HTTPRequest *request = nullptr; + + std::thread http_thread; + bool is_thread_closing = false; + Ref http = nullptr; int addon_id; String addon_name; String repository_name; String root_settings_section; String changes_page; + String godot_domain; String godot_asset_api; String godot_asset_page; @@ -30,7 +36,7 @@ class AssetLibraryUpdateChecker : public RefCounted { static void _bind_methods(); public: - void request_completed(int result, int response_code, PackedStringArray headers, PackedByteArray body); + void request_completed(PackedByteArray body); void init(); AssetLibraryUpdateChecker(); diff --git a/src/editor/editor_menu_extensions.cpp b/src/editor/editor_menu_extensions.cpp index 0f76d9ee..86466b4f 100644 --- a/src/editor/editor_menu_extensions.cpp +++ b/src/editor/editor_menu_extensions.cpp @@ -74,4 +74,9 @@ DebugDrawMenuExtensionPlugin::DebugDrawMenuExtensionPlugin() { menu_item_name = "Debug Draw"; } +DebugDrawMenuExtensionPlugin::~DebugDrawMenuExtensionPlugin() { + DEV_PRINT(NAMEOF(DebugDrawMenuExtensionPlugin) " deconstructor"); + //remove_tool_menu_item(menu_item_name); +} + #endif \ No newline at end of file diff --git a/src/editor/editor_menu_extensions.h b/src/editor/editor_menu_extensions.h index f6d21ceb..973bd751 100644 --- a/src/editor/editor_menu_extensions.h +++ b/src/editor/editor_menu_extensions.h @@ -32,6 +32,7 @@ class DebugDrawMenuExtensionPlugin : public EditorPlugin { void _on_id_pressed(MenuItemId id); DebugDrawMenuExtensionPlugin(); + ~DebugDrawMenuExtensionPlugin(); }; // Register but not expose to GDScript diff --git a/src/register_types.cpp b/src/register_types.cpp index 138611db..143ef69c 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -66,7 +66,7 @@ void uninitialize_debug_draw_3d_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { // If this library is disabled manually before deleting the scene tree, // then an attempt is made to delete this node manually. - if (UtilityFunctions::is_instance_valid(debug_draw_manager)) { + if (Engine::get_singleton()->get_main_loop() && UtilityFunctions::is_instance_valid(debug_draw_manager)) { memdelete(debug_draw_manager); } debug_draw_manager = nullptr; @@ -76,7 +76,7 @@ void uninitialize_debug_draw_3d_module(ModuleInitializationLevel p_level) { #ifdef TOOLS_ENABLED if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { upd_checker.unref(); - //EditorPlugins::remove_by_type(); + // EditorPlugins::remove_by_type(); } #endif #endif From 0d2e7cb17894f4050e17539e6c530eabf1dfae7b Mon Sep 17 00:00:00 2001 From: DmitriySalnikov Date: Fri, 17 Nov 2023 15:41:10 +0300 Subject: [PATCH 2/3] Added instance caching in C# API to avoid creating classes on every call --- src/debug_draw_manager.cpp | 6 + src/debug_draw_manager.h | 3 + src/editor/generate_csharp_bindings.cpp | 196 ++++++++++++++++++++---- src/editor/generate_csharp_bindings.h | 1 + 4 files changed, 172 insertions(+), 34 deletions(-) diff --git a/src/debug_draw_manager.cpp b/src/debug_draw_manager.cpp index 3928e2a1..7ab1f50b 100644 --- a/src/debug_draw_manager.cpp +++ b/src/debug_draw_manager.cpp @@ -20,6 +20,8 @@ DebugDrawManager *DebugDrawManager::singleton = nullptr; using namespace godot; +const char *DebugDrawManager::s_extension_unloading = "extension_unloading"; + void DebugDrawManager::_bind_methods() { #define REG_CLASS_NAME DebugDrawManager @@ -32,6 +34,8 @@ void DebugDrawManager::_bind_methods() { REG_PROP_BOOL(debug_enabled); + ADD_SIGNAL(MethodInfo(s_extension_unloading)); + #undef REG_CLASS_NAME } @@ -275,6 +279,8 @@ void DebugDrawManager::_exit_tree() { memdelete(debug_draw_2d_singleton); debug_draw_2d_singleton = nullptr; } + + emit_signal(s_extension_unloading); } #ifdef TOOLS_ENABLED diff --git a/src/debug_draw_manager.h b/src/debug_draw_manager.h index f18f64b3..aca05861 100644 --- a/src/debug_draw_manager.h +++ b/src/debug_draw_manager.h @@ -37,6 +37,9 @@ class DebugDrawManager : public CanvasLayer { #endif public: + + static const char *s_extension_unloading; + DebugDrawManager(); ~DebugDrawManager(); diff --git a/src/editor/generate_csharp_bindings.cpp b/src/editor/generate_csharp_bindings.cpp index 6e4e9fed..f95850ef 100644 --- a/src/editor/generate_csharp_bindings.cpp +++ b/src/editor/generate_csharp_bindings.cpp @@ -87,6 +87,7 @@ void GenerateCSharpBindingsPlugin::generate() { line("using Godot;"); line("using System;"); + line("using System.Linq;"); is_shift_pressed = true; @@ -138,7 +139,7 @@ void GenerateCSharpBindingsPlugin::generate_class(const StringName &cls, remap_d if (is_preserved_inheritance) { line(FMT_STR("internal class {0} : {1}", cls, parent_name)); } else { - line(FMT_STR("{0}internal class {1}{2}", static_modifier_str, cls, is_singleton ? "" : " : IDisposable")); + line(FMT_STR("{0}internal class {1}{2}", static_modifier_str, cls, is_singleton ? "" : " : _DebugDrawInstanceWrapper_")); } { @@ -230,10 +231,89 @@ void GenerateCSharpBindingsPlugin::generate_class(const StringName &cls, remap_d void GenerateCSharpBindingsPlugin::generate_class_utilities(const remap_data &remapped_data) { log("DebugDraw utilities:", 1); + line("internal class _DebugDrawInstanceWrapper_ : IDisposable"); + { + TAB(); + line("public GodotObject Instance { get; protected set; }"); + line(); + line("public _DebugDrawInstanceWrapper_(GodotObject _instance)"); + { + TAB(); + line("if (_instance == null) throw new ArgumentNullException(\"_instance\");"); + line("if (!ClassDB.IsParentClass(_instance.GetClass(), GetType().Name)) throw new ArgumentException(\"\\\"_instance\\\" has the wrong type.\");"); + line("Instance = _instance;"); + line(); + + if (is_generate_unload_event) { + line("#if DEBUG", 0); + line("_DebugDrawUtils_.ExtensionUnloading += OnUnloading;"); + line("#endif", 0); + } + } + if (is_generate_unload_event) { + line(); + line("#if DEBUG", 0); + line("void OnUnloading()"); + { + TAB(); +#ifdef DEV_ENABLED + line("if (Instance != null)"); + { + TAB(); + line("GD.Print($\"Unload {GetType()}, {Instance.NativeInstance}\");"); + } +#endif + line("try"); + { + TAB(); + line("_DebugDrawUtils_.ExtensionUnloading -= OnUnloading;"); + } + line("catch {}"); + line("Instance = null;"); + } + line("#endif", 0); + } + line(); + line("public void Dispose()"); + { + TAB(); + line("Instance.Dispose();"); + line("Instance = null;"); + } + line(); + line("public void ClearNativePointer()"); + { + TAB(); + line("Instance = null;"); + } + } + line(); + line("internal static class _DebugDrawUtils_"); { TAB(); + if (is_generate_unload_event) { + line("#if DEBUG", 0); + line("public static event Action ExtensionUnloading"); + { + TAB(); + line("add"); + { + TAB(); + line("Engine.GetSingleton(\"DebugDrawManager\").Connect(\"extension_unloading\", Callable.From(value), (uint)GodotObject.ConnectFlags.OneShot);"); + } + + line("remove"); + { + TAB(); + line("Engine.GetSingleton(\"DebugDrawManager\").Disconnect(\"extension_unloading\", Callable.From(value));"); + } + } + line("#endif", 0); + line(); + } + // TODO if merged https://github.com/godotengine/godot/pull/53920, replace by only conditional compilation // Runtime check with disabled debug is 2-3 times slower than conditional compilation // Iterations = 10000 @@ -260,6 +340,10 @@ void GenerateCSharpBindingsPlugin::generate_class_utilities(const remap_data &re // Factory log("Class factory...", 2); { + line("static System.Collections.Generic.Dictionary cached_instances = new();"); + line("static DateTime previous_clear_time = DateTime.Now;"); + line(); + line("public static object CreateWrapperFromObject(GodotObject _instance)"); { TAB(); @@ -269,6 +353,28 @@ void GenerateCSharpBindingsPlugin::generate_class_utilities(const remap_data &re line("return null;"); } + line(""); + line("ulong id = _instance.GetInstanceId();"); + line("if (cached_instances.ContainsKey(id))"); + { + TAB(); + line("return cached_instances[id];"); + } + line(""); + line("if ((DateTime.Now - previous_clear_time).TotalSeconds > 1)"); + { + TAB(); + line("var query = cached_instances.Where((i) => GodotObject.IsInstanceIdValid(i.Key)).ToArray();"); + line("foreach (var i in query)"); + { + TAB(); + line("i.Value.ClearNativePointer();"); + line("cached_instances.Remove(i.Key);"); + } + line("previous_clear_time = DateTime.Now;"); + } + line(""); + line("switch(_instance.GetClass())"); { TAB(); @@ -278,7 +384,9 @@ void GenerateCSharpBindingsPlugin::generate_class_utilities(const remap_data &re line(FMT_STR("case \"{0}\":", cls)); { TAB(); - line(FMT_STR("return new {0}(_instance);", cls)); + line(FMT_STR("_DebugDrawInstanceWrapper_ new_instance = new {0}(_instance);", cls)); + line("cached_instances[id] = new_instance;"); + line("return new_instance;"); } } } @@ -292,6 +400,13 @@ void GenerateCSharpBindingsPlugin::generate_class_utilities(const remap_data &re void GenerateCSharpBindingsPlugin::generate_wrapper(const StringName &cls, bool is_static, bool inheritance) { if (is_static) { String lowered_name = cls; + + if (is_generate_unload_event) { + line("#if DEBUG", 0); + line("static bool _is_connected = false;"); + line("#endif", 0); + } + line("private static GodotObject _instance;"); line("public static GodotObject Instance"); @@ -300,6 +415,17 @@ void GenerateCSharpBindingsPlugin::generate_wrapper(const StringName &cls, bool line("get"); { TAB(); + if (is_generate_unload_event) { + line("#if DEBUG", 0); + line("if (!_is_connected)"); + { + TAB(); + line("_DebugDrawUtils_.ExtensionUnloading += OnUnloading;"); + line("_is_connected = true;"); + } + line("#endif", 0); + line(); + } { line("if (!GodotObject.IsInstanceValid(_instance))"); { @@ -310,29 +436,31 @@ void GenerateCSharpBindingsPlugin::generate_wrapper(const StringName &cls, bool } } } - } else { - if (!inheritance) { - line("public GodotObject Instance { get; private set; }"); - line(FMT_STR("public {0}(GodotObject _instance)", cls)); - { - TAB(); - line("if (_instance == null) throw new ArgumentNullException(\"_instance\");"); - line("if (!ClassDB.IsParentClass(_instance.GetClass(), GetType().Name)) throw new ArgumentException(\"\\\"_instance\\\" has the wrong type.\");"); - line("Instance = _instance;"); - } + if (is_generate_unload_event) { line(); - line("public void Dispose()"); + + line("#if DEBUG", 0); + line("static void OnUnloading()"); { TAB(); - line("Instance.Dispose();"); - line("Instance = null;"); +#ifdef DEV_ENABLED + line("GD.Print(\"Unload " + cls + "\");"); +#endif + line("try"); + { + TAB(); + line("_DebugDrawUtils_.ExtensionUnloading -= OnUnloading;"); + } + line("catch {}"); + line("_instance = null;"); + line("_is_connected = false;"); } - } else { + line("#endif", 0); line(); - line(FMT_STR("public {0}(GodotObject _instance) : base (_instance) {}", cls)); } - + } else { + line(FMT_STR("public {0}(GodotObject _instance) : base (_instance) {}", cls)); line(); line(FMT_STR("public {0}() : this((GodotObject)ClassDB.Instantiate(\"{0}\")) { }", cls)); } @@ -502,7 +630,7 @@ void GenerateCSharpBindingsPlugin::generate_properties(const StringName &cls, co { TAB(); if (is_need_wrapper) { - line(FMT_STR("get => new {0}((GodotObject)ClassDB.ClassGetProperty(Instance, __prop_{1}));", setget.type_name, setget.name)); + line(FMT_STR("get => ({0})_DebugDrawUtils_.CreateWrapperFromObject((GodotObject)ClassDB.ClassGetProperty(Instance, __prop_{1}));", setget.type_name, setget.name)); line(FMT_STR("set => ClassDB.ClassSetProperty(Instance, __prop_{0}, value.Instance);", setget.name)); } else if (setget.is_enum) { line(FMT_STR("get => ({0})(long)ClassDB.ClassGetProperty(Instance, __prop_{1});", setget.type_name, setget.name)); @@ -735,20 +863,20 @@ GenerateCSharpBindingsPlugin::DefaultData GenerateCSharpBindingsPlugin::argument break; } -#define PACKED_ARRAY(_type, _var_type) \ - { \ - _type val = def_val; \ - if (!val.size()) { \ - return DefaultData(arg_data, false, "default"); \ - } else { \ - PackedStringArray strs; \ - for (int i = 0; i < val.size(); i++) { \ - DefaultData data = arguments_get_formatted_value(argument_parse("", "", _var_type), val[i]); \ - strs.append(data.arg_string); \ - } \ - return DefaultData(arg_data, true, FMT_STR("new {0} {{1}}", types_map[def_val.get_type()], String(", ").join(strs)), true); \ - } \ - break; \ +#define PACKED_ARRAY(_type, _var_type) \ + { \ + _type val = def_val; \ + if (!val.size()) { \ + return DefaultData(arg_data, false, "default"); \ + } else { \ + PackedStringArray strs; \ + for (int i = 0; i < val.size(); i++) { \ + DefaultData data = arguments_get_formatted_value(argument_parse("", "", _var_type), val[i]); \ + strs.append(data.arg_string); \ + } \ + return DefaultData(arg_data, true, FMT_STR("new {0} { {1} }", types_map[def_val.get_type()], String(", ").join(strs)), true); \ + } \ + break; \ } case godot::Variant::DICTIONARY: { @@ -764,7 +892,7 @@ GenerateCSharpBindingsPlugin::DefaultData GenerateCSharpBindingsPlugin::argument DefaultData data_v = arguments_get_formatted_value(argument_parse("", "", values[i].get_type()), values[i]); strs.append(FMT_STR("{ {0}, {1} }", data_k.arg_string, data_v.arg_string)); } - return DefaultData(arg_data, true, String("new {0} {{1}}").format(Array::make(types_map[def_val.get_type()], String(", ").join(strs))), true); + return DefaultData(arg_data, true, String("new {0} { {1} }").format(Array::make(types_map[def_val.get_type()], String(", ").join(strs))), true); } break; }; diff --git a/src/editor/generate_csharp_bindings.h b/src/editor/generate_csharp_bindings.h index f1da0db9..f63cb1e3 100644 --- a/src/editor/generate_csharp_bindings.h +++ b/src/editor/generate_csharp_bindings.h @@ -91,6 +91,7 @@ class GenerateCSharpBindingsPlugin { TypedArray generate_for_classes; PackedStringArray singletons; bool is_shift_pressed = false; + bool is_generate_unload_event = false; typedef std::map remap_data; std::map types_map = { From 723fff0ddc0702219f4dc0a7c2d239911064ec7d Mon Sep 17 00:00:00 2001 From: DmitriySalnikov Date: Fri, 17 Nov 2023 16:05:17 +0300 Subject: [PATCH 3/3] revert godot-cpp to 4.1.3 --- godot-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/godot-cpp b/godot-cpp index c4b7b08c..631cd5fe 160000 --- a/godot-cpp +++ b/godot-cpp @@ -1 +1 @@ -Subproject commit c4b7b08c917e4dd41e4a53d28660b7358e60d7b1 +Subproject commit 631cd5fe37d4e6df6e5eb66eb4435feca12708cc