瀏覽代碼

Rewrite `HashMapHasherDefault` based on type traits - it is now possible to declare a default hashing function for any type.
Remove cross-project includes from `hashfuncs.h`.
Improve hashing function for `Color` (based on values instead of `String`).
Move `Variant` comparison from `hash_map.h` to `dictionary.cpp` (`VariantComparatorDictionary`), where it's used.
Remove now unnecessary `HashableHasher`.

Lukas Tenbrink 5 月之前
父節點
當前提交
ad600125df

+ 11 - 0
core/math/aabb.h

@@ -32,6 +32,7 @@
 
 #include "core/math/plane.h"
 #include "core/math/vector3.h"
+#include "core/templates/hashfuncs.h"
 
 /**
  * AABB (Axis Aligned Bounding Box)
@@ -131,6 +132,16 @@ struct [[nodiscard]] AABB {
 		return position + (size * 0.5f);
 	}
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_real(position.x);
+		h = hash_murmur3_one_real(position.y, h);
+		h = hash_murmur3_one_real(position.z, h);
+		h = hash_murmur3_one_real(size.x, h);
+		h = hash_murmur3_one_real(size.y, h);
+		h = hash_murmur3_one_real(size.z, h);
+		return hash_fmix32(h);
+	}
+
 	explicit operator String() const;
 
 	AABB() = default;

+ 9 - 0
core/math/color.h

@@ -31,6 +31,7 @@
 #pragma once
 
 #include "core/math/math_funcs.h"
+#include "core/templates/hashfuncs.h"
 
 class String;
 
@@ -239,6 +240,14 @@ struct [[nodiscard]] Color {
 	_FORCE_INLINE_ void set_ok_hsl_s(float p_s) { set_ok_hsl(get_ok_hsl_h(), p_s, get_ok_hsl_l(), a); }
 	_FORCE_INLINE_ void set_ok_hsl_l(float p_l) { set_ok_hsl(get_ok_hsl_h(), get_ok_hsl_s(), p_l, a); }
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_float(r);
+		h = hash_murmur3_one_float(r, h);
+		h = hash_murmur3_one_float(b, h);
+		h = hash_murmur3_one_float(a, h);
+		return hash_fmix32(h);
+	}
+
 	constexpr Color() :
 			r(0), g(0), b(0), a(1) {}
 

+ 1 - 0
core/math/delaunay_3d.h

@@ -33,6 +33,7 @@
 #include "core/math/aabb.h"
 #include "core/math/projection.h"
 #include "core/math/vector3.h"
+#include "core/math/vector3i.h"
 #include "core/templates/a_hash_map.h"
 #include "core/templates/list.h"
 #include "core/templates/local_vector.h"

+ 2 - 0
core/math/geometry_3d.h

@@ -30,8 +30,10 @@
 
 #pragma once
 
+#include "core/math/color.h"
 #include "core/math/delaunay_3d.h"
 #include "core/math/face3.h"
+#include "core/math/vector2.h"
 #include "core/templates/local_vector.h"
 #include "core/templates/vector.h"
 

+ 9 - 0
core/math/rect2.h

@@ -32,6 +32,7 @@
 
 #include "core/error/error_macros.h"
 #include "core/math/vector2.h"
+#include "core/templates/hashfuncs.h"
 
 class String;
 struct Rect2i;
@@ -361,6 +362,14 @@ struct [[nodiscard]] Rect2 {
 	explicit operator String() const;
 	operator Rect2i() const;
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_real(position.x);
+		h = hash_murmur3_one_real(position.y, h);
+		h = hash_murmur3_one_real(size.x, h);
+		h = hash_murmur3_one_real(size.y, h);
+		return hash_fmix32(h);
+	}
+
 	Rect2() = default;
 	constexpr Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) :
 			position(Point2(p_x, p_y)),

+ 9 - 0
core/math/rect2i.h

@@ -32,6 +32,7 @@
 
 #include "core/error/error_macros.h"
 #include "core/math/vector2i.h"
+#include "core/templates/hashfuncs.h"
 
 class String;
 struct Rect2;
@@ -226,6 +227,14 @@ struct [[nodiscard]] Rect2i {
 	explicit operator String() const;
 	operator Rect2() const;
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_32(uint32_t(position.x));
+		h = hash_murmur3_one_32(uint32_t(position.y), h);
+		h = hash_murmur3_one_32(uint32_t(size.x), h);
+		h = hash_murmur3_one_32(uint32_t(size.y), h);
+		return hash_fmix32(h);
+	}
+
 	Rect2i() = default;
 	constexpr Rect2i(int p_x, int p_y, int p_width, int p_height) :
 			position(Point2i(p_x, p_y)),

+ 7 - 0
core/math/vector2.h

@@ -32,6 +32,7 @@
 
 #include "core/error/error_macros.h"
 #include "core/math/math_funcs.h"
+#include "core/templates/hashfuncs.h"
 
 class String;
 struct Vector2i;
@@ -190,6 +191,12 @@ struct [[nodiscard]] Vector2 {
 	explicit operator String() const;
 	operator Vector2i() const;
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_real(x);
+		h = hash_murmur3_one_real(y, h);
+		return hash_fmix32(h);
+	}
+
 	// NOLINTBEGIN(cppcoreguidelines-pro-type-member-init)
 	constexpr Vector2() :
 			x(0), y(0) {}

+ 7 - 0
core/math/vector2i.h

@@ -32,6 +32,7 @@
 
 #include "core/error/error_macros.h"
 #include "core/math/math_funcs.h"
+#include "core/templates/hashfuncs.h"
 
 class String;
 struct Vector2;
@@ -147,6 +148,12 @@ struct [[nodiscard]] Vector2i {
 	explicit operator String() const;
 	operator Vector2() const;
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_32(uint32_t(x));
+		h = hash_murmur3_one_32(uint32_t(y), h);
+		return hash_fmix32(h);
+	}
+
 	// NOLINTBEGIN(cppcoreguidelines-pro-type-member-init)
 	constexpr Vector2i() :
 			x(0), y(0) {}

+ 7 - 0
core/math/vector3.h

@@ -214,6 +214,13 @@ struct [[nodiscard]] Vector3 {
 	explicit operator String() const;
 	operator Vector3i() const;
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_real(x);
+		h = hash_murmur3_one_real(y, h);
+		h = hash_murmur3_one_real(z, h);
+		return hash_fmix32(h);
+	}
+
 	constexpr Vector3() :
 			x(0), y(0), z(0) {}
 	constexpr Vector3(real_t p_x, real_t p_y, real_t p_z) :

+ 8 - 0
core/math/vector3i.h

@@ -32,6 +32,7 @@
 
 #include "core/error/error_macros.h"
 #include "core/math/math_funcs.h"
+#include "core/templates/hashfuncs.h"
 
 class String;
 struct Vector3;
@@ -140,6 +141,13 @@ struct [[nodiscard]] Vector3i {
 	explicit operator String() const;
 	operator Vector3() const;
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_32(uint32_t(x));
+		h = hash_murmur3_one_32(uint32_t(y), h);
+		h = hash_murmur3_one_32(uint32_t(z), h);
+		return hash_fmix32(h);
+	}
+
 	constexpr Vector3i() :
 			x(0), y(0), z(0) {}
 	constexpr Vector3i(int32_t p_x, int32_t p_y, int32_t p_z) :

+ 9 - 0
core/math/vector4.h

@@ -32,6 +32,7 @@
 
 #include "core/error/error_macros.h"
 #include "core/math/math_defs.h"
+#include "core/templates/hashfuncs.h"
 #include "core/typedefs.h"
 
 class String;
@@ -146,6 +147,14 @@ struct [[nodiscard]] Vector4 {
 	explicit operator String() const;
 	operator Vector4i() const;
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_real(x);
+		h = hash_murmur3_one_real(y, h);
+		h = hash_murmur3_one_real(z, h);
+		h = hash_murmur3_one_real(w, h);
+		return hash_fmix32(h);
+	}
+
 	constexpr Vector4() :
 			x(0), y(0), z(0), w(0) {}
 	constexpr Vector4(real_t p_x, real_t p_y, real_t p_z, real_t p_w) :

+ 9 - 0
core/math/vector4i.h

@@ -32,6 +32,7 @@
 
 #include "core/error/error_macros.h"
 #include "core/math/math_funcs.h"
+#include "core/templates/hashfuncs.h"
 
 class String;
 struct Vector4;
@@ -135,6 +136,14 @@ struct [[nodiscard]] Vector4i {
 	explicit operator String() const;
 	operator Vector4() const;
 
+	uint32_t hash() const {
+		uint32_t h = hash_murmur3_one_32(uint32_t(x));
+		h = hash_murmur3_one_32(uint32_t(y), h);
+		h = hash_murmur3_one_32(uint32_t(z), h);
+		h = hash_murmur3_one_32(uint32_t(w), h);
+		return hash_fmix32(h);
+	}
+
 	constexpr Vector4i() :
 			x(0), y(0), z(0), w(0) {}
 	Vector4i(const Vector4 &p_vec4);

+ 1 - 1
core/object/object.h

@@ -641,7 +641,7 @@ private:
 		};
 
 		MethodInfo user;
-		HashMap<Callable, Slot, HashableHasher<Callable>> slot_map;
+		HashMap<Callable, Slot> slot_map;
 		bool removable = false;
 	};
 	friend struct _ObjectSignalLock;

+ 3 - 0
core/object/object_id.h

@@ -30,6 +30,7 @@
 
 #pragma once
 
+#include "core/templates/hashfuncs.h"
 #include "core/typedefs.h"
 
 // Class to store an object ID (int64)
@@ -54,6 +55,8 @@ public:
 	_ALWAYS_INLINE_ void operator=(int64_t p_int64) { id = p_int64; }
 	_ALWAYS_INLINE_ void operator=(uint64_t p_uint64) { id = p_uint64; }
 
+	uint32_t hash() const { return HashMapHasherDefault::hash(id); }
+
 	_ALWAYS_INLINE_ ObjectID() {}
 	_ALWAYS_INLINE_ explicit ObjectID(const uint64_t p_id) { id = p_id; }
 	_ALWAYS_INLINE_ explicit ObjectID(const int64_t p_id) { id = p_id; }

+ 2 - 0
core/object/ref_counted.h

@@ -216,6 +216,8 @@ public:
 		ref(memnew(T(p_params...)));
 	}
 
+	uint32_t hash() const { return HashMapHasherDefault::hash(reference); }
+
 	Ref() = default;
 
 	~Ref() {

+ 3 - 0
core/string/ustring.h

@@ -34,6 +34,7 @@
 
 #include "core/string/char_utils.h" // IWYU pragma: export
 #include "core/templates/cowdata.h"
+#include "core/templates/hashfuncs.h"
 #include "core/templates/vector.h"
 #include "core/typedefs.h"
 #include "core/variant/array.h"
@@ -232,6 +233,8 @@ public:
 		return *this;
 	}
 
+	uint32_t hash() const { return hash_djb2(get_data()); }
+
 protected:
 	void copy_from(const T *p_cstr) {
 		if (!p_cstr) {

+ 4 - 0
core/templates/a_hash_map.h

@@ -30,8 +30,12 @@
 
 #pragma once
 
+#include "core/string/ustring.h"
 #include "core/templates/hash_map.h"
 
+class StringName;
+class Variant;
+
 /**
  * An array-based implementation of a hash map. It is very efficient in terms of performance and
  * memory usage. Works like a dynamic array, adding elements to the end of the array, and

+ 106 - 186
core/templates/hashfuncs.h

@@ -30,37 +30,17 @@
 
 #pragma once
 
-#include "core/math/aabb.h"
-#include "core/math/basis.h"
-#include "core/math/color.h"
 #include "core/math/math_defs.h"
 #include "core/math/math_funcs.h"
-#include "core/math/plane.h"
-#include "core/math/projection.h"
-#include "core/math/quaternion.h"
-#include "core/math/rect2.h"
-#include "core/math/rect2i.h"
-#include "core/math/transform_2d.h"
-#include "core/math/transform_3d.h"
-#include "core/math/vector2.h"
-#include "core/math/vector2i.h"
-#include "core/math/vector3.h"
-#include "core/math/vector3i.h"
-#include "core/math/vector4.h"
-#include "core/math/vector4i.h"
-#include "core/object/object_id.h"
-#include "core/string/node_path.h"
-#include "core/string/string_name.h"
-#include "core/string/ustring.h"
-#include "core/templates/pair.h"
-#include "core/templates/rid.h"
 #include "core/typedefs.h"
-#include "core/variant/callable.h"
 
 #ifdef _MSC_VER
 #include <intrin.h> // Needed for `__umulh` below.
 #endif
 
+template <typename F, typename S>
+struct Pair;
+
 /**
  * Hashing functions
  */
