From dd9dc75a8331d9e86285b88e805e72c5f68231c3 Mon Sep 17 00:00:00 2001 From: Lukas Tenbrink Date: Thu, 27 Mar 2025 15:39:53 +0100 Subject: [PATCH] Optimize `Object::cast_to` by assuming no virtual and multiple inheritance, gaining 8x throughput over `dynamic_cast`. Add `-Wvirtual-inheritance` to compiler warnings as a sanity check. --- SConstruct | 7 ++++++- core/object/object.h | 27 +++++++++++++++++++++++++-- scene/gui/dialogs.compat.inc | 2 ++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/SConstruct b/SConstruct index 24491ccbaea..46acbdbc056 100644 --- a/SConstruct +++ b/SConstruct @@ -860,7 +860,12 @@ else: # GCC, Clang common_warnings = [] if methods.using_gcc(env): - common_warnings += ["-Wshadow", "-Wno-misleading-indentation"] + common_warnings += [ + "-Wshadow", + "-Wno-misleading-indentation", + # For optimized Object::cast_to / object.inherits_from() + "-Wvirtual-inheritance", + ] if cc_version_major < 11: # Regression in GCC 9/10, spams so much in our variadic templates # that we need to outright disable it. diff --git a/core/object/object.h b/core/object/object.h index 41f20f7d0b7..38d0ad7f410 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -440,6 +440,9 @@ public: } \ \ protected: \ + virtual bool _derives_from(const std::type_info &p_type_info) const override { \ + return typeid(m_class) == p_type_info || m_inherits::_derives_from(p_type_info); \ + } \ _FORCE_INLINE_ static void (*_get_bind_methods())() { \ return &m_class::_bind_methods; \ } \ @@ -768,6 +771,12 @@ protected: mutable VirtualMethodTracker *virtual_method_list = nullptr; #endif + virtual bool _derives_from(const std::type_info &p_type_info) const { + // This could just be false because nobody would reasonably ask if an Object subclass derives from Object, + // but it would be wrong if somebody actually does ask. It's not too slow to check anyway. + return typeid(Object) == p_type_info; + } + public: // Should be protected, but bug in clang++. static void initialize_class(); _FORCE_INLINE_ static void register_custom_data_to_otdb() {} @@ -787,12 +796,26 @@ public: template static T *cast_to(Object *p_object) { - return p_object ? dynamic_cast(p_object) : nullptr; + // This is like dynamic_cast, but faster. + // The reason is that we can assume no virtual and multiple inheritance. + static_assert(std::is_base_of_v, "T must be derived from Object"); + if constexpr (std::is_same_v, typename T::self_type>) { + return p_object && p_object->_derives_from(typeid(T)) ? static_cast(p_object) : nullptr; + } else { + // T does not use GDCLASS, must fall back to dynamic_cast. + return p_object ? dynamic_cast(p_object) : nullptr; + } } template static const T *cast_to(const Object *p_object) { - return p_object ? dynamic_cast(p_object) : nullptr; + static_assert(std::is_base_of_v, "T must be derived from Object"); + if constexpr (std::is_same_v, typename T::self_type>) { + return p_object && p_object->_derives_from(typeid(T)) ? static_cast(p_object) : nullptr; + } else { + // T does not use GDCLASS, must fall back to dynamic_cast. + return p_object ? dynamic_cast(p_object) : nullptr; + } } enum { diff --git a/scene/gui/dialogs.compat.inc b/scene/gui/dialogs.compat.inc index e114675a895..acfc9f0a186 100644 --- a/scene/gui/dialogs.compat.inc +++ b/scene/gui/dialogs.compat.inc @@ -30,6 +30,8 @@ #ifndef DISABLE_DEPRECATED +#include "scene/gui/line_edit.h" + void AcceptDialog::_register_text_enter_bind_compat_89419(Control *p_line_edit) { register_text_enter(Object::cast_to(p_line_edit)); }