diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 2904e54b22..aba96befd6 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -794,6 +794,9 @@ void GDExtension::deinitialize_library(InitializationLevel p_level) { ERR_FAIL_COND(p_level > int32_t(level_initialized)); level_initialized = int32_t(p_level) - 1; + + ERR_FAIL_NULL(initialization.deinitialize); + initialization.deinitialize(initialization.userdata, GDExtensionInitializationLevel(p_level)); } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 197c8813a7..2fb3bda87d 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -36,6 +36,7 @@ #include "core/object/script_language.h" #include "core/os/condition_variable.h" #include "core/os/os.h" +#include "core/os/safe_binary_mutex.h" #include "core/string/print_string.h" #include "core/string/translation.h" #include "core/variant/variant_parser.h" diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 0ab81cd0a8..3099d9aab3 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -39,6 +39,9 @@ class ConditionVariable; +template +class SafeBinaryMutex; + class ResourceFormatLoader : public RefCounted { GDCLASS(ResourceFormatLoader, RefCounted); diff --git a/core/object/class_db.h b/core/object/class_db.h index 7a4ee1afa4..21243230de 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -188,7 +188,7 @@ public: template static void register_class(bool p_virtual = false) { GLOBAL_LOCK_FUNCTION; - static_assert(TypesAreSame::value, "Class not declared properly, please use GDCLASS."); + static_assert(types_are_same_v, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -203,7 +203,7 @@ public: template static void register_abstract_class() { GLOBAL_LOCK_FUNCTION; - static_assert(TypesAreSame::value, "Class not declared properly, please use GDCLASS."); + static_assert(types_are_same_v, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -216,7 +216,7 @@ public: template static void register_internal_class() { GLOBAL_LOCK_FUNCTION; - static_assert(TypesAreSame::value, "Class not declared properly, please use GDCLASS."); + static_assert(types_are_same_v, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -239,7 +239,7 @@ public: template static void register_custom_instance_class() { GLOBAL_LOCK_FUNCTION; - static_assert(TypesAreSame::value, "Class not declared properly, please use GDCLASS."); + static_assert(types_are_same_v, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -296,7 +296,7 @@ public: argptrs[i] = &args[i]; } MethodBind *bind = create_method_bind(p_method); - if constexpr (std::is_same::return_type, Object *>::value) { + if constexpr (std::is_same_v::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); @@ -311,7 +311,7 @@ public: } MethodBind *bind = create_static_method_bind(p_method); bind->set_instance_class(p_class); - if constexpr (std::is_same::return_type, Object *>::value) { + if constexpr (std::is_same_v::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); @@ -325,7 +325,7 @@ public: argptrs[i] = &args[i]; } MethodBind *bind = create_method_bind(p_method); - if constexpr (std::is_same::return_type, Object *>::value) { + if constexpr (std::is_same_v::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); @@ -340,7 +340,7 @@ public: } MethodBind *bind = create_static_method_bind(p_method); bind->set_instance_class(p_class); - if constexpr (std::is_same::return_type, Object *>::value) { + if constexpr (std::is_same_v::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); @@ -353,7 +353,7 @@ public: MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); ERR_FAIL_NULL_V(bind, nullptr); - if constexpr (std::is_same::return_type, Object *>::value) { + if constexpr (std::is_same_v::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return _bind_vararg_method(bind, p_name, p_default_args, false); @@ -366,7 +366,7 @@ public: MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); ERR_FAIL_NULL_V(bind, nullptr); - if constexpr (std::is_same::return_type, Object *>::value) { + if constexpr (std::is_same_v::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return _bind_vararg_method(bind, p_name, p_default_args, true); diff --git a/core/os/memory.cpp b/core/os/memory.cpp index 5f6216a5f1..32c316e58e 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -72,23 +72,23 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { bool prepad = p_pad_align; #endif - void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0)); + void *mem = malloc(p_bytes + (prepad ? DATA_OFFSET : 0)); ERR_FAIL_NULL_V(mem, nullptr); alloc_count.increment(); if (prepad) { - uint64_t *s = (uint64_t *)mem; - *s = p_bytes; - uint8_t *s8 = (uint8_t *)mem; + uint64_t *s = (uint64_t *)(s8 + SIZE_OFFSET); + *s = p_bytes; + #ifdef DEBUG_ENABLED uint64_t new_mem_usage = mem_usage.add(p_bytes); max_usage.exchange_if_greater(new_mem_usage); #endif - return s8 + PAD_ALIGN; + return s8 + DATA_OFFSET; } else { return mem; } @@ -108,8 +108,8 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { #endif if (prepad) { - mem -= PAD_ALIGN; - uint64_t *s = (uint64_t *)mem; + mem -= DATA_OFFSET; + uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET); #ifdef DEBUG_ENABLED if (p_bytes > *s) { @@ -126,14 +126,14 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { } else { *s = p_bytes; - mem = (uint8_t *)realloc(mem, p_bytes + PAD_ALIGN); + mem = (uint8_t *)realloc(mem, p_bytes + DATA_OFFSET); ERR_FAIL_NULL_V(mem, nullptr); - s = (uint64_t *)mem; + s = (uint64_t *)(mem + SIZE_OFFSET); *s = p_bytes; - return mem + PAD_ALIGN; + return mem + DATA_OFFSET; } } else { mem = (uint8_t *)realloc(mem, p_bytes); @@ -158,10 +158,10 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) { alloc_count.decrement(); if (prepad) { - mem -= PAD_ALIGN; + mem -= DATA_OFFSET; #ifdef DEBUG_ENABLED - uint64_t *s = (uint64_t *)mem; + uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET); mem_usage.sub(*s); #endif diff --git a/core/os/memory.h b/core/os/memory.h index a0524b0ea2..6f3f6fed39 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -38,10 +38,6 @@ #include #include -#ifndef PAD_ALIGN -#define PAD_ALIGN 16 //must always be greater than this at much -#endif - class Memory { #ifdef DEBUG_ENABLED static SafeNumeric mem_usage; @@ -51,6 +47,17 @@ class Memory { static SafeNumeric alloc_count; public: + // Alignment: ↓ max_align_t ↓ uint64_t ↓ max_align_t + // ┌─────────────────┬──┬────────────────┬──┬───────────... + // │ uint64_t │░░│ uint64_t │░░│ T[] + // │ alloc size │░░│ element count │░░│ data + // └─────────────────┴──┴────────────────┴──┴───────────... + // Offset: ↑ SIZE_OFFSET ↑ ELEMENT_OFFSET ↑ DATA_OFFSET + + static constexpr size_t SIZE_OFFSET = 0; + static constexpr size_t ELEMENT_OFFSET = ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t) == 0) ? (SIZE_OFFSET + sizeof(uint64_t)) : ((SIZE_OFFSET + sizeof(uint64_t)) + alignof(uint64_t) - ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t))); + static constexpr size_t DATA_OFFSET = ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t) == 0) ? (ELEMENT_OFFSET + sizeof(uint64_t)) : ((ELEMENT_OFFSET + sizeof(uint64_t)) + alignof(max_align_t) - ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t))); + static void *alloc_static(size_t p_bytes, bool p_pad_align = false); static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false); static void free_static(void *p_ptr, bool p_pad_align = false); @@ -105,7 +112,7 @@ void memdelete(T *p_class) { if (!predelete_handler(p_class)) { return; // doesn't want to be deleted } - if (!std::is_trivially_destructible::value) { + if constexpr (!std::is_trivially_destructible_v) { p_class->~T(); } @@ -117,7 +124,7 @@ void memdelete_allocator(T *p_class) { if (!predelete_handler(p_class)) { return; // doesn't want to be deleted } - if (!std::is_trivially_destructible::value) { + if constexpr (!std::is_trivially_destructible_v) { p_class->~T(); } @@ -133,6 +140,10 @@ void memdelete_allocator(T *p_class) { #define memnew_arr(m_class, m_count) memnew_arr_template(m_count) +_FORCE_INLINE_ uint64_t *_get_element_count_ptr(uint8_t *p_ptr) { + return (uint64_t *)(p_ptr - Memory::DATA_OFFSET + Memory::ELEMENT_OFFSET); +} + template T *memnew_arr_template(size_t p_elements) { if (p_elements == 0) { @@ -142,12 +153,14 @@ T *memnew_arr_template(size_t p_elements) { same strategy used by std::vector, and the Vector class, so it should be safe.*/ size_t len = sizeof(T) * p_elements; - uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true); + uint8_t *mem = (uint8_t *)Memory::alloc_static(len, true); T *failptr = nullptr; //get rid of a warning ERR_FAIL_NULL_V(mem, failptr); - *(mem - 1) = p_elements; - if (!std::is_trivially_constructible::value) { + uint64_t *_elem_count_ptr = _get_element_count_ptr(mem); + *(_elem_count_ptr) = p_elements; + + if constexpr (!std::is_trivially_constructible_v) { T *elems = (T *)mem; /* call operator new */ @@ -166,16 +179,18 @@ T *memnew_arr_template(size_t p_elements) { template size_t memarr_len(const T *p_class) { - uint64_t *ptr = (uint64_t *)p_class; - return *(ptr - 1); + uint8_t *ptr = (uint8_t *)p_class; + uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr); + return *(_elem_count_ptr); } template void memdelete_arr(T *p_class) { - uint64_t *ptr = (uint64_t *)p_class; + uint8_t *ptr = (uint8_t *)p_class; - if (!std::is_trivially_destructible::value) { - uint64_t elem_count = *(ptr - 1); + if constexpr (!std::is_trivially_destructible_v) { + uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr); + uint64_t elem_count = *(_elem_count_ptr); for (uint64_t i = 0; i < elem_count; i++) { p_class[i].~T(); diff --git a/core/os/mutex.h b/core/os/mutex.h index 69f494d9cd..a4eab0cd86 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -70,56 +70,6 @@ public: } }; -// A very special kind of mutex, used in scenarios where these -// requirements hold at the same time: -// - Must be used with a condition variable (only binary mutexes are suitable). -// - Must have recursive semnantics (or simulate, as this one does). -// The implementation keeps the lock count in TS. Therefore, only -// one object of each version of the template can exists; hence the Tag argument. -// Tags must be unique across the Godot codebase. -// Also, don't forget to declare the thread_local variable on each use. -template -class SafeBinaryMutex { - friend class MutexLock; - - using StdMutexType = THREADING_NAMESPACE::mutex; - - mutable THREADING_NAMESPACE::mutex mutex; - static thread_local uint32_t count; - -public: - _ALWAYS_INLINE_ void lock() const { - if (++count == 1) { - mutex.lock(); - } - } - - _ALWAYS_INLINE_ void unlock() const { - DEV_ASSERT(count); - if (--count == 0) { - mutex.unlock(); - } - } - - _ALWAYS_INLINE_ bool try_lock() const { - if (count) { - count++; - return true; - } else { - if (mutex.try_lock()) { - count++; - return true; - } else { - return false; - } - } - } - - ~SafeBinaryMutex() { - DEV_ASSERT(!count); - } -}; - template class MutexLock { friend class ConditionVariable; @@ -131,24 +81,6 @@ public: lock(p_mutex.mutex) {} }; -// This specialization is needed so manual locking and MutexLock can be used -// at the same time on a SafeBinaryMutex. -template -class MutexLock> { - friend class ConditionVariable; - - THREADING_NAMESPACE::unique_lock lock; - -public: - _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex &p_mutex) : - lock(p_mutex.mutex) { - SafeBinaryMutex::count++; - }; - _ALWAYS_INLINE_ ~MutexLock() { - SafeBinaryMutex::count--; - }; -}; - using Mutex = MutexImpl; // Recursive, for general use using BinaryMutex = MutexImpl; // Non-recursive, handle with care @@ -168,24 +100,12 @@ public: bool try_lock() const { return true; } }; -template -class SafeBinaryMutex : public MutexImpl { - static thread_local uint32_t count; -}; - template class MutexLock { public: MutexLock(const MutexT &p_mutex) {} }; -template -class MutexLock> { -public: - MutexLock(const SafeBinaryMutex &p_mutex) {} - ~MutexLock() {} -}; - using Mutex = MutexImpl; using BinaryMutex = MutexImpl; diff --git a/core/os/safe_binary_mutex.h b/core/os/safe_binary_mutex.h new file mode 100644 index 0000000000..1e98cc074c --- /dev/null +++ b/core/os/safe_binary_mutex.h @@ -0,0 +1,124 @@ +/**************************************************************************/ +/* safe_binary_mutex.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ + +#ifndef SAFE_BINARY_MUTEX_H +#define SAFE_BINARY_MUTEX_H + +#include "core/error/error_macros.h" +#include "core/os/mutex.h" +#include "core/typedefs.h" + +#ifdef THREADS_ENABLED + +// A very special kind of mutex, used in scenarios where these +// requirements hold at the same time: +// - Must be used with a condition variable (only binary mutexes are suitable). +// - Must have recursive semnantics (or simulate, as this one does). +// The implementation keeps the lock count in TS. Therefore, only +// one object of each version of the template can exists; hence the Tag argument. +// Tags must be unique across the Godot codebase. +// Also, don't forget to declare the thread_local variable on each use. +template +class SafeBinaryMutex { + friend class MutexLock; + + using StdMutexType = THREADING_NAMESPACE::mutex; + + mutable THREADING_NAMESPACE::mutex mutex; + static thread_local uint32_t count; + +public: + _ALWAYS_INLINE_ void lock() const { + if (++count == 1) { + mutex.lock(); + } + } + + _ALWAYS_INLINE_ void unlock() const { + DEV_ASSERT(count); + if (--count == 0) { + mutex.unlock(); + } + } + + _ALWAYS_INLINE_ bool try_lock() const { + if (count) { + count++; + return true; + } else { + if (mutex.try_lock()) { + count++; + return true; + } else { + return false; + } + } + } + + ~SafeBinaryMutex() { + DEV_ASSERT(!count); + } +}; + +// This specialization is needed so manual locking and MutexLock can be used +// at the same time on a SafeBinaryMutex. +template +class MutexLock> { + friend class ConditionVariable; + + THREADING_NAMESPACE::unique_lock lock; + +public: + _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex &p_mutex) : + lock(p_mutex.mutex) { + SafeBinaryMutex::count++; + }; + _ALWAYS_INLINE_ ~MutexLock() { + SafeBinaryMutex::count--; + }; +}; + +#else // No threads. + +template +class SafeBinaryMutex : public MutexImpl { + static thread_local uint32_t count; +}; + +template +class MutexLock> { +public: + MutexLock(const SafeBinaryMutex &p_mutex) {} + ~MutexLock() {} +}; + +#endif // THREADS_ENABLED + +#endif // SAFE_BINARY_MUTEX_H diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index d43cf8107f..466658951e 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -46,7 +46,7 @@ class CharString; template class VMap; -SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint64_t) +static_assert(std::is_trivially_destructible_v>); // Silence a false positive warning (see GH-52119). #if defined(__GNUC__) && !defined(__clang__) @@ -89,18 +89,39 @@ private: return ++x; } - static constexpr USize ALLOC_PAD = sizeof(USize) * 2; // For size and atomic refcount. + // Alignment: ↓ max_align_t ↓ USize ↓ max_align_t + // ┌────────────────────┬──┬─────────────┬──┬───────────... + // │ SafeNumeric │░░│ USize │░░│ T[] + // │ ref. count │░░│ data size │░░│ data + // └────────────────────┴──┴─────────────┴──┴───────────... + // Offset: ↑ REF_COUNT_OFFSET ↑ SIZE_OFFSET ↑ DATA_OFFSET + + static constexpr size_t REF_COUNT_OFFSET = 0; + static constexpr size_t SIZE_OFFSET = ((REF_COUNT_OFFSET + sizeof(SafeNumeric)) % alignof(USize) == 0) ? (REF_COUNT_OFFSET + sizeof(SafeNumeric)) : ((REF_COUNT_OFFSET + sizeof(SafeNumeric)) + alignof(USize) - ((REF_COUNT_OFFSET + sizeof(SafeNumeric)) % alignof(USize))); + static constexpr size_t DATA_OFFSET = ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t) == 0) ? (SIZE_OFFSET + sizeof(USize)) : ((SIZE_OFFSET + sizeof(USize)) + alignof(max_align_t) - ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t))); mutable T *_ptr = nullptr; // internal helpers + static _FORCE_INLINE_ SafeNumeric *_get_refcount_ptr(uint8_t *p_ptr) { + return (SafeNumeric *)(p_ptr + REF_COUNT_OFFSET); + } + + static _FORCE_INLINE_ USize *_get_size_ptr(uint8_t *p_ptr) { + return (USize *)(p_ptr + SIZE_OFFSET); + } + + static _FORCE_INLINE_ T *_get_data_ptr(uint8_t *p_ptr) { + return (T *)(p_ptr + DATA_OFFSET); + } + _FORCE_INLINE_ SafeNumeric *_get_refcount() const { if (!_ptr) { return nullptr; } - return reinterpret_cast *>(_ptr) - 2; + return (SafeNumeric *)((uint8_t *)_ptr - DATA_OFFSET + REF_COUNT_OFFSET); } _FORCE_INLINE_ USize *_get_size() const { @@ -108,7 +129,7 @@ private: return nullptr; } - return reinterpret_cast(_ptr) - 1; + return (USize *)((uint8_t *)_ptr - DATA_OFFSET + SIZE_OFFSET); } _FORCE_INLINE_ USize _get_alloc_size(USize p_elements) const { @@ -233,7 +254,7 @@ void CowData::_unref(void *p_data) { } // clean up - if (!std::is_trivially_destructible::value) { + if constexpr (!std::is_trivially_destructible_v) { USize *count = _get_size(); T *data = (T *)(count + 1); @@ -244,7 +265,7 @@ void CowData::_unref(void *p_data) { } // free mem - Memory::free_static(((uint8_t *)p_data) - ALLOC_PAD, false); + Memory::free_static(((uint8_t *)p_data) - DATA_OFFSET, false); } template @@ -260,26 +281,27 @@ typename CowData::USize CowData::_copy_on_write() { /* in use by more than me */ USize current_size = *_get_size(); - USize *mem_new = (USize *)Memory::alloc_static(_get_alloc_size(current_size) + ALLOC_PAD, false); - mem_new += 2; + uint8_t *mem_new = (uint8_t *)Memory::alloc_static(_get_alloc_size(current_size) + DATA_OFFSET, false); + ERR_FAIL_NULL_V(mem_new, 0); - new (mem_new - 2) SafeNumeric(1); //refcount - *(mem_new - 1) = current_size; //size + SafeNumeric *_refc_ptr = _get_refcount_ptr(mem_new); + USize *_size_ptr = _get_size_ptr(mem_new); + T *_data_ptr = _get_data_ptr(mem_new); - T *_data = (T *)(mem_new); + new (_refc_ptr) SafeNumeric(1); //refcount + *(_size_ptr) = current_size; //size // initialize new elements - if (std::is_trivially_copyable::value) { - memcpy(mem_new, _ptr, current_size * sizeof(T)); - + if constexpr (std::is_trivially_copyable_v) { + memcpy((uint8_t *)_data_ptr, _ptr, current_size * sizeof(T)); } else { for (USize i = 0; i < current_size; i++) { - memnew_placement(&_data[i], T(_ptr[i])); + memnew_placement(&_data_ptr[i], T(_ptr[i])); } } _unref(_ptr); - _ptr = _data; + _ptr = _data_ptr; rc = 1; } @@ -315,27 +337,34 @@ Error CowData::resize(Size p_size) { if (alloc_size != current_alloc_size) { if (current_size == 0) { // alloc from scratch - USize *ptr = (USize *)Memory::alloc_static(alloc_size + ALLOC_PAD, false); - ptr += 2; - ERR_FAIL_NULL_V(ptr, ERR_OUT_OF_MEMORY); - *(ptr - 1) = 0; //size, currently none - new (ptr - 2) SafeNumeric(1); //refcount + uint8_t *mem_new = (uint8_t *)Memory::alloc_static(alloc_size + DATA_OFFSET, false); + ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY); + + SafeNumeric *_refc_ptr = _get_refcount_ptr(mem_new); + USize *_size_ptr = _get_size_ptr(mem_new); + T *_data_ptr = _get_data_ptr(mem_new); - _ptr = (T *)ptr; + new (_refc_ptr) SafeNumeric(1); //refcount + *(_size_ptr) = 0; //size, currently none + + _ptr = _data_ptr; } else { - USize *_ptrnew = (USize *)Memory::realloc_static(((uint8_t *)_ptr) - ALLOC_PAD, alloc_size + ALLOC_PAD, false); - ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY); - _ptrnew += 2; - new (_ptrnew - 2) SafeNumeric(rc); //refcount + uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false); + ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY); + + SafeNumeric *_refc_ptr = _get_refcount_ptr(mem_new); + T *_data_ptr = _get_data_ptr(mem_new); - _ptr = (T *)(_ptrnew); + new (_refc_ptr) SafeNumeric(rc); //refcount + + _ptr = _data_ptr; } } // construct the newly created elements - if (!std::is_trivially_constructible::value) { + if constexpr (!std::is_trivially_constructible_v) { for (Size i = *_get_size(); i < p_size; i++) { memnew_placement(&_ptr[i], T); } @@ -346,7 +375,7 @@ Error CowData::resize(Size p_size) { *_get_size() = p_size; } else if (p_size < current_size) { - if (!std::is_trivially_destructible::value) { + if constexpr (!std::is_trivially_destructible_v) { // deinitialize no longer needed elements for (USize i = p_size; i < *_get_size(); i++) { T *t = &_ptr[i]; @@ -355,12 +384,15 @@ Error CowData::resize(Size p_size) { } if (alloc_size != current_alloc_size) { - USize *_ptrnew = (USize *)Memory::realloc_static(((uint8_t *)_ptr) - ALLOC_PAD, alloc_size + ALLOC_PAD, false); - ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY); - _ptrnew += 2; - new (_ptrnew - 2) SafeNumeric(rc); //refcount + uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false); + ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY); + + SafeNumeric *_refc_ptr = _get_refcount_ptr(mem_new); + T *_data_ptr = _get_data_ptr(mem_new); + + new (_refc_ptr) SafeNumeric(rc); //refcount - _ptr = (T *)(_ptrnew); + _ptr = _data_ptr; } *_get_size() = p_size; diff --git a/core/templates/local_vector.h b/core/templates/local_vector.h index b454821a8f..17ddbf6161 100644 --- a/core/templates/local_vector.h +++ b/core/templates/local_vector.h @@ -64,7 +64,7 @@ public: CRASH_COND_MSG(!data, "Out of memory"); } - if constexpr (!std::is_trivially_constructible::value && !force_trivial) { + if constexpr (!std::is_trivially_constructible_v && !force_trivial) { memnew_placement(&data[count++], T(p_elem)); } else { data[count++] = p_elem; @@ -77,7 +77,7 @@ public: for (U i = p_index; i < count; i++) { data[i] = data[i + 1]; } - if constexpr (!std::is_trivially_destructible::value && !force_trivial) { + if constexpr (!std::is_trivially_destructible_v && !force_trivial) { data[count].~T(); } } @@ -90,7 +90,7 @@ public: if (count > p_index) { data[p_index] = data[count]; } - if constexpr (!std::is_trivially_destructible::value && !force_trivial) { + if constexpr (!std::is_trivially_destructible_v && !force_trivial) { data[count].~T(); } } @@ -133,7 +133,7 @@ public: _FORCE_INLINE_ U size() const { return count; } void resize(U p_size) { if (p_size < count) { - if constexpr (!std::is_trivially_destructible::value && !force_trivial) { + if constexpr (!std::is_trivially_destructible_v && !force_trivial) { for (U i = p_size; i < count; i++) { data[i].~T(); } @@ -145,7 +145,7 @@ public: data = (T *)memrealloc(data, capacity * sizeof(T)); CRASH_COND_MSG(!data, "Out of memory"); } - if constexpr (!std::is_trivially_constructible::value && !force_trivial) { + if constexpr (!std::is_trivially_constructible_v && !force_trivial) { for (U i = count; i < p_size; i++) { memnew_placement(&data[i], T); } diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h index d880eae0c3..48110d37e5 100644 --- a/core/templates/paged_allocator.h +++ b/core/templates/paged_allocator.h @@ -101,7 +101,7 @@ public: private: void _reset(bool p_allow_unfreed) { - if (!p_allow_unfreed || !std::is_trivially_destructible::value) { + if (!p_allow_unfreed || !std::is_trivially_destructible_v) { ERR_FAIL_COND(allocs_available < pages_allocated * page_size); } if (pages_allocated) { diff --git a/core/templates/paged_array.h b/core/templates/paged_array.h index 6b7e0cee16..21053dd033 100644 --- a/core/templates/paged_array.h +++ b/core/templates/paged_array.h @@ -202,7 +202,7 @@ public: uint32_t page = count >> page_size_shift; uint32_t offset = count & page_size_mask; - if (!std::is_trivially_constructible::value) { + if constexpr (!std::is_trivially_constructible_v) { memnew_placement(&page_data[page][offset], T(p_value)); } else { page_data[page][offset] = p_value; @@ -214,7 +214,7 @@ public: _FORCE_INLINE_ void pop_back() { ERR_FAIL_COND(count == 0); - if (!std::is_trivially_destructible::value) { + if constexpr (!std::is_trivially_destructible_v) { uint32_t page = (count - 1) >> page_size_shift; uint32_t offset = (count - 1) & page_size_mask; page_data[page][offset].~T(); @@ -237,7 +237,7 @@ public: void clear() { //destruct if needed - if (!std::is_trivially_destructible::value) { + if constexpr (!std::is_trivially_destructible_v) { for (uint64_t i = 0; i < count; i++) { uint32_t page = i >> page_size_shift; uint32_t offset = i & page_size_mask; @@ -318,13 +318,13 @@ public: uint32_t to_copy = MIN(page_size - new_remainder, remainder); for (uint32_t i = 0; i < to_copy; i++) { - if (!std::is_trivially_constructible::value) { + if constexpr (!std::is_trivially_constructible_v) { memnew_placement(&dst_page[i + new_remainder], T(remainder_page[i + remainder - to_copy])); } else { dst_page[i + new_remainder] = remainder_page[i + remainder - to_copy]; } - if (!std::is_trivially_destructible::value) { + if constexpr (!std::is_trivially_destructible_v) { remainder_page[i + remainder - to_copy].~T(); } } diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h index 20fb0c6501..7bbceadc8d 100644 --- a/core/templates/safe_refcount.h +++ b/core/templates/safe_refcount.h @@ -54,7 +54,7 @@ #define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \ static_assert(sizeof(SafeNumeric) == sizeof(m_type)); \ static_assert(alignof(SafeNumeric) == alignof(m_type)); \ - static_assert(std::is_trivially_destructible>::value); + static_assert(std::is_trivially_destructible_v>); #define SAFE_FLAG_TYPE_PUN_GUARANTEES \ static_assert(sizeof(SafeFlag) == sizeof(bool)); \ static_assert(alignof(SafeFlag) == alignof(bool)); diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index c9f5ae7fc6..a44b938395 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -51,7 +51,7 @@ template struct VariantCaster { static _FORCE_INLINE_ T cast(const Variant &p_variant) { using TStripped = std::remove_pointer_t; - if constexpr (std::is_base_of::value) { + if constexpr (std::is_base_of_v) { return Object::cast_to(p_variant); } else { return p_variant; @@ -63,7 +63,7 @@ template struct VariantCaster { static _FORCE_INLINE_ T cast(const Variant &p_variant) { using TStripped = std::remove_pointer_t; - if constexpr (std::is_base_of::value) { + if constexpr (std::is_base_of_v) { return Object::cast_to(p_variant); } else { return p_variant; @@ -75,7 +75,7 @@ template struct VariantCaster { static _FORCE_INLINE_ T cast(const Variant &p_variant) { using TStripped = std::remove_pointer_t; - if constexpr (std::is_base_of::value) { + if constexpr (std::is_base_of_v) { return Object::cast_to(p_variant); } else { return p_variant; @@ -226,7 +226,7 @@ template struct VariantObjectClassChecker { static _FORCE_INLINE_ bool check(const Variant &p_variant) { using TStripped = std::remove_pointer_t; - if constexpr (std::is_base_of::value) { + if constexpr (std::is_base_of_v) { Object *obj = p_variant; return Object::cast_to(p_variant) || !obj; } else { diff --git a/core/variant/type_info.h b/core/variant/type_info.h index c1f2f86a96..49c4db8229 100644 --- a/core/variant/type_info.h +++ b/core/variant/type_info.h @@ -43,14 +43,10 @@ struct EnableIf { }; template -struct TypesAreSame { - static bool const value = false; -}; +inline constexpr bool types_are_same_v = false; -template -struct TypesAreSame { - static bool const value = true; -}; +template +inline constexpr bool types_are_same_v = true; template struct TypeInherits { @@ -60,7 +56,7 @@ struct TypeInherits { static char (&test(...))[2]; static bool const value = sizeof(test(get_d())) == sizeof(char) && - !TypesAreSame::value; + !types_are_same_v; }; namespace GodotTypeInfo { diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml index 3550a6b7bd..8dd0cfa135 100644 --- a/doc/classes/Callable.xml +++ b/doc/classes/Callable.xml @@ -98,10 +98,18 @@ Calls the method represented by this [Callable] in deferred mode, i.e. at the end of the current frame. Arguments can be passed and should match the method's signature. - [codeblock] + [codeblocks] + [gdscript] func _ready(): grab_focus.call_deferred() - [/codeblock] + [/gdscript] + [csharp] + public override void _Ready() + { + Callable.From(GrabFocus).CallDeferred(); + } + [/csharp] + [/codeblocks] See also [method Object.call_deferred]. diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml index 6801ac672c..0e5de79b25 100644 --- a/doc/classes/EditorExportPlatform.xml +++ b/doc/classes/EditorExportPlatform.xml @@ -8,7 +8,7 @@ Used in scripting by [EditorExportPlugin] to configure platform-specific customization of scenes and resources. See [method EditorExportPlugin._begin_customize_scenes] and [method EditorExportPlugin._begin_customize_resources] for more details. - Console support in Godot + $DOCS_URL/tutorials/platform/consoles.html diff --git a/doc/classes/HeightMapShape3D.xml b/doc/classes/HeightMapShape3D.xml index ff120fa557..ba79cbc89a 100644 --- a/doc/classes/HeightMapShape3D.xml +++ b/doc/classes/HeightMapShape3D.xml @@ -9,9 +9,23 @@ + + + + + Returns the largest height value found in [member map_data]. Recalculates only when [member map_data] changes. + + + + + + Returns the smallest height value found in [member map_data]. Recalculates only when [member map_data] changes. + + + - Height map data, pool array must be of [member map_width] * [member map_depth] size. + Height map data. The array's size must be equal to [member map_width] multiplied by [member map_depth]. Number of vertices in the depth of the height map. Changing this will resize the [member map_data]. diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml index adfdfdc445..eef159d44d 100644 --- a/doc/classes/PhysicsBody2D.xml +++ b/doc/classes/PhysicsBody2D.xml @@ -23,6 +23,12 @@ Returns an array of nodes that were added as collision exceptions for this body. + + + + Returns the gravity vector computed from all sources that can affect the body, including all gravity overrides from [Area2D] nodes and the global world gravity. + + diff --git a/doc/classes/PhysicsBody3D.xml b/doc/classes/PhysicsBody3D.xml index ff994fe6c5..866b3e298c 100644 --- a/doc/classes/PhysicsBody3D.xml +++ b/doc/classes/PhysicsBody3D.xml @@ -31,6 +31,12 @@ Returns an array of nodes that were added as collision exceptions for this body. + + + + Returns the gravity vector computed from all sources that can affect the body, including all gravity overrides from [Area3D] nodes and the global world gravity. + + diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 786e841c21..b4cf6d8de1 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -649,7 +649,9 @@ void EditorData::remove_scene(int p_idx) { EditorNode::get_singleton()->emit_signal("scene_closed", edited_scene[p_idx].path); } - undo_redo_manager->discard_history(edited_scene[p_idx].history_id); + if (undo_redo_manager->has_history(edited_scene[p_idx].history_id)) { // Might not exist if scene failed to load. + undo_redo_manager->discard_history(edited_scene[p_idx].history_id); + } edited_scene.remove_at(p_idx); } diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp index c2491f8611..94f76dbc41 100644 --- a/editor/editor_undo_redo_manager.cpp +++ b/editor/editor_undo_redo_manager.cpp @@ -375,6 +375,10 @@ bool EditorUndoRedoManager::has_redo() { return false; } +bool EditorUndoRedoManager::has_history(int p_idx) const { + return history_map.has(p_idx); +} + void EditorUndoRedoManager::clear_history(bool p_increase_version, int p_idx) { if (p_idx != INVALID_HISTORY) { History &history = get_or_create_history(p_idx); diff --git a/editor/editor_undo_redo_manager.h b/editor/editor_undo_redo_manager.h index effa36a87c..e8c782871c 100644 --- a/editor/editor_undo_redo_manager.h +++ b/editor/editor_undo_redo_manager.h @@ -130,6 +130,7 @@ public: bool is_history_unsaved(int p_idx); bool has_undo(); bool has_redo(); + bool has_history(int p_idx) const; String get_current_action_name(); int get_current_action_history_id(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 1f9f23b5d0..1f3d886f12 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5661,7 +5661,7 @@ void CanvasItemEditorViewport::_create_preview(const Vector &files) cons Ref scene = Ref(Object::cast_to(*res)); if (texture != nullptr || scene != nullptr) { bool root_node_selected = EditorNode::get_singleton()->get_editor_selection()->is_selected(EditorNode::get_singleton()->get_edited_scene()); - String desc = TTR("Drag and drop to add as child of current scene's root node.") + "\n" + vformat(TTR("Hold %s when dropping to add as child of selected node."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)); + String desc = TTR("Drag and drop to add as child of selected node.") + "\n" + TTR("Hold Alt when dropping to add as child of root node."); if (!root_node_selected) { desc += "\n" + TTR("Hold Shift when dropping to add as sibling of selected node."); } @@ -5672,7 +5672,7 @@ void CanvasItemEditorViewport::_create_preview(const Vector &files) cons preview_node->add_child(sprite); label->show(); label_desc->show(); - desc += "\n" + TTR("Hold Alt when dropping to add as a different node type."); + desc += "\n" + TTR("Hold Alt + Shift when dropping to add as a different node type."); label_desc->set_text(desc); } else { if (scene.is_valid()) { @@ -5965,7 +5965,6 @@ bool CanvasItemEditorViewport::_only_packed_scenes_selected() const { void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) { bool is_shift = Input::get_singleton()->is_key_pressed(Key::SHIFT); - bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL); bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT); selected_files.clear(); @@ -5981,9 +5980,9 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p Node *root_node = EditorNode::get_singleton()->get_edited_scene(); if (selected_nodes.size() > 0) { Node *selected_node = selected_nodes[0]; - target_node = root_node; - if (is_ctrl) { - target_node = selected_node; + target_node = selected_node; + if (is_alt) { + target_node = root_node; } else if (is_shift && selected_node != root_node) { target_node = selected_node->get_parent(); } @@ -5997,7 +5996,7 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p drop_pos = p_point; - if (is_alt && !_only_packed_scenes_selected()) { + if (is_alt && is_shift && !_only_packed_scenes_selected()) { _show_resource_type_selector(); } else { _perform_drop_data(); @@ -6030,6 +6029,10 @@ void CanvasItemEditorViewport::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { disconnect("mouse_exited", callable_mp(this, &CanvasItemEditorViewport::_on_mouse_exit)); } break; + + case NOTIFICATION_DRAG_END: { + _remove_preview(); + } break; } } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 5e70354d22..b097523c2f 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1749,8 +1749,7 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { _edit.mode = TRANSFORM_NONE; _edit.original = spatial_editor->get_gizmo_transform(); // To prevent to break when flipping with scale. - bool node_selected = spatial_editor->get_single_selected_node(); - bool can_select_gizmos = node_selected; + bool can_select_gizmos = spatial_editor->get_single_selected_node(); { int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); @@ -1840,6 +1839,8 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { clicked = ObjectID(); + bool node_selected = get_selected_count() > 0; + if (node_selected && ((spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->is_command_or_control_pressed()) || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE)) { begin_transform(TRANSFORM_ROTATE, false); break; @@ -1985,9 +1986,8 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { } else { const bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE; - // enable region-select if nothing has been selected yet or multi-select (shift key) is active - if (selection_in_progress && movement_threshold_passed) { - if (get_selected_count() == 0 || clicked_wants_append) { + if (selection_in_progress && movement_threshold_passed && clicked.is_valid()) { + if (clicked_wants_append || !editor_selection->is_selected(Object::cast_to(ObjectDB::get_instance(clicked)))) { cursor.region_select = true; cursor.region_begin = _edit.original_mouse_pos; clicked = ObjectID(); @@ -3017,6 +3017,8 @@ void Node3DEditorViewport::_notification(int p_what) { // Clear preview material when dropped outside applicable object. if (spatial_editor->get_preview_material().is_valid() && !is_drag_successful()) { _remove_preview_material(); + } else { + _remove_preview_node(); } } break; } @@ -4530,7 +4532,7 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_ } bool is_shift = Input::get_singleton()->is_key_pressed(Key::SHIFT); - bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL); + bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT); selected_files.clear(); Dictionary d = p_data; @@ -4540,15 +4542,15 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_ List selected_nodes = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list(); Node *root_node = EditorNode::get_singleton()->get_edited_scene(); - if (selected_nodes.size() == 1) { + if (selected_nodes.size() > 0) { Node *selected_node = selected_nodes[0]; - target_node = root_node; - if (is_ctrl) { - target_node = selected_node; + target_node = selected_node; + if (is_alt) { + target_node = root_node; } else if (is_shift && selected_node != root_node) { target_node = selected_node->get_parent(); } - } else if (selected_nodes.size() == 0) { + } else { if (root_node) { target_node = root_node; } else { @@ -4556,11 +4558,6 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_ SceneTreeDock::get_singleton()->add_root_node(memnew(Node3D)); target_node = get_tree()->get_edited_scene_root(); } - } else { - accept->set_text(TTR("Cannot drag and drop into multiple selected nodes.")); - accept->popup_centered(); - _remove_preview_node(); - return; } drop_pos = p_point; @@ -6500,20 +6497,20 @@ void vertex() { // Points are already in world space, so no need for MODEL_MATRIX anymore. vec4 clip_a = PROJECTION_MATRIX * (VIEW_MATRIX * vec4(point_a, 1.0)); vec4 clip_b = PROJECTION_MATRIX * (VIEW_MATRIX * vec4(point_b, 1.0)); - + vec2 screen_a = VIEWPORT_SIZE * (0.5 * clip_a.xy / clip_a.w + 0.5); vec2 screen_b = VIEWPORT_SIZE * (0.5 * clip_b.xy / clip_b.w + 0.5); - + vec2 x_basis = normalize(screen_b - screen_a); vec2 y_basis = vec2(-x_basis.y, x_basis.x); - + float width = 3.0; vec2 screen_point_a = screen_a + width * (VERTEX.x * x_basis + VERTEX.y * y_basis); vec2 screen_point_b = screen_b + width * (VERTEX.x * x_basis + VERTEX.y * y_basis); vec2 screen_point_final = mix(screen_point_a, screen_point_b, VERTEX.z); - + vec4 clip_final = mix(clip_a, clip_b, VERTEX.z); - + POSITION = vec4(clip_final.w * ((2.0 * screen_point_final) / VIEWPORT_SIZE - 1.0), clip_final.z, clip_final.w); UV = VERTEX.yz * clip_final.w; diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 2a8b357559..a3d578b437 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -226,8 +226,8 @@ void TextureRegionEditor::_texture_overlay_draw() { hscroll->set_value((hscroll->get_min() + hscroll->get_max() - hscroll->get_page()) / 2); vscroll->set_value((vscroll->get_min() + vscroll->get_max() - vscroll->get_page()) / 2); // This ensures that the view is updated correctly. - callable_mp(this, &TextureRegionEditor::_pan_callback).bind(Vector2(1, 0)).call_deferred(); - callable_mp(this, &TextureRegionEditor::_scroll_changed).bind(0.0).call_deferred(); + callable_mp(this, &TextureRegionEditor::_pan_callback).call_deferred(Vector2(1, 0), Ref()); + callable_mp(this, &TextureRegionEditor::_scroll_changed).call_deferred(0.0); request_center = false; } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index f177164ec2..5b789c9445 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1824,7 +1824,6 @@ bool SceneTreeDock::_check_node_path_recursive(Node *p_root_node, Variant &r_var EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->add_do_property(resource, propertyname, updated_variant); undo_redo->add_undo_property(resource, propertyname, old_variant); - resource->set(propertyname, updated_variant); } } break; @@ -1968,7 +1967,6 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->add_do_property(p_base, propertyname, updated_variant); undo_redo->add_undo_property(p_base, propertyname, old_variant); - p_base->set(propertyname, updated_variant); } } @@ -2849,7 +2847,6 @@ void SceneTreeDock::perform_node_replace(Node *p_base, Node *p_node, Node *p_by_ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->add_do_property(p_base, propertyname, updated_variant); undo_redo->add_undo_property(p_base, propertyname, old_variant); - p_base->set(propertyname, updated_variant); if (!warn_message.is_empty()) { String node_path = (String(edited_scene->get_name()) + "/" + String(edited_scene->get_path_to(p_base))).trim_suffix("/."); WARN_PRINT(warn_message + vformat("Removing the node from variable \"%s\" on node \"%s\".", propertyname, node_path)); diff --git a/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd index 28ab080dd2..bd4816827f 100644 --- a/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd +++ b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd @@ -6,14 +6,11 @@ extends _BASE_ const SPEED = 300.0 const JUMP_VELOCITY = -400.0 -# Get the gravity from the project settings to be synced with RigidBody nodes. -var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity") - func _physics_process(delta: float) -> void: # Add the gravity. if not is_on_floor(): - velocity.y += gravity * delta + velocity += get_gravity() * delta # Handle jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): diff --git a/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd index 9b0e4be4ed..f9c4f70a24 100644 --- a/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd +++ b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd @@ -6,14 +6,11 @@ extends _BASE_ const SPEED = 5.0 const JUMP_VELOCITY = 4.5 -# Get the gravity from the project settings to be synced with RigidBody nodes. -var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") - func _physics_process(delta: float) -> void: # Add the gravity. if not is_on_floor(): - velocity.y -= gravity * delta + velocity += get_gravity() * delta # Handle jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 98a3a1268f..29cf7bc6ca 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -672,6 +672,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { bool has_decimal = false; bool has_exponent = false; bool has_error = false; + bool need_digits = false; bool (*digit_check_func)(char32_t) = is_digit; // Sign before hexadecimal or binary. @@ -686,11 +687,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { // Hexadecimal. base = 16; digit_check_func = is_hex_digit; + need_digits = true; _advance(); } else if (_peek() == 'b') { // Binary. base = 2; digit_check_func = is_binary_digit; + need_digits = true; _advance(); } } @@ -717,6 +720,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { } previous_was_underscore = true; } else { + need_digits = false; previous_was_underscore = false; } _advance(); @@ -820,6 +824,16 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { } } + if (need_digits) { + // No digits in hex or bin literal. + Token error = make_error(vformat(R"(Expected %s digit after "0%c".)", (base == 16 ? "hexadecimal" : "binary"), (base == 16 ? 'x' : 'b'))); + error.start_column = column; + error.leftmost_column = column; + error.end_column = column + 1; + error.rightmost_column = column + 1; + return error; + } + // Detect extra decimal point. if (!has_error && has_decimal && _peek() == '.' && _peek(1) != '.') { Token error = make_error("Cannot use a decimal point twice in a number."); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index f2dccf6d68..73e26fbef5 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5982,8 +5982,13 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref p_state, const GL p_scene_parent = bone_attachment; } if (skeleton->get_parent() == nullptr) { - p_scene_parent->add_child(skeleton, true); - skeleton->set_owner(p_scene_root); + if (p_scene_root) { + p_scene_parent->add_child(skeleton, true); + skeleton->set_owner(p_scene_root); + } else { + p_scene_parent = skeleton; + p_scene_root = skeleton; + } } } @@ -6582,7 +6587,7 @@ float GLTFDocument::get_max_component(const Color &p_color) { return MAX(MAX(r, g), b); } -void GLTFDocument::_process_mesh_instances(Ref p_state) { +void GLTFDocument::_process_mesh_instances(Ref p_state, Node *p_scene_root) { for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); ++node_i) { Ref node = p_state->nodes[node_i]; @@ -6607,7 +6612,7 @@ void GLTFDocument::_process_mesh_instances(Ref p_state) { mi->get_parent()->remove_child(mi); skeleton->add_child(mi, true); - mi->set_owner(skeleton->get_owner()); + mi->set_owner(p_scene_root); mi->set_skin(p_state->skins.write[skin_i]->godot_skin); mi->set_skeleton_path(mi->get_path_to(skeleton)); @@ -7461,7 +7466,7 @@ Node *GLTFDocument::generate_scene(Ref p_state, float p_bake_fps, boo Error err = OK; Node *root = _generate_scene_node_tree(p_state); ERR_FAIL_NULL_V(root, nullptr); - _process_mesh_instances(p_state); + _process_mesh_instances(p_state, root); if (p_state->get_create_animations() && p_state->animations.size()) { AnimationPlayer *ap = memnew(AnimationPlayer); root->add_child(ap, true); diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index f321bb7111..04c85f3b07 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -326,7 +326,7 @@ public: Error _parse_gltf_state(Ref p_state, const String &p_search_path); Error _parse_asset_header(Ref p_state); Error _parse_gltf_extensions(Ref p_state); - void _process_mesh_instances(Ref p_state); + void _process_mesh_instances(Ref p_state, Node *p_scene_root); Node *_generate_scene_node_tree(Ref p_state); void _generate_scene_node(Ref p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root); void _generate_skeleton_bone_node(Ref p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root); diff --git a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs index 87468fb433..698157c6b4 100644 --- a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs +++ b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs @@ -8,20 +8,21 @@ public partial class _CLASS_ : _BASE_ public const float Speed = 300.0f; public const float JumpVelocity = -400.0f; - // Get the gravity from the project settings to be synced with RigidBody nodes. - public float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle(); - public override void _PhysicsProcess(double delta) { Vector2 velocity = Velocity; // Add the gravity. if (!IsOnFloor()) - velocity.Y += gravity * (float)delta; + { + velocity += GetGravity() * (float)delta; + } // Handle Jump. if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) + { velocity.Y = JumpVelocity; + } // Get the input direction and handle the movement/deceleration. // As good practice, you should replace UI actions with custom gameplay actions. diff --git a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs index ddeb9d7e00..30dabd31d9 100644 --- a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs +++ b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs @@ -8,20 +8,21 @@ public partial class _CLASS_ : _BASE_ public const float Speed = 5.0f; public const float JumpVelocity = 4.5f; - // Get the gravity from the project settings to be synced with RigidBody nodes. - public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle(); - public override void _PhysicsProcess(double delta) { Vector3 velocity = Velocity; // Add the gravity. if (!IsOnFloor()) - velocity.Y -= gravity * (float)delta; + { + velocity += GetGravity() * (float)delta; + } // Handle Jump. if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) + { velocity.Y = JumpVelocity; + } // Get the input direction and handle the movement/deceleration. // As good practice, you should replace UI actions with custom gameplay actions. diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 4a8e9cd956..0d1e40fb3d 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -67,13 +67,14 @@ def get_mvk_sdk_path(): if not os.path.exists(dirname): return "" + ver_min = ver_parse("1.3.231.0") ver_num = ver_parse("0.0.0.0") files = os.listdir(dirname) lib_name_out = dirname for file in files: if os.path.isdir(os.path.join(dirname, file)): ver_comp = ver_parse(file) - if ver_comp > ver_num: + if ver_comp > ver_num and ver_comp >= ver_min: # Try new SDK location. lib_name = os.path.join( os.path.join(dirname, file), "macOS/lib/MoltenVK.xcframework/macos-arm64_x86_64/" diff --git a/platform/web/detect.py b/platform/web/detect.py index bce03eb79e..89cdab9fa4 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -206,6 +206,11 @@ def configure(env: "Environment"): env.Append(LINKFLAGS=["-s", "USE_WEBGL2=1"]) # Allow use to take control of swapping WebGL buffers. env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"]) + # Breaking change since emscripten 3.1.51 + # https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#3151---121323 + if cc_semver >= (3, 1, 51): + # Enables the use of *glGetProcAddress() + env.Append(LINKFLAGS=["-s", "GL_ENABLE_GET_PROC_ADDRESS=1"]) if env["javascript_eval"]: env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"]) @@ -229,6 +234,9 @@ def configure(env: "Environment"): # Workaround https://github.com/emscripten-core/emscripten/issues/19781. if cc_semver >= (3, 1, 42) and cc_semver < (3, 1, 46): env.Append(LINKFLAGS=["-Wl,-u,scalbnf"]) + # Workaround https://github.com/emscripten-core/emscripten/issues/16836. + if cc_semver >= (3, 1, 47): + env.Append(LINKFLAGS=["-Wl,-u,_emscripten_run_callback_on_thread"]) if env["dlink_enabled"]: if env["proxy_to_pthread"]: diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 6af5a8dd80..af9008f452 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -35,6 +35,7 @@ void PhysicsBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08), DEFVAL(false)); ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_gravity"), &PhysicsBody2D::get_gravity); ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with); @@ -145,6 +146,12 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); } +Vector2 PhysicsBody2D::get_gravity() const { + PhysicsDirectBodyState2D *state = PhysicsServer2D::get_singleton()->body_get_direct_state(get_rid()); + ERR_FAIL_NULL_V(state, Vector2()); + return state->get_total_gravity(); +} + TypedArray PhysicsBody2D::get_collision_exceptions() { List exceptions; PhysicsServer2D::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 208e72c40f..62636b02f4 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -52,6 +52,7 @@ protected: public: bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true); bool test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref &r_collision = Ref(), real_t p_margin = 0.08, bool p_recovery_as_collision = false); + Vector2 get_gravity() const; TypedArray get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index be9323c07d..7d08d767c7 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -35,6 +35,7 @@ void PhysicsBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(false), DEFVAL(1)); ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(false), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("get_gravity"), &PhysicsBody3D::get_gravity); ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock); ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock); @@ -187,6 +188,12 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); } +Vector3 PhysicsBody3D::get_gravity() const { + PhysicsDirectBodyState3D *state = PhysicsServer3D::get_singleton()->body_get_direct_state(get_rid()); + ERR_FAIL_NULL_V(state, Vector3()); + return state->get_total_gravity(); +} + void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { if (p_lock) { locked_axis |= p_axis; diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index e8d5ef2103..e8373d5907 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -55,6 +55,7 @@ protected: public: bool move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true); bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref &r_collision = Ref(), real_t p_margin = 0.001, bool p_recovery_as_collision = false, int p_max_collisions = 1); + Vector3 get_gravity() const; void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 540c999131..e2925920a2 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -7333,7 +7333,7 @@ void TextEdit::_update_scrollbars() { int visible_rows = get_visible_line_count(); int total_rows = draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_total_visible_line_count(); - if (scroll_past_end_of_file_enabled) { + if (scroll_past_end_of_file_enabled && !fit_content_height) { total_rows += visible_rows - 1; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 10f4962c48..73a49fd427 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2587,12 +2587,8 @@ int Tree::_count_selected_items(TreeItem *p_from) const { } } - if (p_from->get_first_child()) { - count += _count_selected_items(p_from->get_first_child()); - } - - if (p_from->get_next()) { - count += _count_selected_items(p_from->get_next()); + for (TreeItem *c = p_from->get_first_child(); c; c = c->get_next()) { + count += _count_selected_items(c); } return count; diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp index 718d701811..35c905bd43 100644 --- a/scene/resources/height_map_shape_3d.cpp +++ b/scene/resources/height_map_shape_3d.cpp @@ -179,6 +179,14 @@ Vector HeightMapShape3D::get_map_data() const { return map_data; } +real_t HeightMapShape3D::get_min_height() const { + return min_height; +} + +real_t HeightMapShape3D::get_max_height() const { + return max_height; +} + void HeightMapShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape3D::set_map_width); ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape3D::get_map_width); @@ -186,6 +194,8 @@ void HeightMapShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_map_depth"), &HeightMapShape3D::get_map_depth); ClassDB::bind_method(D_METHOD("set_map_data", "data"), &HeightMapShape3D::set_map_data); ClassDB::bind_method(D_METHOD("get_map_data"), &HeightMapShape3D::get_map_data); + ClassDB::bind_method(D_METHOD("get_min_height"), &HeightMapShape3D::get_min_height); + ClassDB::bind_method(D_METHOD("get_max_height"), &HeightMapShape3D::get_max_height); ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_width", "get_map_width"); ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_depth", "get_map_depth"); diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h index 5fe00ec0b1..eff025c816 100644 --- a/scene/resources/height_map_shape_3d.h +++ b/scene/resources/height_map_shape_3d.h @@ -54,6 +54,9 @@ public: void set_map_data(Vector p_new); Vector get_map_data() const; + real_t get_min_height() const; + real_t get_max_height() const; + virtual Vector get_debug_mesh_lines() const override; virtual real_t get_enclosing_radius() const override; diff --git a/servers/rendering/storage/variant_converters.h b/servers/rendering/storage/variant_converters.h index ea291ba058..6e3c07237e 100644 --- a/servers/rendering/storage/variant_converters.h +++ b/servers/rendering/storage/variant_converters.h @@ -243,10 +243,10 @@ inline bool is_convertible_array(Variant::Type type) { } template -struct is_vector_type : std::false_type {}; +inline constexpr bool is_vector_type_v = false; template -struct is_vector_type> : std::true_type {}; +inline constexpr bool is_vector_type_v> = true; template void convert_item_std140(const T &p_item, P *p_write, bool p_compact = false) { @@ -274,7 +274,7 @@ Vector

convert_array_std140(const Variant &p_variant, [[maybe_unused]] bool p const Variant &item = array.get(i); P *offset = write + (i * elements); - if constexpr (is_vector_type::value) { + if constexpr (is_vector_type_v) { const T &vec = convert_to_vector(item, p_linear_color); convert_item_std140(vec, offset, true); } else {