@@ -315,228 +295,168 @@ static _FORCE_INLINE_ uint64_t hash_make_uint64_t(T p_in) {
 	return _u._u64;
 }
 
+template <typename, typename = std::void_t<>>
+struct has_hash_method : std::false_type {};
+
 template <typename T>
-class Ref;
+struct has_hash_method<T, std::void_t<std::is_same<decltype(std::declval<const T>().hash()), uint32_t>>> : std::true_type {};
 
-struct HashMapHasherDefault {
-	// Generic hash function for any type.
-	template <typename T>
-	static _FORCE_INLINE_ uint32_t hash(const T *p_pointer) { return hash_one_uint64((uint64_t)p_pointer); }
+template <typename T>
+constexpr bool has_hash_method_v = has_hash_method<T>::value;
+
+template <typename T, typename = void>
+struct HashMapHasherDefaultImpl {
+};
 
+struct HashMapHasherDefault {
 	template <typename T>
-	static _FORCE_INLINE_ uint32_t hash(const Ref<T> &p_ref) { return hash_one_uint64((uint64_t)p_ref.operator->()); }
+	static _FORCE_INLINE_ uint32_t hash(const T &p_type) {
+		return HashMapHasherDefaultImpl<std::decay_t<T>>::hash(p_type);
+	}
+};
 
-	template <typename F, typename S>
-	static _FORCE_INLINE_ uint32_t hash(const Pair<F, S> &p_pair) {
-		uint64_t h1 = hash(p_pair.first);
-		uint64_t h2 = hash(p_pair.second);
-		return hash_one_uint64((h1 << 32) | h2);
+template <typename T>
+struct HashMapHasherDefaultImpl<T, std::enable_if_t<has_hash_method_v<T>>> {
+	// For self hashing types.
+	static _FORCE_INLINE_ uint32_t hash(const T &p_value) {
+		return p_value.hash();
 	}
+};
 
-	static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); }
-	static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
-	static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(uint32_t(p_wchar)); }
-	static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(uint32_t(p_uchar)); }
-	static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(uint32_t(p_uchar)); }
-	static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
-	static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.get_data()); }
-	static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
-	static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
-	static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }
-	static _FORCE_INLINE_ uint32_t hash(const Callable &p_callable) { return p_callable.hash(); }
+template <typename T>
+struct HashMapHasherDefaultImpl<T *> {
+	// For pointer types.
+	static _FORCE_INLINE_ uint32_t hash(const T *p_pointer) { return hash_one_uint64((uint64_t)p_pointer); }
+};
 
