diff options
63 files changed, 939 insertions, 397 deletions
@@ -17,7 +17,6 @@ generous deed immortalized in the next stable release of Godot Engine. ## Gold sponsors Gamblify <https://www.gamblify.com> - GameDev.TV <https://gdev.tv/godot> Image Campus <https://www.imagecampus.edu.ar> ## Mini sponsors @@ -30,6 +29,7 @@ generous deed immortalized in the next stable release of Godot Engine. Christoph Woinke Edward Flick GameDev.net + GameDev.tv Hein-Pieter van Braam Jamal Alyafei Javary Games @@ -37,6 +37,8 @@ generous deed immortalized in the next stable release of Godot Engine. Justin Arnold Kyle Szklenski Leona Eden + Leonard Meagher + Ludosity AB Matthieu Huvé Maxim Karsten Mike King @@ -56,30 +58,34 @@ generous deed immortalized in the next stable release of Godot Engine. ## Gold donors Asdf + Brandon Waite cheese65536 David Gehrig Edward E + Florian Krick K9Kraken Manuele Finocchiaro Nathanael Beisiegel Officine Pixel S.n.c. Retro Village - Valorware Zashi Zaven Muradyan 13MHz Allen Schade Andreas Schüle + Asher Glick Austen McRae + Daniel James Daniel Lynn David Giardi Florian Breisch Gary Oberbrunner Gero Jay Horton - Johannes Wuensch + Jon Smith Jon Woodward + Jorge Bernal Joshua Lesperance Justo Delgado Baudí Krzysztof Dluzniewski @@ -89,8 +95,10 @@ generous deed immortalized in the next stable release of Godot Engine. paul gruenbacher Paul LaMotte Rob Messick + Scott Wadden Sergey Svenne Krap + Tom Langwaldt William Wold Wyatt Goodin Xananax @@ -107,10 +115,11 @@ generous deed immortalized in the next stable release of Godot Engine. Joan Fons John Krzysztof Jankowski - Laurence Bannister Lucas Ferreira Franca Markus Wiesner Nathan Lundquist + Pascal Grüter + Petr Malac Rami Robert Willes Robin Arys @@ -123,10 +132,8 @@ generous deed immortalized in the next stable release of Godot Engine. Alessandra Pereyra Alexey Dyadchenko - Amanda Haldy Benjamin W Flint Chau Siu Hung - Chris Brown Chris Goddard Chris Petrich Christian Leth Jeppesen @@ -139,13 +146,15 @@ generous deed immortalized in the next stable release of Godot Engine. Eric Monson Ethan Bennis Eugenio Hugo Salgüero Jáñez - Eulogio Enamorado Pallares flesk + gavlig GGGames.org + Giles Montgomery Giovanni Solimeno Guilherme Felipe de C. G. da Silva Heath Hayes Hysteria + Jalal Chaabane Jeppe Zapp Jose Malheiro Juan T Chen @@ -156,6 +165,7 @@ generous deed immortalized in the next stable release of Godot Engine. Marius Kamm Martin Eigel Marvin + Matt Eunson Max R.R. Collada Nick Nikitin Oliver Dick @@ -166,26 +176,28 @@ generous deed immortalized in the next stable release of Godot Engine. Ruben Soares Luis Samuel Judd Sofox + spilldata Stoned Xander Tobias Bocanegra WytRabbit Xavier Fumado Beltran - Zachariah Gibbons ## Silver donors 1D_Inc - 2drealms Adam Brunnmeier Adam Carr + Adam Nakonieczny Adam Smeltzer Adisibio Alder Stefano Alessandro Senese Alexander Gillberg Alexander Koppe + Alice Robinson Andreas Krampitz Anthony Bongiovanni + Arbor Interactive Arthur S. Muszynski Artur Barichello Aubrey Falconer @@ -200,19 +212,25 @@ generous deed immortalized in the next stable release of Godot Engine. Bryan Stevenson Carl Winder Carwyn Edwards + Chris Brown Chris Chapin Christian Baune Christian Winter + Christoffer Sundbom + Chris Wilson Collin Shooltz + Connor Hill Daniel Johnson - Daniel Kaplan DanielMaximiano + Daniel Reed David Cravens David May Dominik Wetzel DOXA Edward Herbert + Egon Elbre Elmeri '- Duy Kevin Nguyen + Emanuel Kotzayan Eric Martini Eric McCarthy Eric Williams @@ -232,11 +250,13 @@ generous deed immortalized in the next stable release of Godot Engine. Igor Buzatovic Jaime Ruiz-Borau Vizárraga Jako Danar + James A F Manley Jeff Hungerford Jeremy Kahn + Jesse Dubay Joao Senerchia Joel Fivat - Johan Lindberg + Johannes Wuensch Jonas Rudlang Jonas Yamazaki Jonathan G @@ -250,11 +270,11 @@ generous deed immortalized in the next stable release of Godot Engine. Juan Negrier Judd Julian Murgia - Justin Luk KC Chan Kevin Boyer - Kevin van Rooijen + Kiyohiro Kawamura (kyorohiro) Klagsam + KR McGinley KsyTek Games Kuan Cheang kycho @@ -269,7 +289,6 @@ generous deed immortalized in the next stable release of Godot Engine. Matt Welke Maxwell Mertcan Mermerkaya - mhilbrunner Michael Dürwald Michael Gringauz Michael Labbe @@ -284,9 +303,7 @@ generous deed immortalized in the next stable release of Godot Engine. Niclas Eriksen Nicolás Montaña Nicolas SAN AGUSTIN - nitenook Pan Ip - Pascal Grüter Patrick Nafarrete Paul Gieske Paul Mason @@ -298,37 +315,42 @@ generous deed immortalized in the next stable release of Godot Engine. Prokhorenko Leonid Psyagnostic Rafael - rayos Rémi Verschelde Ricardo Alcantara Richman Stewart + Robert Farr (Larington) Roger Burgess Roger Smith Roland Rząsa Roman Tinkov + Ryan Cheung + Ryan Hentz Sasori Olkof Sebastian Michailidis Shane Spoor + Simon Wenner Sootstone Theo Cranmore Théo Hay Thibault Barbaroux Thomas Bell Thomas Holmes + Thomas Kelly Thomas Kurz tiansheng li Tim Tim Drumheller Tom Larrow - Troy Austin + Torsten Crass Tryggve Sollid Tyler Stafos UltyX Vaiktorg Victor Viktor Ferenczi - Vladimir Soukup waka nya + Wayne Haak + werner mendizabal William Gervasio William Hogben Wout Standaert diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index e81468e888..f17d7372e2 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -592,17 +592,17 @@ struct Time { }; */ -int _OS::get_static_memory_usage() const { +uint64_t _OS::get_static_memory_usage() const { return OS::get_singleton()->get_static_memory_usage(); } -int _OS::get_static_memory_peak_usage() const { +uint64_t _OS::get_static_memory_peak_usage() const { return OS::get_singleton()->get_static_memory_peak_usage(); } -int _OS::get_dynamic_memory_usage() const { +uint64_t _OS::get_dynamic_memory_usage() const { return OS::get_singleton()->get_dynamic_memory_usage(); } diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index a4b4629037..1c8b985d73 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -283,9 +283,9 @@ public: uint64_t get_system_time_secs() const; uint64_t get_system_time_msecs() const; - int get_static_memory_usage() const; - int get_static_memory_peak_usage() const; - int get_dynamic_memory_usage() const; + uint64_t get_static_memory_usage() const; + uint64_t get_static_memory_peak_usage() const; + uint64_t get_dynamic_memory_usage() const; void delay_usec(uint32_t p_usec) const; void delay_msec(uint32_t p_msec) const; diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index a0556ae839..b885a06834 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -260,8 +260,8 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { } // Check open list - SelfList<Point> *least_cost_point = NULL; - real_t least_cost = 1e30; + SelfList<Point> *least_cost_point = open_list.first(); + real_t least_cost = Math_INF; // TODO: Cache previous results for (SelfList<Point> *E = open_list.first(); E; E = E->next()) { diff --git a/core/object.cpp b/core/object.cpp index 682586a7ab..05e661baab 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1924,6 +1924,11 @@ void *Object::get_script_instance_binding(int p_script_language_index) { return _script_instance_bindings[p_script_language_index]; } +bool Object::has_script_instance_binding(int p_script_language_index) { + + return _script_instance_bindings[p_script_language_index] != NULL; +} + Object::Object() { _class_ptr = NULL; diff --git a/core/object.h b/core/object.h index a5bb6dea5d..5bfef8a439 100644 --- a/core/object.h +++ b/core/object.h @@ -729,6 +729,7 @@ public: //used by script languages to store binding data void *get_script_instance_binding(int p_script_language_index); + bool has_script_instance_binding(int p_script_language_index); void clear_internal_resource_paths(); diff --git a/core/os/os.cpp b/core/os/os.cpp index 1f967030e7..d2d39d253a 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -393,16 +393,16 @@ Error OS::dialog_input_text(String p_title, String p_description, String p_parti return OK; }; -int OS::get_static_memory_usage() const { +uint64_t OS::get_static_memory_usage() const { return Memory::get_mem_usage(); } -int OS::get_dynamic_memory_usage() const { +uint64_t OS::get_dynamic_memory_usage() const { return MemoryPool::total_memory; } -int OS::get_static_memory_peak_usage() const { +uint64_t OS::get_static_memory_peak_usage() const { return Memory::get_mem_max_usage(); } @@ -418,7 +418,7 @@ bool OS::has_touchscreen_ui_hint() const { return Input::get_singleton() && Input::get_singleton()->is_emulating_touch_from_mouse(); } -int OS::get_free_static_memory() const { +uint64_t OS::get_free_static_memory() const { return Memory::get_mem_available(); } diff --git a/core/os/os.h b/core/os/os.h index 36d4e5a8c1..20a3494e11 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -266,6 +266,7 @@ public: virtual bool has_environment(const String &p_var) const = 0; virtual String get_environment(const String &p_var) const = 0; + virtual bool set_environment(const String &p_var, const String &p_value) const = 0; virtual String get_name() = 0; virtual List<String> get_cmdline_args() const { return _cmdline; } @@ -382,10 +383,10 @@ public: virtual void print_resources_in_use(bool p_short = false); virtual void print_all_resources(String p_to_file = ""); - virtual int get_static_memory_usage() const; - virtual int get_static_memory_peak_usage() const; - virtual int get_dynamic_memory_usage() const; - virtual int get_free_static_memory() const; + virtual uint64_t get_static_memory_usage() const; + virtual uint64_t get_static_memory_peak_usage() const; + virtual uint64_t get_dynamic_memory_usage() const; + virtual uint64_t get_free_static_memory() const; RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; } diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml index c5f71fe55e..b0a21d7f82 100644 --- a/doc/classes/Expression.xml +++ b/doc/classes/Expression.xml @@ -16,7 +16,7 @@ func _on_text_entered(command): var error = expression.parse(command, []) if error != OK: - print(get_error_text()) + print(expression.get_error_text()) return var result = expression.execute([], null, true) if not expression.has_execute_failed(): diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index ce1c183242..e3e2e10e82 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -469,6 +469,11 @@ String OS_Unix::get_environment(const String &p_var) const { return ""; } +bool OS_Unix::set_environment(const String &p_var, const String &p_value) const { + + return setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ true) == 0; +} + int OS_Unix::get_processor_count() const { return sysconf(_SC_NPROCESSORS_CONF); diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index 2a23da6f28..09ab4aa1d8 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -95,6 +95,7 @@ public: virtual bool has_environment(const String &p_var) const; virtual String get_environment(const String &p_var) const; + virtual bool set_environment(const String &p_var, const String &p_value) const; virtual String get_locale() const; virtual int get_processor_count() const; diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp index 6ab41c641d..6b13d19d33 100644 --- a/editor/editor_folding.cpp +++ b/editor/editor_folding.cpp @@ -173,7 +173,7 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { for (int i = 0; i < unfolds.size(); i += 2) { NodePath path = unfolds[i]; PoolVector<String> un = unfolds[i + 1]; - Node *node = p_scene->get_node(path); + Node *node = p_scene->get_node_or_null(path); if (!node) { continue; } diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 78bc989c5f..9ece812c49 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2865,6 +2865,18 @@ void SpatialEditorViewport::update_transform_gizmo_view() { Transform xform = spatial_editor->get_gizmo_transform(); Transform camera_xform = camera->get_transform(); + + if (xform.origin.distance_squared_to(camera_xform.origin) < 0.01) { + for (int i = 0; i < 3; i++) { + VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false); + } + return; + } + Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); Plane p(camera_xform.origin, camz); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 7e4baa1533..6602d2974f 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -678,6 +678,11 @@ void SceneTreeEditor::_renamed() { error->set_text(TTR("Invalid node name, the following characters are not allowed:") + "\n" + Node::invalid_character); error->popup_centered_minsize(); + if (new_name.empty()) { + which->set_text(0, n->get_name()); + return; + } + which->set_text(0, new_name); } diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index f62391e436..4f1928d20c 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -551,7 +551,10 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Transform t = spatial_node->get_global_transform(); t.orthonormalize(); - t.set_look_at(t.origin, p_camera->get_camera_transform().origin, Vector3(0, 1, 0)); + Vector3 camera_position = p_camera->get_camera_transform().origin; + if (camera_position.distance_squared_to(t.origin) > 0.01) { + t.set_look_at(t.origin, camera_position, Vector3(0, 1, 0)); + } float scale = t.origin.distance_to(p_camera->get_camera_transform().origin); @@ -563,16 +566,18 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Point2 center = p_camera->unproject_position(t.origin); - Transform oct = p_camera->get_camera_transform(); + Transform orig_camera_transform = p_camera->get_camera_transform(); - p_camera->look_at(t.origin, Vector3(0, 1, 0)); + if (orig_camera_transform.origin.distance_squared_to(t.origin) > 0.01) { + p_camera->look_at(t.origin, Vector3(0, 1, 0)); + } Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale); Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale); Point2 p0 = p_camera->unproject_position(c0); Point2 p1 = p_camera->unproject_position(c1); - p_camera->set_global_transform(oct); + p_camera->set_global_transform(orig_camera_transform); Rect2 rect(p0, p1 - p0); diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 460ef043da..22f2214898 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -825,7 +825,8 @@ void RigidBodyBullet::reload_shapes() { // shapes incorrectly do not set the vector in calculateLocalIntertia. // Arbitrary zero is preferable to undefined behaviour. btVector3 inertia(0, 0, 0); - mainShape->calculateLocalInertia(mass, inertia); + if (EMPTY_SHAPE_PROXYTYPE != mainShape->getShapeType()) // Necessary to avoid assertion of the empty shape + mainShape->calculateLocalInertia(mass, inertia); btBody->setMassProps(mass, inertia); } btBody->updateInertiaTensor(); diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 966c02d4ec..98871ddec3 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1083,7 +1083,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (argc >= 1) { methodstr = String(*argptrs[0]) + " (via call)"; if (err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { - err.argument -= 1; + err.argument += 1; } } } else if (methodstr == "free") { diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 0b21ba3347..5da1344595 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -139,14 +139,24 @@ void CSharpLanguage::finish() { } #endif - // Release gchandle bindings before finalizing mono runtime - script_bindings.clear(); + // Make sure all script binding gchandles are released before finalizing GDMono + for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { + CSharpScriptBinding &script_binding = E->value(); + + if (script_binding.gchandle.is_valid()) { + script_binding.gchandle->release(); + script_binding.inited = false; + } + } if (gdmono) { memdelete(gdmono); gdmono = NULL; } + // Clear here, after finalizing all domains to make sure there is nothing else referencing the elements. + script_bindings.clear(); + finalizing = false; } @@ -578,7 +588,7 @@ void CSharpLanguage::frame() { if (exc) { GDMonoUtils::debug_unhandled_exception(exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); } } } @@ -1054,12 +1064,14 @@ CSharpLanguage::~CSharpLanguage() { singleton = NULL; } -void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { +bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object) { #ifdef DEBUG_ENABLED // I don't trust you - if (p_object->get_script_instance()) - CRASH_COND(NULL != CAST_CSHARP_INSTANCE(p_object->get_script_instance())); + if (p_object->get_script_instance()) { + CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance()); + CRASH_COND(csharp_instance != NULL && !csharp_instance->is_destructing_script_instance()); + } #endif StringName type_name = p_object->get_class_name(); @@ -1068,29 +1080,21 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { const ClassDB::ClassInfo *classinfo = ClassDB::classes.getptr(type_name); while (classinfo && !classinfo->exposed) classinfo = classinfo->inherits_ptr; - ERR_FAIL_NULL_V(classinfo, NULL); + ERR_FAIL_NULL_V(classinfo, false); type_name = classinfo->name; GDMonoClass *type_class = GDMonoUtils::type_get_proxy_class(type_name); - ERR_FAIL_NULL_V(type_class, NULL); + ERR_FAIL_NULL_V(type_class, false); MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object); - ERR_FAIL_NULL_V(mono_object, NULL); - - CSharpScriptBinding script_binding; - - script_binding.type_name = type_name; - script_binding.wrapper_class = type_class; // cache - script_binding.gchandle = MonoGCHandle::create_strong(mono_object); + ERR_FAIL_NULL_V(mono_object, false); - void *data; - - { - SCOPED_MUTEX_LOCK(language_bind_mutex); - data = (void *)script_bindings.insert(p_object, script_binding); - } + r_script_binding.inited = true; + r_script_binding.type_name = type_name; + r_script_binding.wrapper_class = type_class; // cache + r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object); // Tie managed to unmanaged Reference *ref = Object::cast_to<Reference>(p_object); @@ -1104,6 +1108,23 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { ref->reference(); } + return true; +} + +void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { + + CSharpScriptBinding script_binding; + + if (!setup_csharp_script_binding(script_binding, p_object)) + return NULL; + + void *data; + + { + SCOPED_MUTEX_LOCK(language_bind_mutex); + data = (void *)script_bindings.insert(p_object, script_binding); + } + return data; } @@ -1125,10 +1146,15 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; - // Set the native instance field to IntPtr.Zero, if not yet garbage collected - MonoObject *mono_object = data->value().gchandle->get_target(); - if (mono_object) { - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); + CSharpScriptBinding &script_binding = data->value(); + + if (script_binding.inited) { + // Set the native instance field to IntPtr.Zero, if not yet garbage collected. + // This is done to avoid trying to dispose the native instance from Dispose(bool). + MonoObject *mono_object = script_binding.gchandle->get_target(); + if (mono_object) { + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); + } } script_bindings.erase(data); @@ -1144,9 +1170,10 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { #endif void *data = p_object->get_script_instance_binding(get_language_index()); - if (!data) - return; - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; + CRASH_COND(!data); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 // The reference count was increased after the managed side was the only one referencing our owner. @@ -1175,9 +1202,10 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { int refcount = ref_owner->reference_get_count(); void *data = p_object->get_script_instance_binding(get_language_index()); - if (!data) - return refcount == 0; - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; + CRASH_COND(!data); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 // If owner owner is no longer referenced by the unmanaged side, @@ -1223,6 +1251,10 @@ MonoObject *CSharpInstance::get_mono_object() const { return gchandle->get_target(); } +Object *CSharpInstance::get_owner() { + return owner; +} + bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(!script.is_valid(), false); @@ -1483,14 +1515,8 @@ bool CSharpInstance::_unreference_owner_unsafe() { // Unsafe refcount decrement. The managed instance also counts as a reference. // See: _reference_owner_unsafe() - bool die = static_cast<Reference *>(owner)->unreference(); - - if (die) { - memdelete(owner); - owner = NULL; - } - - return die; + // Destroying the owner here means self destructing, so we defer the owner destruction to the caller. + return static_cast<Reference *>(owner)->unreference(); } MonoObject *CSharpInstance::_internal_new_managed() { @@ -1503,27 +1529,33 @@ MonoObject *CSharpInstance::_internal_new_managed() { ERR_FAIL_NULL_V(owner, NULL); ERR_FAIL_COND_V(script.is_null(), NULL); - if (base_ref) - _reference_owner_unsafe(); - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script->script_class->get_mono_ptr()); if (!mono_object) { + // Important to clear this before destroying the script instance here script = Ref<CSharpScript>(); - owner->set_script_instance(NULL); + owner = NULL; + + bool die = _unreference_owner_unsafe(); + // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug. + CRASH_COND(die == true); + ERR_EXPLAIN("Failed to allocate memory for the object"); ERR_FAIL_V(NULL); } + // Tie managed to unmanaged + gchandle = MonoGCHandle::create_strong(mono_object); + + if (base_ref) + _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner); // Construct GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); ctor->invoke_raw(mono_object, NULL); - // Tie managed to unmanaged - gchandle = MonoGCHandle::create_strong(mono_object); - return mono_object; } @@ -1536,25 +1568,36 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); } -void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted) { +void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) { #ifdef DEBUG_ENABLED CRASH_COND(!base_ref); CRASH_COND(gchandle.is_null()); #endif + + r_remove_script_instance = false; + if (_unreference_owner_unsafe()) { - r_owner_deleted = true; + // Safe to self destruct here with memdelete(owner), but it's deferred to the caller to prevent future mistakes. + r_delete_owner = true; } else { - r_owner_deleted = false; + r_delete_owner = false; CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); - if (p_is_finalizer && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { - // If the native instance is still alive, then it was - // referenced from another thread before the finalizer could - // unreference it and delete it, so we want to keep it. - // GC.ReRegisterForFinalize(this) is not safe because the objects - // referenced by this could have already been collected. - // Instead we will create a new managed instance here. - _internal_new_managed(); + + if (!p_is_finalizer) { + // If the native instance is still alive and Dispose() was called + // (instead of the finalizer), then we remove the script instance. + r_remove_script_instance = true; + } else if (!GDMono::get_singleton()->is_finalizing_scripts_domain()) { + // If the native instance is still alive and this is called from the finalizer, + // then it was referenced from another thread before the finalizer could + // unreference and delete it, so we want to keep it. + // GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this' + // could have already been collected. Instead we will create a new managed instance here. + MonoObject *new_managed = _internal_new_managed(); + if (!new_managed) { + r_remove_script_instance = true; + } } } } @@ -1608,7 +1651,7 @@ bool CSharpInstance::refcount_decremented() { return ref_dying; } -MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { +MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(IMonoClassMember *p_member) const { if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) return MultiplayerAPI::RPC_MODE_REMOTE; @@ -1750,6 +1793,8 @@ CSharpInstance::CSharpInstance() : CSharpInstance::~CSharpInstance() { + destructing_script_instance = true; + if (gchandle.is_valid()) { if (!predelete_notified && !ref_dying) { // This destructor is not called from the owners destructor. @@ -1762,9 +1807,7 @@ CSharpInstance::~CSharpInstance() { if (mono_object) { MonoException *exc = NULL; - destructing_script_instance = true; GDMonoUtils::dispose(mono_object, &exc); - destructing_script_instance = false; if (exc) { GDMonoUtils::set_pending_exception(exc); @@ -1772,11 +1815,23 @@ CSharpInstance::~CSharpInstance() { } } - gchandle->release(); // Make sure it's released + gchandle->release(); // Make sure the gchandle is released } - if (base_ref && !ref_dying && owner) { // it may be called from the owner's destructor - _unreference_owner_unsafe(); + // If not being called from the owner's destructor, and we still hold a reference to the owner + if (base_ref && !ref_dying && owner && unsafe_referenced) { + // The owner's script or script instance is being replaced (or removed) + + // Transfer ownership to an "instance binding" + + void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + CRASH_COND(data == NULL); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + CRASH_COND(!script_binding.inited); + + bool die = _unreference_owner_unsafe(); + CRASH_COND(die == true); // The "instance binding" should be holding a reference } if (script.is_valid() && owner) { @@ -2019,7 +2074,7 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve * Returns false if there was an error, otherwise true. * If there was an error, r_prop_info and r_exported are not assigned any value. */ -bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { +bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { StringName name = p_member->get_name(); @@ -2034,9 +2089,9 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p ManagedType type; - if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_FIELD) { + if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_FIELD) { type = static_cast<GDMonoField *>(p_member)->get_type(); - } else if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + } else if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) { type = static_cast<GDMonoProperty *>(p_member)->get_type(); } else { CRASH_NOW(); @@ -2050,7 +2105,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p return true; } - if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) { GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member); if (!property->has_getter() || !property->has_setter()) { ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String()); @@ -2327,6 +2382,32 @@ StringName CSharpScript::get_instance_base_type() const { CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error) { /* STEP 1, CREATE */ + Ref<Reference> ref; + if (p_isref) { + // Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance. + ref = Ref<Reference>(static_cast<Reference *>(p_owner)); + } + + // If the object had a script instance binding, dispose it before adding the CSharpInstance + if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) { + void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + CRASH_COND(data == NULL); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited && script_binding.gchandle.is_valid()) { + MonoObject *mono_object = script_binding.gchandle->get_target(); + if (mono_object) { + MonoException *exc = NULL; + GDMonoUtils::dispose(mono_object, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } + } + + script_binding.inited = false; + } + } CSharpInstance *instance = memnew(CSharpInstance); instance->base_ref = p_isref; @@ -2334,16 +2415,20 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg instance->owner = p_owner; instance->owner->set_script_instance(instance); - if (instance->base_ref) - instance->_reference_owner_unsafe(); - /* STEP 2, INITIALIZE AND CONSTRUCT */ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); if (!mono_object) { + // Important to clear this before destroying the script instance here instance->script = Ref<CSharpScript>(); - instance->owner->set_script_instance(NULL); + instance->owner = NULL; + + bool die = instance->_unreference_owner_unsafe(); + // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug. + CRASH_COND(die == true); + + p_owner->set_script_instance(NULL); r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; ERR_EXPLAIN("Failed to allocate memory for the object"); ERR_FAIL_V(NULL); @@ -2352,6 +2437,9 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg // Tie managed to unmanaged instance->gchandle = MonoGCHandle::create_strong(mono_object); + if (instance->base_ref) + instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + { SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); instances.insert(instance->owner); diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 8a09be79a6..db9d6a73a2 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -127,7 +127,7 @@ class CSharpScript : public Script { bool _update_exports(); #ifdef TOOLS_ENABLED - bool _get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); + bool _get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); #endif CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error); @@ -206,8 +206,15 @@ class CSharpInstance : public ScriptInstance { Ref<MonoGCHandle> gchandle; bool _reference_owner_unsafe(); + + /* + * If true is returned, the caller must memdelete the script instance's owner. + */ bool _unreference_owner_unsafe(); + /* + * If NULL is returned, the caller must destroy the script instance by removing it from its owner. + */ MonoObject *_internal_new_managed(); // Do not use unless you know what you are doing @@ -216,13 +223,15 @@ class CSharpInstance : public ScriptInstance { void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount); - MultiplayerAPI::RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const; + MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const; public: MonoObject *get_mono_object() const; _FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; } + virtual Object *get_owner(); + virtual bool set(const StringName &p_name, const Variant &p_value); virtual bool get(const StringName &p_name, Variant &r_ret) const; virtual void get_property_list(List<PropertyInfo> *p_properties) const; @@ -235,7 +244,12 @@ public: virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); void mono_object_disposed(MonoObject *p_obj); - void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted); + + /* + * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if + * 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner. + */ + void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance); virtual void refcount_incremented(); virtual bool refcount_decremented(); @@ -255,6 +269,7 @@ public: }; struct CSharpScriptBinding { + bool inited; StringName type_name; GDMonoClass *wrapper_class; Ref<MonoGCHandle> gchandle; @@ -391,6 +406,8 @@ public: virtual void refcount_incremented_instance_binding(Object *p_object); virtual bool refcount_decremented_instance_binding(Object *p_object); + bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object); + #ifdef DEBUG_ENABLED Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace); #endif diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 5d9f4d8d54..a040e4a344 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -517,7 +517,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { // Remove old issues file - String issues_file = "msbuild_issues.csv"; + String issues_file = get_msbuild_issues_filename(); DirAccessRef d = DirAccess::create_for_path(log_dirpath); if (d->file_exists(issues_file)) { Error err = d->remove(issues_file); @@ -584,7 +584,8 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { exit_code = klass->get_field("exitCode")->get_int_value(mono_object); if (exit_code != 0) { - print_verbose("MSBuild finished with exit code " + itos(exit_code)); + String log_filepath = build_info.get_log_dirpath().plus_file(get_msbuild_log_filename()); + print_verbose("MSBuild exited with code: " + itos(exit_code) + ". Log file: " + log_filepath); } build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR); diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h index 2d53583916..652d30538a 100644 --- a/modules/mono/editor/godotsharp_builds.h +++ b/modules/mono/editor/godotsharp_builds.h @@ -76,6 +76,9 @@ public: static void show_build_error_dialog(const String &p_message); + static const char *get_msbuild_issues_filename() { return "msbuild_issues.csv"; } + static const char *get_msbuild_log_filename() { return "msbuild_log.txt"; } + void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code); void restart_build(MonoBuildTab *p_build_tab); diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index cee4405826..ee93229700 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -30,6 +30,7 @@ #include "godotsharp_editor.h" +#include "core/message_queue.h" #include "core/os/os.h" #include "core/project_settings.h" #include "scene/gui/control.h" @@ -114,10 +115,33 @@ bool GodotSharpEditor::_create_project_solution() { void GodotSharpEditor::_make_api_solutions_if_needed() { // I'm sick entirely of ProgressDialog + + static int attempts_left = 100; + + if (MessageQueue::get_singleton()->is_flushing() || !SceneTree::get_singleton()) { + ERR_FAIL_COND(attempts_left == 0); // You've got to be kidding + + if (SceneTree::get_singleton()) { + SceneTree::get_singleton()->connect("idle_frame", this, "_make_api_solutions_if_needed", Vector<Variant>()); + } else { + call_deferred("_make_api_solutions_if_needed"); + } + + attempts_left--; + return; + } + + // Recursion guard needed because signals don't play well with ProgressDialog either, but unlike + // the message queue, with signals the collateral damage should be minimal in the worst case. static bool recursion_guard = false; if (!recursion_guard) { recursion_guard = true; + + // Oneshot signals don't play well with ProgressDialog either, so we do it this way instead + SceneTree::get_singleton()->disconnect("idle_frame", this, "_make_api_solutions_if_needed"); + _make_api_solutions_if_needed_impl(); + recursion_guard = false; } } @@ -434,7 +458,7 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { String csproj_path = GodotSharpDirs::get_project_csproj_path(); if (FileAccess::exists(sln_path) && FileAccess::exists(csproj_path)) { - // We can't use EditorProgress here. It calls Main::iterarion() and the main loop is not initialized yet. + // Defer this task because EditorProgress calls Main::iterarion() and the main loop is not yet initialized. call_deferred("_make_api_solutions_if_needed"); } else { bottom_panel_btn->hide(); @@ -452,7 +476,7 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { EditorSettings *ed_settings = EditorSettings::get_singleton(); EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE); - String settings_hint_str = "None"; + String settings_hint_str = "Disabled"; #ifdef WINDOWS_ENABLED settings_hint_str += ",MonoDevelop,Visual Studio Code"; diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index 55a334bc4e..d3efa288e9 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -187,7 +187,7 @@ void MonoBottomPanel::_view_log_pressed() { String log_dirpath = build_tab->get_build_info().get_log_dirpath(); - OS::get_singleton()->shell_open(log_dirpath.plus_file("msbuild_log.txt")); + OS::get_singleton()->shell_open(log_dirpath.plus_file(GodotSharpBuilds::get_msbuild_log_filename())); } } @@ -421,7 +421,7 @@ void MonoBuildTab::on_build_exit(BuildResult result) { build_exited = true; build_result = result; - _load_issues_from_file(logs_dir.plus_file("msbuild_issues.csv")); + _load_issues_from_file(logs_dir.plus_file(GodotSharpBuilds::get_msbuild_issues_filename())); _update_issues_list(); MonoBottomPanel::get_singleton()->raise_build_tab(this); diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 88adc3e256..2963619b79 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -65,9 +65,12 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); if (data) { - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; - if (gchandle.is_valid()) { - CSharpLanguage::release_script_gchandle(p_obj, gchandle); + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited) { + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + if (gchandle.is_valid()) { + CSharpLanguage::release_script_gchandle(p_obj, gchandle); + } } } } @@ -85,11 +88,14 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance()); if (cs_instance) { if (!cs_instance->is_destructing_script_instance()) { - bool r_owner_deleted; - cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, r_owner_deleted); - if (!r_owner_deleted && !p_is_finalizer) { - // If the native instance is still alive and Dispose() was called - // (instead of the finalizer), then we remove the script instance. + bool delete_owner; + bool remove_script_instance; + + cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance); + + if (delete_owner) { + memdelete(ref); + } else if (remove_script_instance) { ref->set_script_instance(NULL); } } @@ -105,9 +111,12 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_ void *data = ref->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); if (data) { - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; - if (gchandle.is_valid()) { - CSharpLanguage::release_script_gchandle(p_obj, gchandle); + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited) { + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + if (gchandle.is_valid()) { + CSharpLanguage::release_script_gchandle(p_obj, gchandle); + } } } } @@ -138,7 +147,7 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj) { wref->set_obj(p_obj); } - return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr())); + return GDMonoUtils::unmanaged_get_managed(wref.ptr()); } Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) { diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index b6c9b5f7dd..a9e2136a19 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -65,7 +65,7 @@ Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) { void MonoGCHandle::release() { #ifdef DEBUG_ENABLED - CRASH_COND(GDMono::get_singleton() == NULL); + CRASH_COND(!released && GDMono::get_singleton() == NULL); #endif if (!released && GDMono::get_singleton()->is_runtime_initialized()) { diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 900caefe1c..ca1ff9f9ff 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -54,17 +54,6 @@ #include "main/main.h" #endif -#ifdef MONO_PRINT_HANDLER_ENABLED -void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { - - if (is_stdout) { - OS::get_singleton()->print(string); - } else { - OS::get_singleton()->printerr(string); - } -} -#endif - GDMono *GDMono::singleton = NULL; namespace { @@ -132,7 +121,7 @@ void gdmono_debug_init() { CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); - if (da_args == "") { + if (da_args.length() == 0) { da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) .utf8(); @@ -150,6 +139,40 @@ void gdmono_debug_init() { } // namespace +void GDMono::add_mono_shared_libs_dir_to_path() { + // By default Mono seems to search shared libraries in the following directories: + // Current working directory, @executable_path@ and PATH + // The parent directory of the image file (assembly where the dllimport method is declared) + // @executable_path@/../lib + // @executable_path@/../Libraries (__MACH__ only) + + // This does not work when embedding Mono unless we use the same directory structure. + // To fix this we append the directory containing our shared libraries to PATH. + +#if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED) + String path_var("PATH"); + String path_value = OS::get_singleton()->get_environment(path_var); + +#ifdef WINDOWS_ENABLED + path_value += ';'; + + String bundled_bin_dir = GodotSharpDirs::get_data_mono_bin_dir(); + path_value += DirAccess::exists(bundled_bin_dir) ? bundled_bin_dir : mono_reg_info.bin_dir; +#else + path_value += ':'; + + String bundled_lib_dir = GodotSharpDirs::get_data_mono_lib_dir(); + if (DirAccess::exists(bundled_lib_dir)) { + path_value += bundled_lib_dir; + } else { + // TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms? + } +#endif + + OS::get_singleton()->set_environment(path_var, path_value); +#endif +} + void GDMono::initialize() { ERR_FAIL_NULL(Engine::get_singleton()); @@ -162,11 +185,6 @@ void GDMono::initialize() { GDMonoLog::get_singleton()->initialize(); -#ifdef MONO_PRINT_HANDLER_ENABLED - mono_trace_set_print_handler(gdmono_MonoPrintCallback); - mono_trace_set_printerr_handler(gdmono_MonoPrintCallback); -#endif - String assembly_rootdir; String config_dir; @@ -223,6 +241,8 @@ void GDMono::initialize() { mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL, config_dir.length() ? config_dir.utf8().get_data() : NULL); + add_mono_shared_libs_dir_to_path(); + GDMonoAssembly::initialize(); #ifdef DEBUG_ENABLED @@ -327,7 +347,7 @@ namespace GodotSharpBindings { uint64_t get_core_api_hash(); #ifdef TOOLS_ENABLED uint64_t get_editor_api_hash(); -#endif // TOOLS_ENABLED +#endif uint32_t get_bindings_version(); void register_generated_icalls(); @@ -344,29 +364,20 @@ void GDMono::_register_internal_calls() { #endif } -#ifdef DEBUG_METHODS_ENABLED void GDMono::_initialize_and_check_api_hashes() { - api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); - #ifdef MONO_GLUE_ENABLED - if (api_core_hash != GodotSharpBindings::get_core_api_hash()) { + if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) { ERR_PRINT("Mono: Core API hash mismatch!"); } -#endif #ifdef TOOLS_ENABLED - api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); - -#ifdef MONO_GLUE_ENABLED - if (api_editor_hash != GodotSharpBindings::get_editor_api_hash()) { + if (get_api_editor_hash() != GodotSharpBindings::get_editor_api_hash()) { ERR_PRINT("Mono: Editor API hash mismatch!"); } -#endif - #endif // TOOLS_ENABLED +#endif // MONO_GLUE_ENABLED } -#endif // DEBUG_METHODS_ENABLED void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) { @@ -915,7 +926,7 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { ScriptDebugger::get_singleton()->idle_poll(); #endif abort(); - _UNREACHABLE_(); + GD_UNREACHABLE(); } GDMono::GDMono() { @@ -946,12 +957,10 @@ GDMono::GDMono() { editor_tools_assembly = NULL; #endif -#ifdef DEBUG_METHODS_ENABLED api_core_hash = 0; #ifdef TOOLS_ENABLED api_editor_hash = 0; #endif -#endif } GDMono::~GDMono() { @@ -1074,17 +1083,9 @@ void _GodotSharp::_bind_methods() { _GodotSharp::_GodotSharp() { singleton = this; - queue_empty = true; -#ifndef NO_THREADS - queue_mutex = Mutex::create(); -#endif } _GodotSharp::~_GodotSharp() { singleton = NULL; - - if (queue_mutex) { - memdelete(queue_mutex); - } } diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 13de3061b3..785b65ce13 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -134,13 +134,11 @@ class GDMono { Error _load_tools_domain(); #endif -#ifdef DEBUG_METHODS_ENABLED uint64_t api_core_hash; #ifdef TOOLS_ENABLED uint64_t api_editor_hash; #endif void _initialize_and_check_api_hashes(); -#endif GDMonoLog *gdmono_log; @@ -148,15 +146,23 @@ class GDMono { MonoRegInfo mono_reg_info; #endif + void add_mono_shared_libs_dir_to_path(); + protected: static GDMono *singleton; public: -#ifdef DEBUG_METHODS_ENABLED - uint64_t get_api_core_hash() { return api_core_hash; } + uint64_t get_api_core_hash() { + if (api_core_hash == 0) + api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); + return api_core_hash; + } #ifdef TOOLS_ENABLED - uint64_t get_api_editor_hash() { return api_editor_hash; } -#endif + uint64_t get_api_editor_hash() { + if (api_editor_hash == 0) + api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); + return api_editor_hash; + } #endif #ifdef TOOLS_ENABLED @@ -268,12 +274,6 @@ class _GodotSharp : public Object { List<NodePath *> np_delete_queue; List<RID *> rid_delete_queue; - bool queue_empty; - -#ifndef NO_THREADS - Mutex *queue_mutex; -#endif - protected: static _GodotSharp *singleton; static void _bind_methods(); diff --git a/modules/mono/mono_gd/gd_mono_class_member.h b/modules/mono/mono_gd/gd_mono_class_member.h index 5bd21178ba..3058b18c05 100644 --- a/modules/mono/mono_gd/gd_mono_class_member.h +++ b/modules/mono/mono_gd/gd_mono_class_member.h @@ -35,7 +35,7 @@ #include <mono/metadata/object.h> -class GDMonoClassMember { +class IMonoClassMember { public: enum Visibility { PRIVATE, @@ -51,7 +51,7 @@ public: MEMBER_TYPE_METHOD }; - virtual ~GDMonoClassMember() {} + virtual ~IMonoClassMember() {} virtual MemberType get_member_type() = 0; diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index a9d993412e..48fa380456 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -505,20 +505,20 @@ bool GDMonoField::is_static() { return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoField::get_visibility() { +IMonoClassMember::Visibility GDMonoField::get_visibility() { switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) { case MONO_FIELD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_FIELD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_FIELD_ATTR_ASSEMBLY: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_FIELD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_FIELD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h index 319e895ab9..a3244101d8 100644 --- a/modules/mono/mono_gd/gd_mono_field.h +++ b/modules/mono/mono_gd/gd_mono_field.h @@ -35,7 +35,7 @@ #include "gd_mono_class_member.h" #include "gd_mono_header.h" -class GDMonoField : public GDMonoClassMember { +class GDMonoField : public IMonoClassMember { GDMonoClass *owner; MonoClassField *mono_field; @@ -47,15 +47,15 @@ class GDMonoField : public GDMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_FIELD; } + virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_FIELD; } - virtual StringName get_name() { return name; } + virtual StringName get_name() GD_FINAL { return name; } - virtual bool is_static(); - virtual Visibility get_visibility(); + virtual bool is_static() GD_FINAL; + virtual Visibility get_visibility() GD_FINAL; - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; void fetch_attributes(); _FORCE_INLINE_ ManagedType get_type() const { return type; } diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index 23306d11b9..dd8c047386 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -35,7 +35,7 @@ class GDMonoAssembly; class GDMonoClass; -class GDMonoClassMember; +class IMonoClassMember; class GDMonoField; class GDMonoProperty; class GDMonoMethod; diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index 0574b0025d..0daa394e63 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -72,7 +72,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { void unhandled_exception(MonoException *p_exc) { mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well abort(); - _UNREACHABLE_(); + GD_UNREACHABLE(); } } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index 09cb59ee6b..2d77bde27c 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -45,7 +45,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged); * Do not call this function directly. * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead. */ -_NO_RETURN_ void unhandled_exception(MonoException *p_exc); +GD_NORETURN void unhandled_exception(MonoException *p_exc); } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index fa4850ca8c..ec89df1959 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -52,7 +52,7 @@ static int log_level_get_id(const char *p_log_level) { return -1; } -void gdmono_MonoLogCallback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { +static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { FileAccess *f = GDMonoLog::get_singleton()->get_log_file(); @@ -153,7 +153,7 @@ void GDMonoLog::initialize() { if (log_file) { print_verbose("Mono: Logfile is " + log_file_path); - mono_trace_set_log_handler(gdmono_MonoLogCallback, this); + mono_trace_set_log_handler(mono_log_callback, this); } else { OS::get_singleton()->printerr("Mono: No log file, using default log handler\n"); } diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 5e2deea43c..4f86e02f87 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -206,9 +206,10 @@ enum { // In the future we may force this if we want to ref return these structs #ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY -// Sometimes clang-format can be an ass -GD_STATIC_ASSERT(MATCHES_Vector2 &&MATCHES_Rect2 &&MATCHES_Transform2D &&MATCHES_Vector3 && - MATCHES_Basis &&MATCHES_Quat &&MATCHES_Transform &&MATCHES_AABB &&MATCHES_Color &&MATCHES_Plane); +/* clang-format off */ +GD_STATIC_ASSERT(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && + MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&MATCHES_Plane); +/* clang-format on */ #endif } // namespace InteropLayout diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 071d852ce7..7f11e4671d 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -78,20 +78,20 @@ bool GDMonoMethod::is_static() { return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoMethod::get_visibility() { +IMonoClassMember::Visibility GDMonoMethod::get_visibility() { switch (mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { case MONO_METHOD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_METHOD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_METHOD_ATTR_ASSEM: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_METHOD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_METHOD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index c4a994177e..fc035d387c 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -35,7 +35,7 @@ #include "gd_mono_class_member.h" #include "gd_mono_header.h" -class GDMonoMethod : public GDMonoClassMember { +class GDMonoMethod : public IMonoClassMember { StringName name; @@ -57,17 +57,17 @@ class GDMonoMethod : public GDMonoClassMember { MonoMethod *mono_method; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_METHOD; } + virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_METHOD; } - virtual StringName get_name() { return name; } + virtual StringName get_name() GD_FINAL { return name; } - virtual bool is_static(); + virtual bool is_static() GD_FINAL; - virtual Visibility get_visibility(); + virtual Visibility get_visibility() GD_FINAL; - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); - virtual void fetch_attributes(); + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; + void fetch_attributes(); _FORCE_INLINE_ int get_parameters_count() { return params_count; } _FORCE_INLINE_ ManagedType get_return_type() { return return_type; } diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index 04fd8b8e63..5842e26241 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -80,24 +80,24 @@ bool GDMonoProperty::is_static() { return mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoProperty::get_visibility() { +IMonoClassMember::Visibility GDMonoProperty::get_visibility() { MonoMethod *prop_method = mono_property_get_get_method(mono_property); if (prop_method == NULL) prop_method = mono_property_get_set_method(mono_property); switch (mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { case MONO_METHOD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_METHOD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_METHOD_ATTR_ASSEM: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_METHOD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_METHOD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 934ce68904..d8a9f69ffb 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -35,7 +35,7 @@ #include "gd_mono_class_member.h" #include "gd_mono_header.h" -class GDMonoProperty : public GDMonoClassMember { +class GDMonoProperty : public IMonoClassMember { GDMonoClass *owner; MonoProperty *mono_property; @@ -47,15 +47,15 @@ class GDMonoProperty : public GDMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_PROPERTY; } + virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_PROPERTY; } - virtual StringName get_name() { return name; } + virtual StringName get_name() GD_FINAL { return name; } - virtual bool is_static(); - virtual Visibility get_visibility(); + virtual bool is_static() GD_FINAL; + virtual Visibility get_visibility() GD_FINAL; - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; void fetch_attributes(); bool has_getter(); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 97a00a504d..ac6ccac3a7 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -265,61 +265,70 @@ void clear_cache() { } MonoObject *unmanaged_get_managed(Object *unmanaged) { - if (unmanaged) { - if (unmanaged->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance()); - if (cs_instance) { - return cs_instance->get_mono_object(); - } + if (!unmanaged) + return NULL; + + if (unmanaged->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance()); + + if (cs_instance) { + return cs_instance->get_mono_object(); } + } + + // If the owner does not have a CSharpInstance... + + void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + + if (!data) + return NULL; - // If the owner does not have a CSharpInstance... + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); - void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + if (!script_binding.inited) { + // Already had a binding that needs to be setup + CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged); - if (data) { - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); + if (!script_binding.inited) + return NULL; + } - Ref<MonoGCHandle> &gchandle = script_binding.gchandle; - ERR_FAIL_COND_V(gchandle.is_null(), NULL); + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + ERR_FAIL_COND_V(gchandle.is_null(), NULL); - MonoObject *target = gchandle->get_target(); + MonoObject *target = gchandle->get_target(); - if (target) - return target; + if (target) + return target; - CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); - // Create a new one + // Create a new one #ifdef DEBUG_ENABLED - CRASH_COND(script_binding.type_name == StringName()); - CRASH_COND(script_binding.wrapper_class == NULL); + CRASH_COND(script_binding.type_name == StringName()); + CRASH_COND(script_binding.wrapper_class == NULL); #endif - MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); - ERR_FAIL_NULL_V(mono_object, NULL); - - gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE); + MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); + ERR_FAIL_NULL_V(mono_object, NULL); - // Tie managed to unmanaged - Reference *ref = Object::cast_to<Reference>(unmanaged); + gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE); - if (ref) { - // Unsafe refcount increment. The managed instance also counts as a reference. - // This way if the unmanaged world has no references to our owner - // but the managed instance is alive, the refcount will be 1 instead of 0. - // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) + // Tie managed to unmanaged + Reference *ref = Object::cast_to<Reference>(unmanaged); - ref->reference(); - } + if (ref) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) - return mono_object; - } + ref->reference(); } - return NULL; + return mono_object; } void set_main_thread(MonoThread *p_thread) { @@ -565,7 +574,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { if (unexpected_exc) { GDMonoInternals::unhandled_exception(unexpected_exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); } Vector<ScriptLanguage::StackInfo> _si; @@ -604,7 +613,7 @@ void debug_unhandled_exception(MonoException *p_exc) { ScriptDebugger::get_singleton()->idle_poll(); #endif GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well - _UNREACHABLE_(); + GD_UNREACHABLE(); } void print_unhandled_exception(MonoException *p_exc) { @@ -615,7 +624,7 @@ void set_pending_exception(MonoException *p_exc) { #ifdef HAS_PENDING_EXCEPTIONS if (get_runtime_invoke_count() == 0) { debug_unhandled_exception(p_exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); } if (!mono_runtime_set_pending_exception(p_exc, false)) { @@ -624,7 +633,7 @@ void set_pending_exception(MonoException *p_exc) { } #else debug_unhandled_exception(p_exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); #endif } diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 6febc50a5f..e88bf1ced9 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -44,7 +44,7 @@ #define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \ if (unlikely(m_exc != NULL)) { \ GDMonoUtils::debug_unhandled_exception(m_exc); \ - _UNREACHABLE_(); \ + GD_UNREACHABLE(); \ } namespace GDMonoUtils { @@ -214,7 +214,7 @@ void set_exception_message(MonoException *p_exc, String message); void debug_print_unhandled_exception(MonoException *p_exc); void debug_send_unhandled_exception_error(MonoException *p_exc); -_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc); +GD_NORETURN void debug_unhandled_exception(MonoException *p_exc); void print_unhandled_exception(MonoException *p_exc); /** diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index 94e1193adf..87295a98f2 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -31,39 +31,57 @@ #ifndef UTIL_MACROS_H #define UTIL_MACROS_H -#define _GD_VARNAME_CONCAT_B(m_ignore, m_name) m_name -#define _GD_VARNAME_CONCAT_A(m_a, m_b, m_c) _GD_VARNAME_CONCAT_B(hello there, m_a##m_b##m_c) -#define _GD_VARNAME_CONCAT(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A(m_a, m_b, m_c) -#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT(m_name, _, __COUNTER__) +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(attr_token) 0 +#endif -// noreturn +#define _GD_VARNAME_CONCAT_B_(m_ignore, m_name) m_name +#define _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_B_(hello there, m_a##m_b##m_c) +#define _GD_VARNAME_CONCAT_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c) +#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT_(m_name, _, __COUNTER__) -#if __cpp_static_assert +// static assert +// TODO: Get rid of this macro once we upgrade to C++11 + +#ifdef __cpp_static_assert #define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed") #else #define GD_STATIC_ASSERT(m_cond) typedef int GD_UNIQUE_NAME(godot_static_assert)[((m_cond) ? 1 : -1)] #endif -#undef _NO_RETURN_ +// final +// TODO: Get rid of this macro once we upgrade to C++11 + +#if (__cplusplus >= 201103L) +#define GD_FINAL final +#else +#define GD_FINAL +#endif + +// noreturn +// TODO: Get rid of this macro once we upgrade to C++11 -#ifdef __GNUC__ -#define _NO_RETURN_ __attribute__((noreturn)) -#elif _MSC_VER -#define _NO_RETURN_ __declspec(noreturn) +#if __has_cpp_attribute(deprecated) +#define GD_NORETURN [[noreturn]] +#elif defined(__GNUC__) +#define GD_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define GD_NORETURN __declspec(noreturn) #else -#error Platform or compiler not supported +#define GD_NORETURN +#pragma message "Macro GD_NORETURN will have no effect" #endif // unreachable #if defined(_MSC_VER) -#define _UNREACHABLE_() __assume(0) +#define GD_UNREACHABLE() __assume(0) #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 -#define _UNREACHABLE_() __builtin_unreachable() +#define GD_UNREACHABLE() __builtin_unreachable() #else -#define _UNREACHABLE_() \ - CRASH_NOW(); \ - do { \ +#define GD_UNREACHABLE() \ + CRASH_NOW(); \ + do { \ } while (true); #endif diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp index 5ed982200f..f1362be249 100644 --- a/modules/mono/utils/osx_utils.cpp +++ b/modules/mono/utils/osx_utils.cpp @@ -30,10 +30,10 @@ #include "osx_utils.h" -#include "core/print_string.h" - #ifdef OSX_ENABLED +#include "core/print_string.h" + #include <CoreFoundation/CoreFoundation.h> #include <CoreServices/CoreServices.h> diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 31fcbc0427..36a753e683 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -126,7 +126,7 @@ def configure(env): env.Append(CPPPATH=['#platform/osx']) env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED', '-DCOREMIDI_ENABLED']) - env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) + env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'CoreVideo']) env.Append(LIBS=['pthread']) env.Append(CPPFLAGS=['-mmacosx-version-min=10.9']) diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 262079fa89..927c8c9b00 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -43,8 +43,10 @@ #include "servers/visual/rasterizer.h" #include "servers/visual/visual_server_wrap_mt.h" #include "servers/visual_server.h" +#include <AppKit/AppKit.h> #include <AppKit/NSCursor.h> #include <ApplicationServices/ApplicationServices.h> +#include <CoreVideo/CoreVideo.h> #undef CursorShape /** @@ -102,10 +104,13 @@ public: id window_view; id autoreleasePool; id cursor; - id pixelFormat; - id context; + NSOpenGLPixelFormat *pixelFormat; + NSOpenGLContext *context; bool layered_window; + bool waiting_for_vsync; + NSCondition *vsync_condition; + CVDisplayLinkRef displayLink; CursorShape cursor_shape; NSCursor *cursors[CURSOR_MAX]; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 760858b2e5..6b65c1a529 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -117,6 +117,21 @@ static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFacto return Vector2(mouse_x, mouse_y); } +// DisplayLinkCallback is called from our DisplayLink OS thread informing us right before +// a screen update is required. We can use it to work around the broken vsync. +static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) { + OS_OSX *os = (OS_OSX *)displayLinkContext; + + // Set flag so we know we can output our next frame and signal our conditional lock + // if we're not doing vsync this will be ignored + [os->vsync_condition lock]; + os->waiting_for_vsync = false; + [os->vsync_condition signal]; + [os->vsync_condition unlock]; + + return kCVReturnSuccess; +} + @interface GodotApplication : NSApplication @end @@ -267,7 +282,9 @@ static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFacto NSWindow *window = (NSWindow *)[notification object]; CGFloat newBackingScaleFactor = [window backingScaleFactor]; CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; - [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:YES]; + if (OS_OSX::singleton->is_hidpi_allowed()) { + [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:YES]; + } if (newBackingScaleFactor != oldBackingScaleFactor) { //Set new display scale and window size @@ -1366,6 +1383,15 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a [context makeCurrentContext]; + // setup our display link, this will inform us when a refresh is needed + CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); + CVDisplayLinkSetOutputCallback(displayLink, &DisplayLinkCallback, this); + CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, context.CGLContextObj, pixelFormat.CGLPixelFormatObj); + CVDisplayLinkStart(displayLink); + + // initialise a conditional lock object + vsync_condition = [[NSCondition alloc] init]; + set_use_vsync(p_desired.use_vsync); [NSApp activateIgnoringOtherApps:YES]; @@ -1453,6 +1479,11 @@ void OS_OSX::finalize() { midi_driver.close(); #endif + if (displayLink) { + CVDisplayLinkRelease(displayLink); + } + [vsync_condition release]; + CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL); CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL); @@ -1967,6 +1998,17 @@ String OS_OSX::get_locale() const { } void OS_OSX::swap_buffers() { + if (is_vsync_enabled()) { + // Wait until our DisplayLink callback unsets our flag... + [vsync_condition lock]; + while (waiting_for_vsync) + [vsync_condition wait]; + + // Make sure we wait again next frame around + waiting_for_vsync = true; + + [vsync_condition unlock]; + } [context flushBuffer]; } @@ -2633,22 +2675,22 @@ Error OS_OSX::move_to_trash(const String &p_path) { } void OS_OSX::_set_use_vsync(bool p_enable) { - CGLContextObj ctx = CGLGetCurrentContext(); + // CGLCPSwapInterval broke in OSX 10.14 and it seems Apple is not interested in fixing + // it as OpenGL is now deprecated and Metal solves this differently. + // Following SDLs example we're working around this using DisplayLink + // When vsync is enabled we set a flag "waiting_for_vsync" to true. + // This flag is set to false when DisplayLink informs us our display is about to refresh. + + /* CGLContextObj ctx = CGLGetCurrentContext(); if (ctx) { GLint swapInterval = p_enable ? 1 : 0; CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval); - } -} -/* -bool OS_OSX::is_vsync_enabled() const { - GLint swapInterval = 0; - CGLContextObj ctx = CGLGetCurrentContext(); - if (ctx) { - CGLGetParameter(ctx, kCGLCPSwapInterval, &swapInterval); - } - return swapInterval ? true : false; + }*/ + + ///TODO Maybe pause/unpause display link? + waiting_for_vsync = p_enable; } -*/ + OS_OSX *OS_OSX::singleton = NULL; OS_OSX::OS_OSX() { diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index ea0193b8ed..ea4f63b49c 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -739,6 +739,11 @@ String OSUWP::get_environment(const String &p_var) const { return ""; }; +bool OSUWP::set_environment(const String &p_var, const String &p_value) const { + + return false; +} + String OSUWP::get_stdin_string(bool p_block) { return String(); diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 491c9bce03..5475c4e60a 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -213,6 +213,7 @@ public: virtual bool has_environment(const String &p_var) const; virtual String get_environment(const String &p_var) const; + virtual bool set_environment(const String &p_var, const String &p_value) const; virtual void set_clipboard(const String &p_text); virtual String get_clipboard() const; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 35f9d541ef..9ae1be9afd 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -2604,6 +2604,11 @@ String OS_Windows::get_environment(const String &p_var) const { return ""; } +bool OS_Windows::set_environment(const String &p_var, const String &p_value) const { + + return (bool)SetEnvironmentVariableW(p_var.c_str(), p_value.c_str()); +} + String OS_Windows::get_stdin_string(bool p_block) { if (p_block) { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 4936a69120..771789c86b 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -268,6 +268,7 @@ public: virtual bool has_environment(const String &p_var) const; virtual String get_environment(const String &p_var) const; + virtual bool set_environment(const String &p_var, const String &p_value) const; virtual void set_clipboard(const String &p_text); virtual String get_clipboard() const; diff --git a/platform/x11/SCsub b/platform/x11/SCsub index 97d3d1b514..e302f09c88 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -13,6 +13,7 @@ common_x11 = [ "key_mapping_x11.cpp", "joypad_linux.cpp", "power_x11.cpp", + "detect_prime.cpp" ] prog = env.add_program('#bin/godot', ['godot_x11.cpp'] + common_x11) diff --git a/platform/x11/detect_prime.cpp b/platform/x11/detect_prime.cpp new file mode 100644 index 0000000000..04a825fac9 --- /dev/null +++ b/platform/x11/detect_prime.cpp @@ -0,0 +1,233 @@ +/*************************************************************************/ +/* detect_prime.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifdef X11_ENABLED +#if defined(OPENGL_ENABLED) + +#include "core/print_string.h" +#include "core/ustring.h" + +#include <stdlib.h> + +#include <GL/gl.h> +#include <GL/glx.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include <cstring> + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 + +typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *); + +struct vendor { + const char *glxvendor; + int priority; +}; + +vendor vendormap[] = { + { "Advanced Micro Devices, Inc.", 30 }, + { "NVIDIA Corporation", 30 }, + { "X.Org", 30 }, + { "Intel Open Source Technology Center", 20 }, + { "nouveau", 10 }, + { "Mesa Project", 0 }, + { NULL, 0 } +}; + +// Runs inside a child. Exiting will not quit the engine. +void create_context() { + Display *x11_display = XOpenDisplay(NULL); + Window x11_window; + GLXContext glx_context; + + GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB"); + + static int visual_attribs[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_DOUBLEBUFFER, true, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DEPTH_SIZE, 24, + None + }; + + int fbcount; + GLXFBConfig fbconfig = 0; + XVisualInfo *vi = NULL; + + XSetWindowAttributes swa; + swa.event_mask = StructureNotifyMask; + swa.border_pixel = 0; + unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask; + + GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount); + if (!fbc) + exit(1); + + vi = glXGetVisualFromFBConfig(x11_display, fbc[0]); + + fbconfig = fbc[0]; + + static int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + None + }; + + glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs); + + swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); + x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, 10, 10, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa); + + if (!x11_window) + exit(1); + + glXMakeCurrent(x11_display, x11_window, glx_context); + XFree(vi); +} + +int detect_prime() { + pid_t p; + int priorities[2]; + String vendors[2]; + String renderers[2]; + + vendors[0] = "Unknown"; + vendors[1] = "Unknown"; + renderers[0] = "Unknown"; + renderers[1] = "Unknown"; + + for (int i = 0; i < 2; ++i) { + int fdset[2]; + + if (pipe(fdset) == -1) { + print_verbose("Failed to pipe(), using default GPU"); + return 0; + } + + // Fork so the driver initialization can crash without taking down the engine. + p = fork(); + + if (p > 0) { + // Main thread + + int stat_loc = 0; + char string[201]; + string[200] = '\0'; + + close(fdset[1]); + + waitpid(p, &stat_loc, 0); + + if (!stat_loc) { + // No need to do anything complicated here. Anything less than + // PIPE_BUF will be delivered in one read() call. + read(fdset[0], string, sizeof(string) - 1); + + vendors[i] = string; + renderers[i] = string + strlen(string) + 1; + } + + close(fdset[0]); + + } else { + // In child, exit() here will not quit the engine. + + char string[201]; + + close(fdset[0]); + + if (i) setenv("DRI_PRIME", "1", 1); + create_context(); + + const char *vendor = (const char *)glGetString(GL_VENDOR); + const char *renderer = (const char *)glGetString(GL_RENDERER); + + int vendor_len = strlen(vendor) + 1; + int renderer_len = strlen(renderer) + 1; + + if (vendor_len + renderer_len >= sizeof(string)) { + renderer_len = 200 - vendor_len; + } + + memcpy(&string, vendor, vendor_len); + memcpy(&string[vendor_len], renderer, renderer_len); + + write(fdset[1], string, vendor_len + renderer_len); + + close(fdset[1]); + exit(0); + } + } + + int preferred = 0; + int priority = 0; + + if (vendors[0] == vendors[1]) { + print_verbose("Only one GPU found, using default."); + return 0; + } + + for (int i = 1; i >= 0; --i) { + vendor *v = vendormap; + while (v->glxvendor) { + if (v->glxvendor == vendors[i]) { + priorities[i] = v->priority; + + if (v->priority >= priority) { + priority = v->priority; + preferred = i; + } + } + ++v; + } + } + + print_verbose("Found renderers:"); + for (int i = 0; i < 2; ++i) { + print_verbose("Renderer " + itos(i) + ": " + renderers[i] + " with priority: " + itos(priorities[i])); + } + + print_verbose("Using renderer: " + renderers[preferred]); + return preferred; +} + +#endif +#endif diff --git a/platform/x11/detect_prime.h b/platform/x11/detect_prime.h new file mode 100644 index 0000000000..13bcf6fc38 --- /dev/null +++ b/platform/x11/detect_prime.h @@ -0,0 +1,37 @@ +/*************************************************************************/ +/* detect_prime.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifdef X11_ENABLED +#if defined(OPENGL_ENABLED) + +int detect_prime(); + +#endif +#endif diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 0db79fa3e9..e0924fc982 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "os_x11.h" +#include "detect_prime.h" + #include "core/os/dir_access.h" #include "core/print_string.h" #include "drivers/gles2/rasterizer_gles2.h" @@ -240,28 +242,15 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a // maybe contextgl wants to be in charge of creating the window #if defined(OPENGL_ENABLED) - // Set DRI_PRIME if not set. This means that Godot should default to a higher-power GPU if it exists. - // Note: Due to the final '0' parameter to setenv any existing DRI_PRIME environment variables will not - // be overwritten. - bool enable_dri_prime = true; - // Check if Nouveau is loaded, we don't want to force dGPU usage with that driver. - if (FileAccess *f = FileAccess::open("/proc/modules", FileAccess::READ)) { - // Match driver name + space - String nouveau_str = "nouveau "; - - while (!f->eof_reached()) { - String line = f->get_line(); - - if (line.begins_with(nouveau_str)) { - enable_dri_prime = false; - break; - } + if (getenv("DRI_PRIME") == NULL) { + print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic."); + int use_prime = detect_prime(); + + if (use_prime) { + print_line("Found discrete GPU, setting DRI_PRIME=1 to use it."); + print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU."); + setenv("DRI_PRIME", "1", 1); } - f->close(); - memdelete(f); - } - if (enable_dri_prime) { - setenv("DRI_PRIME", "1", 0); } ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE; diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index dced688899..41cee7335e 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -134,7 +134,7 @@ void CanvasItemMaterial::_update_shader() { code += "\tint total_frames = particles_anim_h_frames * particles_anim_v_frames;\n"; code += "\tint frame = int(float(total_frames) * INSTANCE_CUSTOM.z);\n"; code += "\tif (particles_anim_loop) {\n"; - code += "\t\tframe = abs(frame) % total_frames;\n"; + code += "\t\tframe = int(abs(frame) % total_frames);\n"; code += "\t} else {\n"; code += "\t\tframe = clamp(frame, 0, total_frames - 1);\n"; code += "\t}\n"; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index a3fa2ac98d..6b70eef662 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -382,36 +382,30 @@ void Sprite3D::_draw() { VS::get_singleton()->immediate_clear(immediate); if (!texture.is_valid()) - return; //no texuture no life + return; Vector2 tsize = texture->get_size(); if (tsize.x == 0 || tsize.y == 0) return; - Size2i s; - Rect2 src_rect; - - if (region) { - - s = region_rect.size; - src_rect = region_rect; - } else { - s = texture->get_size(); - s = s / Size2(hframes, vframes); + Rect2 base_rect; + if (region) + base_rect = region_rect; + else + base_rect = Rect2(0, 0, texture->get_width(), texture->get_height()); - src_rect.size = s; - src_rect.position.x += (frame % hframes) * s.x; - src_rect.position.y += (frame / hframes) * s.y; - } + Size2 frame_size = base_rect.size / Size2(hframes, vframes); + Point2 frame_offset = Point2(frame % hframes, frame / hframes); + frame_offset *= frame_size; - Point2 ofs = get_offset(); + Point2 dest_offset = get_offset(); if (is_centered()) - ofs -= s / 2; - - Rect2 dst_rect(ofs, s); + dest_offset -= frame_size / 2; + Rect2 src_rect(base_rect.position + frame_offset, frame_size); + Rect2 final_dst_rect(dest_offset, frame_size); Rect2 final_rect; Rect2 final_src_rect; - if (!texture->get_rect_region(dst_rect, src_rect, final_rect, final_src_rect)) + if (!texture->get_rect_region(final_dst_rect, src_rect, final_rect, final_src_rect)) return; if (final_rect.size.x == 0 || final_rect.size.y == 0) diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 1d2001c30d..4c249b117e 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -344,10 +344,16 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float for (int i = 0; i < a->get_track_count(); i++) { + // If an animation changes this animation (or it animates itself) + // we need to recreate our animation cache + if (p_anim->node_cache.size() != a->get_track_count()) { + _ensure_node_caches(p_anim); + } + TrackNodeCache *nc = p_anim->node_cache[i]; - if (!nc) // no node cache for this track, skip it - continue; + if (!nc) + continue; // no node cache for this track, skip it if (!a->track_is_enabled(i)) continue; // do nothing if the track is disabled diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 307a421016..4b1c7b3db7 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -82,11 +82,8 @@ void ColorPicker::_notification(int p_what) { } break; case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: { - if (screen != NULL) { - if (screen->is_visible()) { - screen->hide(); - } - } + if (screen != NULL && screen->is_visible()) + screen->hide(); } break; } } @@ -500,21 +497,17 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event) { void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> bev = p_event; - - if (bev.is_valid()) { - - if (bev->get_button_index() == BUTTON_LEFT && !bev->is_pressed()) { - emit_signal("color_changed", color); - screen->hide(); - } + if (bev.is_valid() && bev->get_button_index() == BUTTON_LEFT && !bev->is_pressed()) { + emit_signal("color_changed", color); + screen->hide(); } Ref<InputEventMouseMotion> mev = p_event; - if (mev.is_valid()) { Viewport *r = get_tree()->get_root(); if (!r->get_visible_rect().has_point(Point2(mev->get_global_position().x, mev->get_global_position().y))) return; + Ref<Image> img = r->get_texture()->get_data(); if (img.is_valid() && !img->empty()) { img->lock(); @@ -540,6 +533,8 @@ void ColorPicker::_screen_pick_pressed() { screen->set_anchors_and_margins_preset(Control::PRESET_WIDE); screen->set_default_cursor_shape(CURSOR_POINTING_HAND); screen->connect("gui_input", this, "_screen_input"); + // It immediately toggles off in the first press otherwise. + screen->call_deferred("connect", "hide", btn_pick, "set_pressed", varray(false)); } screen->raise(); screen->show_modal(); @@ -830,8 +825,6 @@ void ColorPickerButton::_update_picker() { add_child(popup); picker->connect("color_changed", this, "_color_changed"); popup->connect("modal_closed", this, "_modal_closed"); - popup->connect("about_to_show", this, "set_pressed", varray(true)); - popup->connect("popup_hide", this, "set_pressed", varray(false)); picker->set_pick_color(color); picker->set_edit_alpha(edit_alpha); } @@ -862,6 +855,4 @@ ColorPickerButton::ColorPickerButton() { picker = NULL; popup = NULL; edit_alpha = true; - - set_toggle_mode(true); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 09fbb39866..4abde6cc0f 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2645,24 +2645,26 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } case KEY_UP: { - if (k->get_shift()) - _pre_shift_selection(); if (k->get_alt()) { scancode_handled = false; break; } #ifndef APPLE_STYLE_KEYS if (k->get_command()) { - _scroll_lines_up(); - break; - } #else if (k->get_command() && k->get_alt()) { +#endif _scroll_lines_up(); break; } + if (k->get_shift()) { + _pre_shift_selection(); + } + +#ifdef APPLE_STYLE_KEYS if (k->get_command()) { + cursor_set_line(0); } else #endif @@ -2696,24 +2698,24 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } case KEY_DOWN: { - if (k->get_shift()) - _pre_shift_selection(); if (k->get_alt()) { scancode_handled = false; break; } #ifndef APPLE_STYLE_KEYS if (k->get_command()) { - _scroll_lines_down(); - break; - } - #else if (k->get_command() && k->get_alt()) { +#endif _scroll_lines_down(); break; } + if (k->get_shift()) { + _pre_shift_selection(); + } + +#ifdef APPLE_STYLE_KEYS if (k->get_command()) { cursor_set_line(get_last_unhidden_line(), true, false, 9999); } else diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 6aa3891035..04d7107fa4 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1311,7 +1311,7 @@ Node *Node::_get_child_by_name(const StringName &p_name) const { return NULL; } -Node *Node::_get_node(const NodePath &p_path) const { +Node *Node::get_node_or_null(const NodePath &p_path) const { if (!data.inside_tree && p_path.is_absolute()) { ERR_EXPLAIN("Can't use get_node() with absolute paths from outside the active scene tree."); @@ -1376,7 +1376,7 @@ Node *Node::_get_node(const NodePath &p_path) const { Node *Node::get_node(const NodePath &p_path) const { - Node *node = _get_node(p_path); + Node *node = get_node_or_null(p_path); if (!node) { ERR_EXPLAIN("Node not found: " + p_path); ERR_FAIL_COND_V(!node, NULL); @@ -1386,7 +1386,7 @@ Node *Node::get_node(const NodePath &p_path) const { bool Node::has_node(const NodePath &p_path) const { - return _get_node(p_path) != NULL; + return get_node_or_null(p_path) != NULL; } Node *Node::find_node(const String &p_mask, bool p_recursive, bool p_owned) const { @@ -2709,6 +2709,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_child", "idx"), &Node::get_child); ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node); ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node); + ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null); ClassDB::bind_method(D_METHOD("get_parent"), &Node::get_parent); ClassDB::bind_method(D_METHOD("find_node", "mask", "recursive", "owned"), &Node::find_node, DEFVAL(true), DEFVAL(true)); ClassDB::bind_method(D_METHOD("find_parent", "mask"), &Node::find_parent); diff --git a/scene/main/node.h b/scene/main/node.h index 1c451ef567..e6189389cb 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -153,7 +153,6 @@ private: void _print_tree_pretty(const String prefix, const bool last); void _print_tree(const Node *p_node); - Node *_get_node(const NodePath &p_path) const; Node *_get_child_by_name(const StringName &p_name) const; void _replace_connections_target(Node *p_new_target); @@ -252,6 +251,7 @@ public: Node *get_child(int p_index) const; bool has_node(const NodePath &p_path) const; Node *get_node(const NodePath &p_path) const; + Node *get_node_or_null(const NodePath &p_path) const; Node *find_node(const String &p_mask, bool p_recursive = true, bool p_owned = true) const; bool has_node_and_resource(const NodePath &p_path) const; Node *get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index ea810edf8c..f28a67b493 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -55,7 +55,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { Node *p_name; \ if (p_id & FLAG_ID_IS_PATH) { \ NodePath np = node_paths[p_id & FLAG_MASK]; \ - p_name = ret_nodes[0]->_get_node(np); \ + p_name = ret_nodes[0]->get_node_or_null(np); \ } else { \ ERR_FAIL_INDEX_V(p_id &FLAG_MASK, nc, NULL); \ p_name = ret_nodes[p_id & FLAG_MASK]; \ @@ -342,7 +342,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { } for (int i = 0; i < editable_instances.size(); i++) { - Node *ei = ret_nodes[0]->_get_node(editable_instances[i]); + Node *ei = ret_nodes[0]->get_node_or_null(editable_instances[i]); if (ei) { ret_nodes[0]->set_editable_instance(ei, true); } diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 5a2e7245a2..3c83de91fd 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -614,7 +614,7 @@ Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, if (coords.size() == 0) { return autotile_get_icon_coordinate(p_id); } else { - return coords[Math::random(0, (int)coords.size())]; + return coords[Math::rand() % coords.size()]; } } |