소스 검색

Update for virtual method compatibility system

David Snopek 7 달 전
부모
커밋
5c9529fc84
5개의 변경된 파일574개의 추가작업 그리고 143개의 파일을 삭제
  1. 1 1
      binding_generator.py
  2. 550 129
      gdextension/extension_api.json
  3. 4 2
      gdextension/gdextension_interface.h
  4. 10 5
      include/godot_cpp/core/class_db.hpp
  5. 9 6
      src/core/class_db.cpp

+ 1 - 1
binding_generator.py

@@ -1788,7 +1788,7 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
                     # condition returns false (in such cases it can't compile due to ambiguity).
                     f"\t\tif constexpr (!std::is_same_v<decltype(&B::{method_name}), decltype(&T::{method_name})>) {{"
                 )
-                result.append(f"\t\t\tBIND_VIRTUAL_METHOD(T, {method_name});")
+                result.append(f"\t\t\tBIND_VIRTUAL_METHOD(T, {method_name}, {method['hash']});")
                 result.append("\t\t}")
 
     result.append("\t}")

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 550 - 129
gdextension/extension_api.json


+ 4 - 2
gdextension/gdextension_interface.h

@@ -273,7 +273,9 @@ typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_us
 typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
 typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
 typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
+typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
 typedef void *(*GDExtensionClassGetVirtualCallData)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
+typedef void *(*GDExtensionClassGetVirtualCallData2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
 typedef void (*GDExtensionClassCallVirtualWithData)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, void *p_virtual_call_userdata, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
 
 typedef struct {
@@ -383,14 +385,14 @@ typedef struct {
 	GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
 	GDExtensionClassRecreateInstance recreate_instance_func;
 	// Queries a virtual function by name and returns a callback to invoke the requested virtual function.
-	GDExtensionClassGetVirtual get_virtual_func;
+	GDExtensionClassGetVirtual2 get_virtual_func;
 	// Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that
 	// need or benefit from extra data when calling virtual functions.
 	// Returns user data that will be passed to `call_virtual_with_data_func`.
 	// Returning `NULL` from this function signals to Godot that the virtual function is not overridden.
 	// Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.
 	// You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`.
-	GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+	GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
 	// Used to call virtual functions when `get_virtual_call_data_func` is not null.
 	GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
 	void *class_userdata; // Per-class user data, later accessible in instance bindings.

+ 10 - 5
include/godot_cpp/core/class_db.hpp

@@ -88,12 +88,17 @@ class ClassDB {
 
 public:
 	struct ClassInfo {
+		struct VirtualMethod {
+			GDExtensionClassCallVirtual func;
+			uint32_t hash;
+		};
+
 		StringName name;
 		StringName parent_name;
 		GDExtensionInitializationLevel level = GDEXTENSION_INITIALIZATION_SCENE;
 		std::unordered_map<StringName, MethodBind *> method_map;
 		std::set<StringName> signal_names;
-		std::unordered_map<StringName, GDExtensionClassCallVirtual> virtual_methods;
+		std::unordered_map<StringName, VirtualMethod> virtual_methods;
 		std::set<StringName> property_names;
 		std::set<StringName> constant_names;
 		// Pointer to the parent custom class, if any. Will be null if the parent class is a Godot class.
@@ -193,13 +198,13 @@ public:
 	static void add_signal(const StringName &p_class, const MethodInfo &p_signal);
 	static void bind_integer_constant(const StringName &p_class_name, const StringName &p_enum_name, const StringName &p_constant_name, GDExtensionInt p_constant_value, bool p_is_bitfield = false);
 	// Binds an implementation of a virtual method defined in Godot.
-	static void bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call);
+	static void bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call, uint32_t p_hash);
 	// Add a new virtual method that can be implemented by scripts.
 	static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, const Vector<StringName> &p_arg_names = Vector<StringName>());
 
 	static MethodBind *get_method(const StringName &p_class, const StringName &p_method);
 
-	static GDExtensionClassCallVirtual get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name);
+	static GDExtensionClassCallVirtual get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
 	static const GDExtensionInstanceBindingCallbacks *get_instance_binding_callbacks(const StringName &p_class);
 
 	static void initialize(GDExtensionInitializationLevel p_level);
@@ -217,12 +222,12 @@ public:
 #define BIND_BITFIELD_FLAG(m_constant) \
 	::godot::ClassDB::bind_integer_constant(get_class_static(), ::godot::_gde_constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true);
 
-#define BIND_VIRTUAL_METHOD(m_class, m_method)                                                                                                \
+#define BIND_VIRTUAL_METHOD(m_class, m_method, m_hash)                                                                                        \
 	{                                                                                                                                         \
 		auto _call##m_method = [](GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) -> void { \
 			call_with_ptr_args(reinterpret_cast<m_class *>(p_instance), &m_class::m_method, p_args, p_ret);                                   \
 		};                                                                                                                                    \
-		::godot::ClassDB::bind_virtual_method(m_class::get_class_static(), #m_method, _call##m_method);                                       \
+		::godot::ClassDB::bind_virtual_method(m_class::get_class_static(), #m_method, _call##m_method, m_hash);                               \
 	}
 
 template <typename T, bool is_abstract>

+ 9 - 6
src/core/class_db.cpp

@@ -285,7 +285,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class_name, const String
 	// Register it with Godot
 	internal::gdextension_interface_classdb_register_extension_class_integer_constant(internal::library, p_class_name._native_ptr(), p_enum_name._native_ptr(), p_constant_name._native_ptr(), p_constant_value, p_is_bitfield);
 }
-GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name) {
+GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash) {
 	// This is called by Godot the first time it calls a virtual function, and it caches the result, per object instance.
 	// Because of this, it can happen from different threads at once.
 	// It should be ok not using any mutex as long as we only READ data.
@@ -299,10 +299,10 @@ GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtens
 
 	// Find method in current class, or any of its parent classes (Godot classes not included)
 	while (type != nullptr) {
-		std::unordered_map<StringName, GDExtensionClassCallVirtual>::const_iterator method_it = type->virtual_methods.find(*name);
+		std::unordered_map<StringName, ClassInfo::VirtualMethod>::const_iterator method_it = type->virtual_methods.find(*name);
 
-		if (method_it != type->virtual_methods.end()) {
-			return method_it->second;
+		if (method_it != type->virtual_methods.end() && method_it->second.hash == p_hash) {
+			return method_it->second.func;
 		}
 
 		type = type->parent_ptr;
@@ -328,7 +328,7 @@ const GDExtensionInstanceBindingCallbacks *ClassDB::get_instance_binding_callbac
 	return callbacks_it->second;
 }
 
-void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call) {
+void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call, uint32_t p_hash) {
 	std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(p_class);
 	ERR_FAIL_COND_MSG(type_it == classes.end(), String("Class '{0}' doesn't exist.").format(Array::make(p_class)));
 
@@ -337,7 +337,10 @@ void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p
 	ERR_FAIL_COND_MSG(type.method_map.find(p_method) != type.method_map.end(), String("Method '{0}::{1}()' already registered as non-virtual.").format(Array::make(p_class, p_method)));
 	ERR_FAIL_COND_MSG(type.virtual_methods.find(p_method) != type.virtual_methods.end(), String("Virtual '{0}::{1}()' method already registered.").format(Array::make(p_class, p_method)));
 
-	type.virtual_methods[p_method] = p_call;
+	type.virtual_methods[p_method] = ClassInfo::VirtualMethod{
+		p_call,
+		p_hash,
+	};
 }
 
 void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, const Vector<StringName> &p_arg_names) {

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.