-	static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
-	static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(uint64_t(p_int)); }
-	static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_murmur3_one_float(p_float); }
-	static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_murmur3_one_double(p_double); }
-	static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return hash_fmix32(p_int); }
-	static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return hash_fmix32(uint32_t(p_int)); }
-	static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return hash_fmix32(uint32_t(p_int)); }
-	static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return hash_fmix32(uint32_t(p_int)); }
-	static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return hash_fmix32(uint32_t(p_int)); }
-	static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return hash_fmix32(uint32_t(p_int)); }
-	static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) {
-		uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
-		h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) {
-		uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
-		h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
-		h = hash_murmur3_one_32(uint32_t(p_vec.z), h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const Vector4i &p_vec) {
-		uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
-		h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
-		h = hash_murmur3_one_32(uint32_t(p_vec.z), h);
-		h = hash_murmur3_one_32(uint32_t(p_vec.w), h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
-		uint32_t h = hash_murmur3_one_real(p_vec.x);
-		h = hash_murmur3_one_real(p_vec.y, h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const Vector3 &p_vec) {
-		uint32_t h = hash_murmur3_one_real(p_vec.x);
-		h = hash_murmur3_one_real(p_vec.y, h);
-		h = hash_murmur3_one_real(p_vec.z, h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const Vector4 &p_vec) {
-		uint32_t h = hash_murmur3_one_real(p_vec.x);
-		h = hash_murmur3_one_real(p_vec.y, h);
-		h = hash_murmur3_one_real(p_vec.z, h);
-		h = hash_murmur3_one_real(p_vec.w, h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const Color &p_vec) {
-		uint32_t h = hash_murmur3_one_float(p_vec.r);
-		h = hash_murmur3_one_float(p_vec.g, h);
-		h = hash_murmur3_one_float(p_vec.b, h);
-		h = hash_murmur3_one_float(p_vec.a, h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
-		uint32_t h = hash_murmur3_one_32(uint32_t(p_rect.position.x));
-		h = hash_murmur3_one_32(uint32_t(p_rect.position.y), h);
-		h = hash_murmur3_one_32(uint32_t(p_rect.size.x), h);
-		h = hash_murmur3_one_32(uint32_t(p_rect.size.y), h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) {
-		uint32_t h = hash_murmur3_one_real(p_rect.position.x);
-		h = hash_murmur3_one_real(p_rect.position.y, h);
-		h = hash_murmur3_one_real(p_rect.size.x, h);
-		h = hash_murmur3_one_real(p_rect.size.y, h);
-		return hash_fmix32(h);
-	}
-	static _FORCE_INLINE_ uint32_t hash(const AABB &p_aabb) {
-		uint32_t h = hash_murmur3_one_real(p_aabb.position.x);
-		h = hash_murmur3_one_real(p_aabb.position.y, h);
-		h = hash_murmur3_one_real(p_aabb.position.z, h);
-		h = hash_murmur3_one_real(p_aabb.size.x, h);
-		h = hash_murmur3_one_real(p_aabb.size.y, h);
-		h = hash_murmur3_one_real(p_aabb.size.z, h);
-		return hash_fmix32(h);
+template <typename T>
+struct HashMapHasherDefaultImpl<T, std::enable_if_t<std::is_enum_v<T>>> {
+	// For all enums.
+	static _FORCE_INLINE_ uint32_t hash(T p_value) {
+		return HashMapHasherDefaultImpl<std::underlying_type_t<T>>::hash(static_cast<std::underlying_type_t<T>>(p_value));
 	}
 };
 
-struct HashHasher {
-	static _FORCE_INLINE_ uint32_t hash(const int32_t hash) { return hash; }
-	static _FORCE_INLINE_ uint32_t hash(const uint32_t hash) { return hash; }
-	static _FORCE_INLINE_ uint64_t hash(const int64_t hash) { return hash; }
-	static _FORCE_INLINE_ uint64_t hash(const uint64_t hash) { return hash; }
+template <>
+struct HashMapHasherDefaultImpl<char *> {
+	static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
 };
 
-// TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed
-template <typename T>
-struct HashableHasher {
-	static _FORCE_INLINE_ uint32_t hash(const T &hashable) { return hashable.hash(); }
+template <>
+struct HashMapHasherDefaultImpl<wchar_t> {
+	static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(uint32_t(p_wchar)); }
 };
 
-template <typename T>
-struct HashMapComparatorDefault {
-	static bool compare(const T &p_lhs, const T &p_rhs) {
-		return p_lhs == p_rhs;
-	}
+template <>
+struct HashMapHasherDefaultImpl<char16_t> {
+	static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(uint32_t(p_uchar)); }
 };
 
 template <>
-struct HashMapComparatorDefault<float> {
-	static bool compare(const float &p_lhs, const float &p_rhs) {
-		return Math::is_same(p_lhs, p_rhs);
-	}
+struct HashMapHasherDefaultImpl<char32_t> {
+	static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(uint32_t(p_uchar)); }
 };
 
 template <>
-struct HashMapComparatorDefault<double> {
-	static bool compare(const double &p_lhs, const double &p_rhs) {
-		return Math::is_same(p_lhs, p_rhs);
-	}
+struct HashMapHasherDefaultImpl<uint64_t> {
+	static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
 };
 
 template <>
-struct HashMapComparatorDefault<Color> {
-	static bool compare(const Color &p_lhs, const Color &p_rhs) {
-		return p_lhs.is_same(p_rhs);
-	}
+struct HashMapHasherDefaultImpl<int64_t> {
+	static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(uint64_t(p_int)); }
 };
 
 template <>
-struct HashMapComparatorDefault<Vector2> {
-	static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) {
-		return p_lhs.is_same(p_rhs);
-	}
+struct HashMapHasherDefaultImpl<float> {
+	static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_murmur3_one_float(p_float); }
 };
 
 template <>
-struct HashMapComparatorDefault<Vector3> {
-	static bool compare(const Vector3 &p_lhs, const Vector3 &p_rhs) {
-		return p_lhs.is_same(p_rhs);
-	}
+struct HashMapHasherDefaultImpl<double> {
+	static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_murmur3_one_double(p_double); }
 };
 
 template <>
-struct HashMapComparatorDefault<Vector4> {
-	static bool compare(const Vector4 &p_lhs, const Vector4 &p_rhs) {
-		return p_lhs.is_same(p_rhs);
-	}
+struct HashMapHasherDefaultImpl<uint32_t> {
+	static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return hash_fmix32(p_int); }
 };
 
 template <>
-struct HashMapComparatorDefault<Rect2> {
-	static bool compare(const Rect2 &p_lhs, const Rect2 &p_rhs) {
-		return p_lhs.is_same(p_rhs);
-	}
+struct HashMapHasherDefaultImpl<int32_t> {
+	static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return hash_fmix32(uint32_t(p_int)); }
 };
 
 template <>
-struct HashMapComparatorDefault<AABB> {
-	static bool compare(const AABB &p_lhs, const AABB &p_rhs) {
-		return p_lhs.is_same(p_rhs);
-	}
+struct HashMapHasherDefaultImpl<uint16_t> {
+	static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return hash_fmix32(uint32_t(p_int)); }
 };
 
 template <>
-struct HashMapComparatorDefault<Plane> {
-	static bool compare(const Plane &p_lhs, const Plane &p_rhs) {
-		return p_lhs.is_same(p_rhs);
-	}
+struct HashMapHasherDefaultImpl<int16_t> {
+	static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return hash_fmix32(uint32_t(p_int)); }
 };
 
 template <>
-struct HashMapComparatorDefault<Transform2D> {
-	static bool compare(const Transform2D &p_lhs, const Transform2D &p_rhs) {
-		return p_lhs.is_same(p_rhs);
-	}
+struct HashMapHasherDefaultImpl<uint8_t> {
+	static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return hash_fmix32(uint32_t(p_int)); }
 };
 
 template <>
-struct HashMapComparatorDefault<Basis> {
-	static bool compare(const Basis &p_lhs, const Basis &p_rhs) {
-		return p_lhs.is_same(p_rhs);
+struct HashMapHasherDefaultImpl<int8_t> {
+	static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return hash_fmix32(uint32_t(p_int)); }
+};
+
+template <typename F, typename S>
+struct HashMapHasherDefaultImpl<Pair<F, S>> {
+	static _FORCE_INLINE_ uint32_t hash(const Pair<F, S> &p_pair) {
+		uint64_t h1 = HashMapHasherDefault::hash(p_pair.first);
+		uint64_t h2 = HashMapHasherDefault::hash(p_pair.second);
+		return hash_one_uint64((h1 << 32) | h2);
 	}
 };
 
-template <>
-struct HashMapComparatorDefault<Transform3D> {
-	static bool compare(const Transform3D &p_lhs, const Transform3D &p_rhs) {
-		return p_lhs.is_same(p_rhs);
+template <typename, typename = std::void_t<>>
+struct has_is_same_method : std::false_type {};
+
+template <typename T>
+struct has_is_same_method<T, std::void_t<std::is_same<decltype(std::declval<const T>().is_same(std::declval<const T>())), uint32_t>>> : std::true_type {};
+
+template <typename T>
+constexpr bool has_is_same_method_v = has_is_same_method<T>::value;
+
+struct HashHasher {
+	static _FORCE_INLINE_ uint32_t hash(const int32_t hash) { return hash; }
+	static _FORCE_INLINE_ uint32_t hash(const uint32_t hash) { return hash; }
+	static _FORCE_INLINE_ uint64_t hash(const int64_t hash) { return hash; }
+	static _FORCE_INLINE_ uint64_t hash(const uint64_t hash) { return hash; }
+};
+
+template <typename T, typename = void>
+struct HashMapComparatorDefault {
+	static bool compare(const T &p_lhs, const T &p_rhs) {
+		return p_lhs == p_rhs;
 	}
 };
 
 template <>
-struct HashMapComparatorDefault<Projection> {
-	static bool compare(const Projection &p_lhs, const Projection &p_rhs) {
-		return p_lhs.is_same(p_rhs);
+struct HashMapComparatorDefault<float> {
+	static bool compare(const float &p_lhs, const float &p_rhs) {
+		return Math::is_same(p_lhs, p_rhs);
 	}
 };
 
 template <>
-struct HashMapComparatorDefault<Quaternion> {
-	static bool compare(const Quaternion &p_lhs, const Quaternion &p_rhs) {
+struct HashMapComparatorDefault<double> {
+	static bool compare(const double &p_lhs, const double &p_rhs) {
+		return Math::is_same(p_lhs, p_rhs);
+	}
+};
+
+template <typename T>
+struct HashMapComparatorDefault<T, std::enable_if_t<has_is_same_method_v<T>>> {
+	// For self comparing types.
+	static bool compare(const T &p_lhs, const T &p_rhs) {
 		return p_lhs.is_same(p_rhs);
 	}
 };

+ 3 - 0
core/templates/rid.h

@@ -30,6 +30,7 @@
 
 #pragma once
 
+#include "core/templates/hashfuncs.h"
 #include "core/typedefs.h"
 
 class RID_AllocBase;
@@ -69,6 +70,8 @@ public:
 	}
 	_ALWAYS_INLINE_ uint64_t get_id() const { return _id; }
 
+	uint32_t hash() const { return HashMapHasherDefault::hash(_id); }
+
 	_ALWAYS_INLINE_ RID() {}
 };
 

+ 2 - 0
drivers/metal/inflection_map.h

@@ -33,6 +33,8 @@
 #include "core/templates/hash_map.h"
 #include "core/templates/local_vector.h"
 
+#include <iterator>
+
 /// An unordered map that splits elements between a fast-access vector of LinearCount consecutively
 /// indexed elements, and a slower-access map holding sparse indexes larger than LinearCount.
 ///

+ 1 - 1
drivers/metal/metal_objects.h

@@ -196,7 +196,7 @@ public:
 
 class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDResourceCache {
 private:
-	typedef HashMap<ClearAttKey, id<MTLRenderPipelineState>, HashableHasher<ClearAttKey>> HashMap;
+	typedef HashMap<ClearAttKey, id<MTLRenderPipelineState>> HashMap;
 	std::unique_ptr<MDResourceFactory> resource_factory;
 	HashMap clear_states;
 

+ 1 - 0
drivers/metal/pixel_formats.h

@@ -60,6 +60,7 @@ GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations")
 #include "servers/rendering/rendering_device.h"
 
 #import <Metal/Metal.h>
+#include <iterator>
 
 #pragma mark -
 #pragma mark Metal format capabilities

+ 1 - 1
drivers/metal/rendering_device_driver_metal.h

@@ -90,7 +90,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMet
 	 * To prevent unbounded growth of the cache, cache entries are automatically freed when
 	 * there are no more references to the MDLibrary associated with the cache entry.
 	 */
-	HashMap<SHA256Digest, ShaderCacheEntry *, HashableHasher<SHA256Digest>> _shader_cache;
+	HashMap<SHA256Digest, ShaderCacheEntry *> _shader_cache;
 	void shader_cache_free_entry(const SHA256Digest &key);
 
 public:

+ 2 - 0
modules/gridmap/grid_map.h

@@ -70,6 +70,8 @@ class GridMap : public Node3D {
 			return Vector3i(x, y, z);
 		}
 
+		uint32_t hash() const { return operator Vector3i().hash(); }
+
 		IndexKey(Vector3i p_vector) {
 			x = (int16_t)p_vector.x;
 			y = (int16_t)p_vector.y;

+ 4 - 4
modules/svg/image_loader_svg.cpp

@@ -109,22 +109,22 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const ui
 
 	tvg::Result res = sw_canvas->target((uint32_t *)buffer.ptrw(), width, width, height, tvg::SwCanvas::ABGR8888S);
 	if (res != tvg::Result::Success) {
-		ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't set target on ThorVG canvas.");
+		ERR_FAIL_V_MSG(FAILED, vformat("ImageLoaderSVG: Couldn't set target on ThorVG canvas, error code %d.", res));
 	}
 
 	res = sw_canvas->push(std::move(picture));
 	if (res != tvg::Result::Success) {
-		ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't insert ThorVG picture on canvas.");
+		ERR_FAIL_V_MSG(FAILED, vformat("ImageLoaderSVG: Couldn't insert ThorVG picture on canvas, error code %d.", res));
 	}
 
 	res = sw_canvas->draw();
 	if (res != tvg::Result::Success) {
-		ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't draw ThorVG pictures on canvas.");
+		ERR_FAIL_V_MSG(FAILED, vformat("ImageLoaderSVG: Couldn't draw ThorVG pictures on canvas, error code %d.", res));
 	}
 
 	res = sw_canvas->sync();
 	if (res != tvg::Result::Success) {
-		ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't sync ThorVG canvas.");
+		ERR_FAIL_V_MSG(FAILED, vformat("ImageLoaderSVG: Couldn't sync ThorVG canvas, error code %d.", res));
 	}
 
 	p_image->set_data(width, height, false, Image::FORMAT_RGBA8, buffer);

+ 1 - 0
modules/text_server_adv/thorvg_svg_in_ot.h

@@ -43,6 +43,7 @@ using namespace godot;
 // Headers for building as built-in module.
 
 #include "core/os/mutex.h"
+#include "core/string/ustring.h"
 #include "core/templates/hash_map.h"
 #include "core/typedefs.h"
 

+ 1 - 0
modules/text_server_fb/thorvg_svg_in_ot.h

@@ -43,6 +43,7 @@ using namespace godot;
 // Headers for building as built-in module.
 
 #include "core/os/mutex.h"
+#include "core/string/ustring.h"
 #include "core/templates/hash_map.h"
 #include "core/typedefs.h"
 

+ 3 - 2
scene/resources/visual_shader.h

@@ -153,8 +153,9 @@ private:
 			uint64_t port : 32;
 		};
 		uint64_t key = 0;
-		// This is used to apply default equal and hash methods for uint64_t to ConnectionKey.
-		operator uint64_t() const { return key; }
+
+		uint32_t hash() const { return HashMapHasherDefault::hash(key); }
+		bool is_same(const ConnectionKey &p_key) const { return HashMapComparatorDefault<uint64_t>::compare(key, p_key.key); }
 	};
 
 	Error _write_node(Type p_type, StringBuilder *p_global_code, StringBuilder *p_global_code_per_node, HashMap<Type, StringBuilder> *p_global_code_per_func, StringBuilder &r_code, Vector<DefaultTextureParam> &r_def_tex_params, const HashMap<ConnectionKey, const List<Connection>::Element *> &p_input_connections, int p_node, HashSet<int> &r_processed, bool p_for_preview, HashSet<StringName> &r_classes) const;

+ 2 - 2
servers/rendering/renderer_rd/renderer_canvas_render_rd.h

@@ -487,7 +487,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
 	static void _uniform_set_invalidation_callback(void *p_userdata);
 	static void _canvas_texture_invalidation_callback(bool p_deleted, void *p_userdata);
 
-	typedef LRUCache<RIDSetKey, RID, HashableHasher<RIDSetKey>, HashMapComparatorDefault<RIDSetKey>, _before_evict> RIDCache;
+	typedef LRUCache<RIDSetKey, RID, HashMapHasherDefault, HashMapComparatorDefault<RIDSetKey>, _before_evict> RIDCache;
 	RIDCache rid_set_to_uniform_set;
 	/// Maps a CanvasTexture to its associated uniform sets, which must
 	/// be invalidated when the CanvasTexture is updated, such as changing the
@@ -526,7 +526,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
 		uint32_t flags = 0;
 	};
 
-	HashMap<TextureState, TextureInfo, HashableHasher<TextureState>, HashMapComparatorDefault<TextureState>, PagedAllocator<HashMapElement<TextureState, TextureInfo>>> texture_info_map;
+	HashMap<TextureState, TextureInfo, HashMapHasherDefault, HashMapComparatorDefault<TextureState>, PagedAllocator<HashMapElement<TextureState, TextureInfo>>> texture_info_map;
 
 	// per-frame buffers
 	struct DataBuffer {