summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/linux_builds.yml2
-rw-r--r--core/config/engine.cpp6
-rw-r--r--core/config/engine.h6
-rw-r--r--core/config/project_settings.cpp6
-rw-r--r--core/core_bind.cpp15
-rw-r--r--core/core_bind.h2
-rw-r--r--core/error/error_macros.cpp22
-rw-r--r--core/error/error_macros.h1
-rw-r--r--core/extension/extension_api_dump.cpp1
-rw-r--r--core/input/input.cpp2
-rw-r--r--core/math/geometry_2d.h12
-rw-r--r--core/string/char_range.inc613
-rw-r--r--core/string/char_utils.h66
-rw-r--r--core/string/ustring.cpp4
-rw-r--r--core/templates/a_hash_map.cpp41
-rw-r--r--core/templates/a_hash_map.h734
-rw-r--r--core/templates/hashfuncs.h7
-rw-r--r--core/variant/variant_op.cpp2
-rw-r--r--doc/classes/AudioServer.xml6
-rw-r--r--doc/classes/BackBufferCopy.xml1
-rw-r--r--doc/classes/Control.xml2
-rw-r--r--doc/classes/DisplayServer.xml13
-rw-r--r--doc/classes/EditorDebuggerPlugin.xml32
-rw-r--r--doc/classes/EditorDebuggerSession.xml2
-rw-r--r--doc/classes/EngineDebugger.xml3
-rw-r--r--doc/classes/Geometry2D.xml14
-rw-r--r--doc/classes/HTTPRequest.xml2
-rw-r--r--doc/classes/InputEventMouseMotion.xml2
-rw-r--r--doc/classes/ProjectSettings.xml2
-rw-r--r--doc/classes/ResourceImporterOBJ.xml13
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp2
-rw-r--r--drivers/egl/egl_manager.cpp24
-rw-r--r--drivers/egl/egl_manager.h2
-rw-r--r--drivers/gles3/storage/material_storage.cpp4
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.cpp235
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.h5
-rw-r--r--editor/debugger/script_editor_debugger.cpp2
-rw-r--r--editor/editor_file_system.cpp156
-rw-r--r--editor/editor_file_system.h17
-rw-r--r--editor/editor_properties.cpp8
-rw-r--r--editor/editor_properties.h2
-rw-r--r--editor/filesystem_dock.cpp92
-rw-r--r--editor/gui/editor_spin_slider.cpp4
-rw-r--r--editor/gui/editor_spin_slider.h2
-rw-r--r--editor/import/3d/resource_importer_obj.cpp83
-rw-r--r--editor/import/resource_importer_wav.cpp15
-rw-r--r--editor/import_dock.cpp17
-rw-r--r--editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp57
-rw-r--r--editor/plugins/gizmos/gizmo_3d_helper.cpp95
-rw-r--r--editor/plugins/gizmos/gizmo_3d_helper.h5
-rw-r--r--editor/scene_tree_dock.cpp9
-rw-r--r--main/main.cpp14
-rw-r--r--modules/csg/editor/csg_gizmos.cpp54
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml2
-rw-r--r--modules/gdscript/gdscript.cpp28
-rw-r--r--modules/gdscript/gdscript.h1
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp14
-rw-r--r--modules/gdscript/gdscript_cache.cpp3
-rw-r--r--modules/gdscript/gdscript_editor.cpp10
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/gdscript/gdscript_parser.h4
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp492
-rw-r--r--modules/gdscript/gdscript_vm.cpp22
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript_utility_implicit_conversion.gd12
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript_utility_implicit_conversion.out5
-rw-r--r--modules/openxr/SCsub2
-rw-r--r--modules/openxr/extensions/platform/openxr_opengl_extension.cpp37
-rw-r--r--modules/openxr/extensions/platform/openxr_opengl_extension.h11
-rw-r--r--modules/openxr/openxr_platform_inc.h7
-rw-r--r--modules/openxr/scene/openxr_composition_layer.cpp4
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp12
-rw-r--r--platform/android/detect.py8
-rw-r--r--platform/android/display_server_android.cpp8
-rw-r--r--platform/ios/detect.py8
-rw-r--r--platform/linuxbsd/detect.py9
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp12
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp12
-rw-r--r--platform/macos/detect.py9
-rw-r--r--platform/macos/display_server_macos.mm12
-rw-r--r--platform/web/detect.py8
-rw-r--r--platform/windows/detect.py9
-rw-r--r--platform/windows/display_server_windows.cpp12
-rw-r--r--platform_methods.py10
-rw-r--r--scene/3d/skeleton_3d.h3
-rw-r--r--scene/animation/animation_mixer.cpp66
-rw-r--r--scene/animation/animation_mixer.h21
-rw-r--r--scene/animation/animation_node_state_machine.cpp2
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/animation/animation_player.h4
-rw-r--r--scene/animation/animation_tree.cpp32
-rw-r--r--scene/animation/animation_tree.h47
-rw-r--r--scene/gui/button.cpp17
-rw-r--r--scene/gui/color_picker.cpp20
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/tree.cpp20
-rw-r--r--scene/resources/2d/tile_set.cpp8
-rw-r--r--scene/resources/style_box_flat.cpp24
-rw-r--r--servers/audio_server.cpp6
-rw-r--r--servers/audio_server.h2
-rw-r--r--servers/display_server.cpp2
-rw-r--r--servers/display_server.h2
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_scene_occlusion_cull.h6
-rw-r--r--servers/rendering/rendering_device_graph.cpp9
-rw-r--r--servers/rendering/rendering_device_graph.h6
-rw-r--r--servers/rendering/shader_types.cpp9
-rw-r--r--tests/core/string/test_string.h18
-rw-r--r--tests/core/templates/test_a_hash_map.h297
-rw-r--r--tests/scene/test_tree.h126
-rw-r--r--tests/test_main.cpp1
111 files changed, 3036 insertions, 1048 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 911c377c50..c4885ce131 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -106,7 +106,7 @@ jobs:
# TODO: Figure out somehow how to embed this one.
- name: wayland-scanner dependency
run: |
- sudo apt-get install libwayland-bin
+ sudo apt-get install libwayland-bin libegl-dev
- name: Free disk space on runner
run: |
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index ccdfc78df1..fa6bbe0646 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -321,6 +321,12 @@ bool Engine::is_extra_gpu_memory_tracking_enabled() const {
return extra_gpu_memory_tracking;
}
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+bool Engine::is_accurate_breadcrumbs_enabled() const {
+ return accurate_breadcrumbs;
+}
+#endif
+
void Engine::set_print_to_stdout(bool p_enabled) {
CoreGlobals::print_line_enabled = p_enabled;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 1d95905a03..c71b7975ed 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -75,6 +75,9 @@ private:
bool use_validation_layers = false;
bool generate_spirv_debug_info = false;
bool extra_gpu_memory_tracking = false;
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+ bool accurate_breadcrumbs = false;
+#endif
int32_t gpu_idx = -1;
uint64_t _process_frames = 0;
@@ -191,6 +194,9 @@ public:
bool is_validation_layers_enabled() const;
bool is_generate_spirv_debug_info_enabled() const;
bool is_extra_gpu_memory_tracking_enabled() const;
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+ bool is_accurate_breadcrumbs_enabled() const;
+#endif
int32_t get_gpu_index() const;
void increment_frames_drawn();
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 129f5df070..a342d4963c 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -517,9 +517,9 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {
Dictionary action = E.value.variant;
Array events = action["events"];
for (int i = 0; i < events.size(); i++) {
- Ref<InputEvent> x = events[i];
- if (x->get_device() == -1) { // -1 was the previous value (GH-97707).
- x->set_device(InputEvent::DEVICE_ID_ALL_DEVICES);
+ Ref<InputEvent> ev = events[i];
+ if (ev.is_valid() && ev->get_device() == -1) { // -1 was the previous value (GH-97707).
+ ev->set_device(InputEvent::DEVICE_ID_ALL_DEVICES);
}
}
}
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 54e7d2e324..9bec2a9504 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -922,6 +922,19 @@ Dictionary Geometry2D::make_atlas(const Vector<Size2> &p_rects) {
return ret;
}
+TypedArray<Point2i> Geometry2D::bresenham_line(const Point2i &p_from, const Point2i &p_to) {
+ Vector<Point2i> points = ::Geometry2D::bresenham_line(p_from, p_to);
+
+ TypedArray<Point2i> result;
+ result.resize(points.size());
+
+ for (int i = 0; i < points.size(); i++) {
+ result[i] = points[i];
+ }
+
+ return result;
+}
+
void Geometry2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &Geometry2D::is_point_in_circle);
ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &Geometry2D::segment_intersects_circle);
@@ -956,6 +969,8 @@ void Geometry2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &Geometry2D::make_atlas);
+ ClassDB::bind_method(D_METHOD("bresenham_line", "from", "to"), &Geometry2D::bresenham_line);
+
BIND_ENUM_CONSTANT(OPERATION_UNION);
BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE);
BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
diff --git a/core/core_bind.h b/core/core_bind.h
index 75b5c20b42..1467bfc7ab 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -326,6 +326,8 @@ public:
Dictionary make_atlas(const Vector<Size2> &p_rects);
+ TypedArray<Point2i> bresenham_line(const Point2i &p_from, const Point2i &p_to);
+
Geometry2D() { singleton = this; }
};
diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp
index e11cf40511..2a6dfe67d8 100644
--- a/core/error/error_macros.cpp
+++ b/core/error/error_macros.cpp
@@ -109,6 +109,28 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
_global_unlock();
}
+// For printing errors when we may crash at any point, so we must flush ASAP a lot of lines
+// but we don't want to make it noisy by printing lots of file & line info (because it's already
+// been printing by a preceding _err_print_error).
+void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) {
+ if (OS::get_singleton()) {
+ OS::get_singleton()->printerr("ERROR: %s\n", p_error.utf8().get_data());
+ } else {
+ // Fallback if errors happen before OS init or after it's destroyed.
+ const char *err_details = p_error.utf8().get_data();
+ fprintf(stderr, "ERROR: %s\n", err_details);
+ }
+
+ _global_lock();
+ ErrorHandlerList *l = error_handler_list;
+ while (l) {
+ l->errfunc(l->userdata, "", "", 0, p_error.utf8().get_data(), "", false, p_type);
+ l = l->next;
+ }
+
+ _global_unlock();
+}
+
// Errors with message. (All combinations of p_error and p_message as String or char*.)
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) {
_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type);
diff --git a/core/error/error_macros.h b/core/error/error_macros.h
index 7f40ca2822..ec5f07dc21 100644
--- a/core/error/error_macros.h
+++ b/core/error/error_macros.h
@@ -70,6 +70,7 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
+void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false);
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false);
void _err_flush_stdout();
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 2df3f7ef74..51ddb3d3b3 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -117,6 +117,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
header["version_patch"] = 0;
#endif
header["version_status"] = VERSION_STATUS;
+ header["version_status_version"] = VERSION_STATUS_VERSION;
header["version_build"] = VERSION_BUILD;
header["version_full_name"] = VERSION_FULL_NAME;
diff --git a/core/input/input.cpp b/core/input/input.cpp
index ab4f40e04e..eb3bd5b56d 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -692,6 +692,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
button_event->set_canceled(st->is_canceled());
button_event->set_button_index(MouseButton::LEFT);
button_event->set_double_click(st->is_double_tap());
+ button_event->set_window_id(st->get_window_id());
BitField<MouseButtonMask> ev_bm = mouse_button_mask;
if (st->is_pressed()) {
@@ -729,6 +730,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
motion_event->set_velocity(sd->get_velocity());
motion_event->set_screen_velocity(sd->get_screen_velocity());
motion_event->set_button_mask(mouse_button_mask);
+ motion_event->set_window_id(sd->get_window_id());
_parse_input_event_impl(motion_event, true);
}
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index 1a9ede981c..e811597a88 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -453,17 +453,17 @@ public:
return H;
}
- static Vector<Point2i> bresenham_line(const Point2i &p_start, const Point2i &p_end) {
+ static Vector<Point2i> bresenham_line(const Point2i &p_from, const Point2i &p_to) {
Vector<Point2i> points;
- Vector2i delta = (p_end - p_start).abs() * 2;
- Vector2i step = (p_end - p_start).sign();
- Vector2i current = p_start;
+ Vector2i delta = (p_to - p_from).abs() * 2;
+ Vector2i step = (p_to - p_from).sign();
+ Vector2i current = p_from;
if (delta.x > delta.y) {
int err = delta.x / 2;
- for (; current.x != p_end.x; current.x += step.x) {
+ for (; current.x != p_to.x; current.x += step.x) {
points.push_back(current);
err -= delta.y;
@@ -475,7 +475,7 @@ public:
} else {
int err = delta.y / 2;
- for (; current.y != p_end.y; current.y += step.y) {
+ for (; current.y != p_to.y; current.y += step.y) {
points.push_back(current);
err -= delta.x;
diff --git a/core/string/char_range.inc b/core/string/char_range.inc
index 9d9bb188ed..7a7bf8422d 100644
--- a/core/string/char_range.inc
+++ b/core/string/char_range.inc
@@ -35,14 +35,17 @@
#include "core/typedefs.h"
+// Unicode Derived Core Properties
+// Source: https://www.unicode.org/Public/16.0.0/ucd/DerivedCoreProperties.txt
+
struct CharRange {
char32_t start;
char32_t end;
};
-inline constexpr CharRange xid_start[] = {
+constexpr inline CharRange xid_start[] = {
{ 0x41, 0x5a },
- { 0x5f, 0x5f },
+ { 0x5f, 0x5f }, // Underscore technically isn't in XID_Start, but for our purposes it's included.
{ 0x61, 0x7a },
{ 0xaa, 0xaa },
{ 0xb5, 0xb5 },
@@ -56,7 +59,7 @@ inline constexpr CharRange xid_start[] = {
{ 0x2ee, 0x2ee },
{ 0x370, 0x374 },
{ 0x376, 0x377 },
- { 0x37a, 0x37d },
+ { 0x37b, 0x37d },
{ 0x37f, 0x37f },
{ 0x386, 0x386 },
{ 0x388, 0x38a },
@@ -184,7 +187,7 @@ inline constexpr CharRange xid_start[] = {
{ 0xdbd, 0xdbd },
{ 0xdc0, 0xdc6 },
{ 0xe01, 0xe30 },
- { 0xe32, 0xe33 },
+ { 0xe32, 0xe32 },
{ 0xe40, 0xe46 },
{ 0xe81, 0xe82 },
{ 0xe84, 0xe84 },
@@ -192,7 +195,7 @@ inline constexpr CharRange xid_start[] = {
{ 0xe8c, 0xea3 },
{ 0xea5, 0xea5 },
{ 0xea7, 0xeb0 },
- { 0xeb2, 0xeb3 },
+ { 0xeb2, 0xeb2 },
{ 0xebd, 0xebd },
{ 0xec0, 0xec4 },
{ 0xec6, 0xec6 },
@@ -247,8 +250,7 @@ inline constexpr CharRange xid_start[] = {
{ 0x17d7, 0x17d7 },
{ 0x17dc, 0x17dc },
{ 0x1820, 0x1878 },
- { 0x1880, 0x1884 },
- { 0x1887, 0x18a8 },
+ { 0x1880, 0x18a8 },
{ 0x18aa, 0x18aa },
{ 0x18b0, 0x18f5 },
{ 0x1900, 0x191e },
@@ -267,7 +269,7 @@ inline constexpr CharRange xid_start[] = {
{ 0x1c00, 0x1c23 },
{ 0x1c4d, 0x1c4f },
{ 0x1c5a, 0x1c7d },
- { 0x1c80, 0x1c88 },
+ { 0x1c80, 0x1c8a },
{ 0x1c90, 0x1cba },
{ 0x1cbd, 0x1cbf },
{ 0x1ce9, 0x1cec },
@@ -332,7 +334,7 @@ inline constexpr CharRange xid_start[] = {
{ 0x3031, 0x3035 },
{ 0x3038, 0x303c },
{ 0x3041, 0x3096 },
- { 0x309b, 0x309f },
+ { 0x309d, 0x309f },
{ 0x30a1, 0x30fa },
{ 0x30fc, 0x30ff },
{ 0x3105, 0x312f },
@@ -350,10 +352,10 @@ inline constexpr CharRange xid_start[] = {
{ 0xa6a0, 0xa6ef },
{ 0xa717, 0xa71f },
{ 0xa722, 0xa788 },
- { 0xa78b, 0xa7ca },
+ { 0xa78b, 0xa7cd },
{ 0xa7d0, 0xa7d1 },
{ 0xa7d3, 0xa7d3 },
- { 0xa7d5, 0xa7d9 },
+ { 0xa7d5, 0xa7dc },
{ 0xa7f2, 0xa801 },
{ 0xa803, 0xa805 },
{ 0xa807, 0xa80a },
@@ -408,15 +410,22 @@ inline constexpr CharRange xid_start[] = {
{ 0xfb40, 0xfb41 },
{ 0xfb43, 0xfb44 },
{ 0xfb46, 0xfbb1 },
- { 0xfbd3, 0xfd3d },
+ { 0xfbd3, 0xfc5d },
+ { 0xfc64, 0xfd3d },
{ 0xfd50, 0xfd8f },
{ 0xfd92, 0xfdc7 },
- { 0xfdf0, 0xfdfb },
- { 0xfe70, 0xfe74 },
- { 0xfe76, 0xfefc },
+ { 0xfdf0, 0xfdf9 },
+ { 0xfe71, 0xfe71 },
+ { 0xfe73, 0xfe73 },
+ { 0xfe77, 0xfe77 },
+ { 0xfe79, 0xfe79 },
+ { 0xfe7b, 0xfe7b },
+ { 0xfe7d, 0xfe7d },
+ { 0xfe7f, 0xfefc },
{ 0xff21, 0xff3a },
{ 0xff41, 0xff5a },
- { 0xff66, 0xffbe },
+ { 0xff66, 0xff9d },
+ { 0xffa0, 0xffbe },
{ 0xffc2, 0xffc7 },
{ 0xffca, 0xffcf },
{ 0xffd2, 0xffd7 },
@@ -451,6 +460,7 @@ inline constexpr CharRange xid_start[] = {
{ 0x105a3, 0x105b1 },
{ 0x105b3, 0x105b9 },
{ 0x105bb, 0x105bc },
+ { 0x105c0, 0x105f3 },
{ 0x10600, 0x10736 },
{ 0x10740, 0x10755 },
{ 0x10760, 0x10767 },
@@ -487,8 +497,11 @@ inline constexpr CharRange xid_start[] = {
{ 0x10c80, 0x10cb2 },
{ 0x10cc0, 0x10cf2 },
{ 0x10d00, 0x10d23 },
+ { 0x10d4a, 0x10d65 },
+ { 0x10d6f, 0x10d85 },
{ 0x10e80, 0x10ea9 },
{ 0x10eb0, 0x10eb1 },
+ { 0x10ec2, 0x10ec4 },
{ 0x10f00, 0x10f1c },
{ 0x10f27, 0x10f27 },
{ 0x10f30, 0x10f45 },
@@ -511,6 +524,7 @@ inline constexpr CharRange xid_start[] = {
{ 0x111dc, 0x111dc },
{ 0x11200, 0x11211 },
{ 0x11213, 0x1122b },
+ { 0x1123f, 0x11240 },
{ 0x11280, 0x11286 },
{ 0x11288, 0x11288 },
{ 0x1128a, 0x1128d },
@@ -526,6 +540,13 @@ inline constexpr CharRange xid_start[] = {
{ 0x1133d, 0x1133d },
{ 0x11350, 0x11350 },
{ 0x1135d, 0x11361 },
+ { 0x11380, 0x11389 },
+ { 0x1138b, 0x1138b },
+ { 0x1138e, 0x1138e },
+ { 0x11390, 0x113b5 },
+ { 0x113b7, 0x113b7 },
+ { 0x113d1, 0x113d1 },
+ { 0x113d3, 0x113d3 },
{ 0x11400, 0x11434 },
{ 0x11447, 0x1144a },
{ 0x1145f, 0x11461 },
@@ -560,6 +581,7 @@ inline constexpr CharRange xid_start[] = {
{ 0x11a5c, 0x11a89 },
{ 0x11a9d, 0x11a9d },
{ 0x11ab0, 0x11af8 },
+ { 0x11bc0, 0x11be0 },
{ 0x11c00, 0x11c08 },
{ 0x11c0a, 0x11c2e },
{ 0x11c40, 0x11c40 },
@@ -573,13 +595,19 @@ inline constexpr CharRange xid_start[] = {
{ 0x11d6a, 0x11d89 },
{ 0x11d98, 0x11d98 },
{ 0x11ee0, 0x11ef2 },
+ { 0x11f02, 0x11f02 },
+ { 0x11f04, 0x11f10 },
+ { 0x11f12, 0x11f33 },
{ 0x11fb0, 0x11fb0 },
{ 0x12000, 0x12399 },
{ 0x12400, 0x1246e },
{ 0x12480, 0x12543 },
{ 0x12f90, 0x12ff0 },
- { 0x13000, 0x1342e },
+ { 0x13000, 0x1342f },
+ { 0x13441, 0x13446 },
+ { 0x13460, 0x143fa },
{ 0x14400, 0x14646 },
+ { 0x16100, 0x1611d },
{ 0x16800, 0x16a38 },
{ 0x16a40, 0x16a5e },
{ 0x16a70, 0x16abe },
@@ -588,6 +616,7 @@ inline constexpr CharRange xid_start[] = {
{ 0x16b40, 0x16b43 },
{ 0x16b63, 0x16b77 },
{ 0x16b7d, 0x16b8f },
+ { 0x16d40, 0x16d6c },
{ 0x16e40, 0x16e7f },
{ 0x16f00, 0x16f4a },
{ 0x16f50, 0x16f50 },
@@ -596,12 +625,14 @@ inline constexpr CharRange xid_start[] = {
{ 0x16fe3, 0x16fe3 },
{ 0x17000, 0x187f7 },
{ 0x18800, 0x18cd5 },
- { 0x18d00, 0x18d08 },
+ { 0x18cff, 0x18d08 },
{ 0x1aff0, 0x1aff3 },
{ 0x1aff5, 0x1affb },
{ 0x1affd, 0x1affe },
{ 0x1b000, 0x1b122 },
+ { 0x1b132, 0x1b132 },
{ 0x1b150, 0x1b152 },
+ { 0x1b155, 0x1b155 },
{ 0x1b164, 0x1b167 },
{ 0x1b170, 0x1b2fb },
{ 0x1bc00, 0x1bc6a },
@@ -639,11 +670,16 @@ inline constexpr CharRange xid_start[] = {
{ 0x1d7aa, 0x1d7c2 },
{ 0x1d7c4, 0x1d7cb },
{ 0x1df00, 0x1df1e },
+ { 0x1df25, 0x1df2a },
+ { 0x1e030, 0x1e06d },
{ 0x1e100, 0x1e12c },
{ 0x1e137, 0x1e13d },
{ 0x1e14e, 0x1e14e },
{ 0x1e290, 0x1e2ad },
{ 0x1e2c0, 0x1e2eb },
+ { 0x1e4d0, 0x1e4eb },
+ { 0x1e5d0, 0x1e5ed },
+ { 0x1e5f0, 0x1e5f0 },
{ 0x1e7e0, 0x1e7e6 },
{ 0x1e7e8, 0x1e7eb },
{ 0x1e7ed, 0x1e7ee },
@@ -685,15 +721,17 @@ inline constexpr CharRange xid_start[] = {
{ 0x1eea5, 0x1eea9 },
{ 0x1eeab, 0x1eebb },
{ 0x20000, 0x2a6df },
- { 0x2a700, 0x2b738 },
+ { 0x2a700, 0x2b739 },
{ 0x2b740, 0x2b81d },
{ 0x2b820, 0x2cea1 },
{ 0x2ceb0, 0x2ebe0 },
+ { 0x2ebf0, 0x2ee5d },
{ 0x2f800, 0x2fa1d },
{ 0x30000, 0x3134a },
+ { 0x31350, 0x323af },
};
-inline constexpr CharRange xid_continue[] = {
+constexpr inline CharRange xid_continue[] = {
{ 0x30, 0x39 },
{ 0x41, 0x5a },
{ 0x5f, 0x5f },
@@ -711,7 +749,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x2ee, 0x2ee },
{ 0x300, 0x374 },
{ 0x376, 0x377 },
- { 0x37a, 0x37d },
+ { 0x37b, 0x37d },
{ 0x37f, 0x37f },
{ 0x386, 0x38a },
{ 0x38c, 0x38c },
@@ -747,7 +785,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x860, 0x86a },
{ 0x870, 0x887 },
{ 0x889, 0x88e },
- { 0x898, 0x8e1 },
+ { 0x897, 0x8e1 },
{ 0x8e3, 0x963 },
{ 0x966, 0x96f },
{ 0x971, 0x983 },
@@ -852,7 +890,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0xcdd, 0xcde },
{ 0xce0, 0xce3 },
{ 0xce6, 0xcef },
- { 0xcf1, 0xcf2 },
+ { 0xcf1, 0xcf3 },
{ 0xd00, 0xd0c },
{ 0xd0e, 0xd10 },
{ 0xd12, 0xd44 },
@@ -885,7 +923,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0xea7, 0xebd },
{ 0xec0, 0xec4 },
{ 0xec6, 0xec6 },
- { 0xec8, 0xecd },
+ { 0xec8, 0xece },
{ 0xed0, 0xed9 },
{ 0xedc, 0xedf },
{ 0xf00, 0xf00 },
@@ -923,8 +961,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x1312, 0x1315 },
{ 0x1318, 0x135a },
{ 0x135d, 0x135f },
- { 0x1369, 0x1369 },
- { 0x1371, 0x1371 },
+ { 0x1369, 0x1371 },
{ 0x1380, 0x138f },
{ 0x13a0, 0x13f5 },
{ 0x13f8, 0x13fd },
@@ -971,7 +1008,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x1c00, 0x1c37 },
{ 0x1c40, 0x1c49 },
{ 0x1c4d, 0x1c7d },
- { 0x1c80, 0x1c88 },
+ { 0x1c80, 0x1c8a },
{ 0x1c90, 0x1cba },
{ 0x1cbd, 0x1cbf },
{ 0x1cd0, 0x1cd2 },
@@ -995,6 +1032,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x1fe0, 0x1fec },
{ 0x1ff2, 0x1ff4 },
{ 0x1ff6, 0x1ffc },
+ { 0x200c, 0x200d },
{ 0x203f, 0x2040 },
{ 0x2054, 0x2054 },
{ 0x2071, 0x2071 },
@@ -1038,9 +1076,9 @@ inline constexpr CharRange xid_continue[] = {
{ 0x3031, 0x3035 },
{ 0x3038, 0x303c },
{ 0x3041, 0x3096 },
- { 0x3099, 0x309f },
- { 0x30a1, 0x30fa },
- { 0x30fc, 0x30ff },
+ { 0x3099, 0x309a },
+ { 0x309d, 0x309f },
+ { 0x30a1, 0x30ff },
{ 0x3105, 0x312f },
{ 0x3131, 0x318e },
{ 0x31a0, 0x31bf },
@@ -1055,10 +1093,10 @@ inline constexpr CharRange xid_continue[] = {
{ 0xa67f, 0xa6f1 },
{ 0xa717, 0xa71f },
{ 0xa722, 0xa788 },
- { 0xa78b, 0xa7ca },
+ { 0xa78b, 0xa7cd },
{ 0xa7d0, 0xa7d1 },
{ 0xa7d3, 0xa7d3 },
- { 0xa7d5, 0xa7d9 },
+ { 0xa7d5, 0xa7dc },
{ 0xa7f2, 0xa827 },
{ 0xa82c, 0xa82c },
{ 0xa840, 0xa873 },
@@ -1104,21 +1142,27 @@ inline constexpr CharRange xid_continue[] = {
{ 0xfb40, 0xfb41 },
{ 0xfb43, 0xfb44 },
{ 0xfb46, 0xfbb1 },
- { 0xfbd3, 0xfd3d },
+ { 0xfbd3, 0xfc5d },
+ { 0xfc64, 0xfd3d },
{ 0xfd50, 0xfd8f },
{ 0xfd92, 0xfdc7 },
- { 0xfdf0, 0xfdfb },
+ { 0xfdf0, 0xfdf9 },
{ 0xfe00, 0xfe0f },
{ 0xfe20, 0xfe2f },
{ 0xfe33, 0xfe34 },
{ 0xfe4d, 0xfe4f },
- { 0xfe70, 0xfe74 },
- { 0xfe76, 0xfefc },
+ { 0xfe71, 0xfe71 },
+ { 0xfe73, 0xfe73 },
+ { 0xfe77, 0xfe77 },
+ { 0xfe79, 0xfe79 },
+ { 0xfe7b, 0xfe7b },
+ { 0xfe7d, 0xfe7d },
+ { 0xfe7f, 0xfefc },
{ 0xff10, 0xff19 },
{ 0xff21, 0xff3a },
{ 0xff3f, 0xff3f },
{ 0xff41, 0xff5a },
- { 0xff66, 0xffbe },
+ { 0xff65, 0xffbe },
{ 0xffc2, 0xffc7 },
{ 0xffca, 0xffcf },
{ 0xffd2, 0xffd7 },
@@ -1156,6 +1200,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x105a3, 0x105b1 },
{ 0x105b3, 0x105b9 },
{ 0x105bb, 0x105bc },
+ { 0x105c0, 0x105f3 },
{ 0x10600, 0x10736 },
{ 0x10740, 0x10755 },
{ 0x10760, 0x10767 },
@@ -1196,10 +1241,14 @@ inline constexpr CharRange xid_continue[] = {
{ 0x10cc0, 0x10cf2 },
{ 0x10d00, 0x10d27 },
{ 0x10d30, 0x10d39 },
+ { 0x10d40, 0x10d65 },
+ { 0x10d69, 0x10d6d },
+ { 0x10d6f, 0x10d85 },
{ 0x10e80, 0x10ea9 },
{ 0x10eab, 0x10eac },
{ 0x10eb0, 0x10eb1 },
- { 0x10f00, 0x10f1c },
+ { 0x10ec2, 0x10ec4 },
+ { 0x10efc, 0x10f1c },
{ 0x10f27, 0x10f27 },
{ 0x10f30, 0x10f50 },
{ 0x10f70, 0x10f85 },
@@ -1222,7 +1271,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x111dc, 0x111dc },
{ 0x11200, 0x11211 },
{ 0x11213, 0x11237 },
- { 0x1123e, 0x1123e },
+ { 0x1123e, 0x11241 },
{ 0x11280, 0x11286 },
{ 0x11288, 0x11288 },
{ 0x1128a, 0x1128d },
@@ -1245,6 +1294,16 @@ inline constexpr CharRange xid_continue[] = {
{ 0x1135d, 0x11363 },
{ 0x11366, 0x1136c },
{ 0x11370, 0x11374 },
+ { 0x11380, 0x11389 },
+ { 0x1138b, 0x1138b },
+ { 0x1138e, 0x1138e },
+ { 0x11390, 0x113b5 },
+ { 0x113b7, 0x113c0 },
+ { 0x113c2, 0x113c2 },
+ { 0x113c5, 0x113c5 },
+ { 0x113c7, 0x113ca },
+ { 0x113cc, 0x113d3 },
+ { 0x113e1, 0x113e2 },
{ 0x11400, 0x1144a },
{ 0x11450, 0x11459 },
{ 0x1145e, 0x11461 },
@@ -1259,6 +1318,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x11650, 0x11659 },
{ 0x11680, 0x116b8 },
{ 0x116c0, 0x116c9 },
+ { 0x116d0, 0x116e3 },
{ 0x11700, 0x1171a },
{ 0x1171d, 0x1172b },
{ 0x11730, 0x11739 },
@@ -1282,6 +1342,8 @@ inline constexpr CharRange xid_continue[] = {
{ 0x11a50, 0x11a99 },
{ 0x11a9d, 0x11a9d },
{ 0x11ab0, 0x11af8 },
+ { 0x11bc0, 0x11be0 },
+ { 0x11bf0, 0x11bf9 },
{ 0x11c00, 0x11c08 },
{ 0x11c0a, 0x11c36 },
{ 0x11c38, 0x11c40 },
@@ -1303,13 +1365,20 @@ inline constexpr CharRange xid_continue[] = {
{ 0x11d93, 0x11d98 },
{ 0x11da0, 0x11da9 },
{ 0x11ee0, 0x11ef6 },
+ { 0x11f00, 0x11f10 },
+ { 0x11f12, 0x11f3a },
+ { 0x11f3e, 0x11f42 },
+ { 0x11f50, 0x11f5a },
{ 0x11fb0, 0x11fb0 },
{ 0x12000, 0x12399 },
{ 0x12400, 0x1246e },
{ 0x12480, 0x12543 },
{ 0x12f90, 0x12ff0 },
- { 0x13000, 0x1342e },
+ { 0x13000, 0x1342f },
+ { 0x13440, 0x13455 },
+ { 0x13460, 0x143fa },
{ 0x14400, 0x14646 },
+ { 0x16100, 0x16139 },
{ 0x16800, 0x16a38 },
{ 0x16a40, 0x16a5e },
{ 0x16a60, 0x16a69 },
@@ -1322,6 +1391,8 @@ inline constexpr CharRange xid_continue[] = {
{ 0x16b50, 0x16b59 },
{ 0x16b63, 0x16b77 },
{ 0x16b7d, 0x16b8f },
+ { 0x16d40, 0x16d6c },
+ { 0x16d70, 0x16d79 },
{ 0x16e40, 0x16e7f },
{ 0x16f00, 0x16f4a },
{ 0x16f4f, 0x16f87 },
@@ -1331,12 +1402,14 @@ inline constexpr CharRange xid_continue[] = {
{ 0x16ff0, 0x16ff1 },
{ 0x17000, 0x187f7 },
{ 0x18800, 0x18cd5 },
- { 0x18d00, 0x18d08 },
+ { 0x18cff, 0x18d08 },
{ 0x1aff0, 0x1aff3 },
{ 0x1aff5, 0x1affb },
{ 0x1affd, 0x1affe },
{ 0x1b000, 0x1b122 },
+ { 0x1b132, 0x1b132 },
{ 0x1b150, 0x1b152 },
+ { 0x1b155, 0x1b155 },
{ 0x1b164, 0x1b167 },
{ 0x1b170, 0x1b2fb },
{ 0x1bc00, 0x1bc6a },
@@ -1344,6 +1417,7 @@ inline constexpr CharRange xid_continue[] = {
{ 0x1bc80, 0x1bc88 },
{ 0x1bc90, 0x1bc99 },
{ 0x1bc9d, 0x1bc9e },
+ { 0x1ccf0, 0x1ccf9 },
{ 0x1cf00, 0x1cf2d },
{ 0x1cf30, 0x1cf46 },
{ 0x1d165, 0x1d169 },
@@ -1390,17 +1464,22 @@ inline constexpr CharRange xid_continue[] = {
{ 0x1da9b, 0x1da9f },
{ 0x1daa1, 0x1daaf },
{ 0x1df00, 0x1df1e },
+ { 0x1df25, 0x1df2a },
{ 0x1e000, 0x1e006 },
{ 0x1e008, 0x1e018 },
{ 0x1e01b, 0x1e021 },
{ 0x1e023, 0x1e024 },
{ 0x1e026, 0x1e02a },
+ { 0x1e030, 0x1e06d },
+ { 0x1e08f, 0x1e08f },
{ 0x1e100, 0x1e12c },
{ 0x1e130, 0x1e13d },
{ 0x1e140, 0x1e149 },
{ 0x1e14e, 0x1e14e },
{ 0x1e290, 0x1e2ae },
{ 0x1e2c0, 0x1e2f9 },
+ { 0x1e4d0, 0x1e4f9 },
+ { 0x1e5d0, 0x1e5fa },
{ 0x1e7e0, 0x1e7e6 },
{ 0x1e7e8, 0x1e7eb },
{ 0x1e7ed, 0x1e7ee },
@@ -1444,16 +1523,18 @@ inline constexpr CharRange xid_continue[] = {
{ 0x1eeab, 0x1eebb },
{ 0x1fbf0, 0x1fbf9 },
{ 0x20000, 0x2a6df },
- { 0x2a700, 0x2b738 },
+ { 0x2a700, 0x2b739 },
{ 0x2b740, 0x2b81d },
{ 0x2b820, 0x2cea1 },
{ 0x2ceb0, 0x2ebe0 },
+ { 0x2ebf0, 0x2ee5d },
{ 0x2f800, 0x2fa1d },
{ 0x30000, 0x3134a },
+ { 0x31350, 0x323af },
{ 0xe0100, 0xe01ef },
};
-inline constexpr CharRange uppercase_letter[] = {
+constexpr inline CharRange uppercase_letter[] = {
{ 0x41, 0x5a },
{ 0xc0, 0xd6 },
{ 0xd8, 0xde },
@@ -1730,6 +1811,7 @@ inline constexpr CharRange uppercase_letter[] = {
{ 0x10c7, 0x10c7 },
{ 0x10cd, 0x10cd },
{ 0x13a0, 0x13f5 },
+ { 0x1c89, 0x1c89 },
{ 0x1c90, 0x1cba },
{ 0x1cbd, 0x1cbf },
{ 0x1e00, 0x1e00 },
@@ -1884,7 +1966,9 @@ inline constexpr CharRange uppercase_letter[] = {
{ 0x2130, 0x2133 },
{ 0x213e, 0x213f },
{ 0x2145, 0x2145 },
+ { 0x2160, 0x216f },
{ 0x2183, 0x2183 },
+ { 0x24b6, 0x24cf },
{ 0x2c00, 0x2c2f },
{ 0x2c60, 0x2c60 },
{ 0x2c62, 0x2c64 },
@@ -2054,9 +2138,12 @@ inline constexpr CharRange uppercase_letter[] = {
{ 0xa7c2, 0xa7c2 },
{ 0xa7c4, 0xa7c7 },
{ 0xa7c9, 0xa7c9 },
+ { 0xa7cb, 0xa7cc },
{ 0xa7d0, 0xa7d0 },
{ 0xa7d6, 0xa7d6 },
{ 0xa7d8, 0xa7d8 },
+ { 0xa7da, 0xa7da },
+ { 0xa7dc, 0xa7dc },
{ 0xa7f5, 0xa7f5 },
{ 0xff21, 0xff3a },
{ 0x10400, 0x10427 },
@@ -2066,6 +2153,7 @@ inline constexpr CharRange uppercase_letter[] = {
{ 0x1058c, 0x10592 },
{ 0x10594, 0x10595 },
{ 0x10c80, 0x10cb2 },
+ { 0x10d50, 0x10d65 },
{ 0x118a0, 0x118bf },
{ 0x16e40, 0x16e5f },
{ 0x1d400, 0x1d419 },
@@ -2100,11 +2188,16 @@ inline constexpr CharRange uppercase_letter[] = {
{ 0x1d790, 0x1d7a8 },
{ 0x1d7ca, 0x1d7ca },
{ 0x1e900, 0x1e921 },
+ { 0x1f130, 0x1f149 },
+ { 0x1f150, 0x1f169 },
+ { 0x1f170, 0x1f189 },
};
-inline constexpr CharRange lowercase_letter[] = {
+constexpr inline CharRange lowercase_letter[] = {
{ 0x61, 0x7a },
+ { 0xaa, 0xaa },
{ 0xb5, 0xb5 },
+ { 0xba, 0xba },
{ 0xdf, 0xf6 },
{ 0xf8, 0xff },
{ 0x101, 0x101 },
@@ -2248,11 +2341,14 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0x24b, 0x24b },
{ 0x24d, 0x24d },
{ 0x24f, 0x293 },
- { 0x295, 0x2af },
+ { 0x295, 0x2b8 },
+ { 0x2c0, 0x2c1 },
+ { 0x2e0, 0x2e4 },
+ { 0x345, 0x345 },
{ 0x371, 0x371 },
{ 0x373, 0x373 },
{ 0x377, 0x377 },
- { 0x37b, 0x37d },
+ { 0x37a, 0x37d },
{ 0x390, 0x390 },
{ 0x3ac, 0x3ce },
{ 0x3d0, 0x3d1 },
@@ -2374,12 +2470,11 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0x52f, 0x52f },
{ 0x560, 0x588 },
{ 0x10d0, 0x10fa },
- { 0x10fd, 0x10ff },
+ { 0x10fc, 0x10ff },
{ 0x13f8, 0x13fd },
{ 0x1c80, 0x1c88 },
- { 0x1d00, 0x1d2b },
- { 0x1d6b, 0x1d77 },
- { 0x1d79, 0x1d9a },
+ { 0x1c8a, 0x1c8a },
+ { 0x1d00, 0x1dbf },
{ 0x1e01, 0x1e01 },
{ 0x1e03, 0x1e03 },
{ 0x1e05, 0x1e05 },
@@ -2524,6 +2619,9 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0x1fe0, 0x1fe7 },
{ 0x1ff2, 0x1ff4 },
{ 0x1ff6, 0x1ff7 },
+ { 0x2071, 0x2071 },
+ { 0x207f, 0x207f },
+ { 0x2090, 0x209c },
{ 0x210a, 0x210a },
{ 0x210e, 0x210f },
{ 0x2113, 0x2113 },
@@ -2533,7 +2631,9 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0x213c, 0x213d },
{ 0x2146, 0x2149 },
{ 0x214e, 0x214e },
+ { 0x2170, 0x217f },
{ 0x2184, 0x2184 },
+ { 0x24d0, 0x24e9 },
{ 0x2c30, 0x2c5f },
{ 0x2c61, 0x2c61 },
{ 0x2c65, 0x2c66 },
@@ -2542,7 +2642,7 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0x2c6c, 0x2c6c },
{ 0x2c71, 0x2c71 },
{ 0x2c73, 0x2c74 },
- { 0x2c76, 0x2c7b },
+ { 0x2c76, 0x2c7d },
{ 0x2c81, 0x2c81 },
{ 0x2c83, 0x2c83 },
{ 0x2c85, 0x2c85 },
@@ -2635,7 +2735,7 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0xa695, 0xa695 },
{ 0xa697, 0xa697 },
{ 0xa699, 0xa699 },
- { 0xa69b, 0xa69b },
+ { 0xa69b, 0xa69d },
{ 0xa723, 0xa723 },
{ 0xa725, 0xa725 },
{ 0xa727, 0xa727 },
@@ -2673,8 +2773,7 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0xa769, 0xa769 },
{ 0xa76b, 0xa76b },
{ 0xa76d, 0xa76d },
- { 0xa76f, 0xa76f },
- { 0xa771, 0xa778 },
+ { 0xa76f, 0xa778 },
{ 0xa77a, 0xa77a },
{ 0xa77c, 0xa77c },
{ 0xa77f, 0xa77f },
@@ -2707,15 +2806,18 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0xa7c3, 0xa7c3 },
{ 0xa7c8, 0xa7c8 },
{ 0xa7ca, 0xa7ca },
+ { 0xa7cd, 0xa7cd },
{ 0xa7d1, 0xa7d1 },
{ 0xa7d3, 0xa7d3 },
{ 0xa7d5, 0xa7d5 },
{ 0xa7d7, 0xa7d7 },
{ 0xa7d9, 0xa7d9 },
+ { 0xa7db, 0xa7db },
+ { 0xa7f2, 0xa7f4 },
{ 0xa7f6, 0xa7f6 },
- { 0xa7fa, 0xa7fa },
+ { 0xa7f8, 0xa7fa },
{ 0xab30, 0xab5a },
- { 0xab60, 0xab68 },
+ { 0xab5c, 0xab69 },
{ 0xab70, 0xabbf },
{ 0xfb00, 0xfb06 },
{ 0xfb13, 0xfb17 },
@@ -2726,7 +2828,12 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0x105a3, 0x105b1 },
{ 0x105b3, 0x105b9 },
{ 0x105bb, 0x105bc },
+ { 0x10780, 0x10780 },
+ { 0x10783, 0x10785 },
+ { 0x10787, 0x107b0 },
+ { 0x107b2, 0x107ba },
{ 0x10cc0, 0x10cf2 },
+ { 0x10d70, 0x10d85 },
{ 0x118c0, 0x118df },
{ 0x16e60, 0x16e7f },
{ 0x1d41a, 0x1d433 },
@@ -2760,10 +2867,11 @@ inline constexpr CharRange lowercase_letter[] = {
{ 0x1df00, 0x1df09 },
{ 0x1df0b, 0x1df1e },
{ 0x1df25, 0x1df2a },
+ { 0x1e030, 0x1e06d },
{ 0x1e922, 0x1e943 },
};
-inline constexpr CharRange unicode_letter[] = {
+constexpr inline CharRange unicode_letter[] = {
{ 0x41, 0x5a },
{ 0x61, 0x7a },
{ 0xaa, 0xaa },
@@ -2776,7 +2884,8 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x2e0, 0x2e4 },
{ 0x2ec, 0x2ec },
{ 0x2ee, 0x2ee },
- { 0x370, 0x374 },
+ { 0x345, 0x345 },
+ { 0x363, 0x374 },
{ 0x376, 0x377 },
{ 0x37a, 0x37d },
{ 0x37f, 0x37f },
@@ -2790,49 +2899,58 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x531, 0x556 },
{ 0x559, 0x559 },
{ 0x560, 0x588 },
+ { 0x5b0, 0x5bd },
+ { 0x5bf, 0x5bf },
+ { 0x5c1, 0x5c2 },
+ { 0x5c4, 0x5c5 },
+ { 0x5c7, 0x5c7 },
{ 0x5d0, 0x5ea },
{ 0x5ef, 0x5f2 },
- { 0x620, 0x64a },
- { 0x66e, 0x66f },
- { 0x671, 0x6d3 },
- { 0x6d5, 0x6d5 },
- { 0x6e5, 0x6e6 },
- { 0x6ee, 0x6ef },
+ { 0x610, 0x61a },
+ { 0x620, 0x657 },
+ { 0x659, 0x65f },
+ { 0x66e, 0x6d3 },
+ { 0x6d5, 0x6dc },
+ { 0x6e1, 0x6e8 },
+ { 0x6ed, 0x6ef },
{ 0x6fa, 0x6fc },
{ 0x6ff, 0x6ff },
- { 0x710, 0x710 },
- { 0x712, 0x72f },
- { 0x74d, 0x7a5 },
- { 0x7b1, 0x7b1 },
+ { 0x710, 0x73f },
+ { 0x74d, 0x7b1 },
{ 0x7ca, 0x7ea },
{ 0x7f4, 0x7f5 },
{ 0x7fa, 0x7fa },
- { 0x800, 0x815 },
- { 0x81a, 0x81a },
- { 0x824, 0x824 },
- { 0x828, 0x828 },
+ { 0x800, 0x817 },
+ { 0x81a, 0x82c },
{ 0x840, 0x858 },
{ 0x860, 0x86a },
{ 0x870, 0x887 },
{ 0x889, 0x88e },
+ { 0x897, 0x897 },
{ 0x8a0, 0x8c9 },
- { 0x904, 0x939 },
- { 0x93d, 0x93d },
- { 0x950, 0x950 },
- { 0x958, 0x961 },
- { 0x971, 0x980 },
+ { 0x8d4, 0x8df },
+ { 0x8e3, 0x8e9 },
+ { 0x8f0, 0x93b },
+ { 0x93d, 0x94c },
+ { 0x94e, 0x950 },
+ { 0x955, 0x963 },
+ { 0x971, 0x983 },
{ 0x985, 0x98c },
{ 0x98f, 0x990 },
{ 0x993, 0x9a8 },
{ 0x9aa, 0x9b0 },
{ 0x9b2, 0x9b2 },
{ 0x9b6, 0x9b9 },
- { 0x9bd, 0x9bd },
+ { 0x9bd, 0x9c4 },
+ { 0x9c7, 0x9c8 },
+ { 0x9cb, 0x9cc },
{ 0x9ce, 0x9ce },
+ { 0x9d7, 0x9d7 },
{ 0x9dc, 0x9dd },
- { 0x9df, 0x9e1 },
+ { 0x9df, 0x9e3 },
{ 0x9f0, 0x9f1 },
{ 0x9fc, 0x9fc },
+ { 0xa01, 0xa03 },
{ 0xa05, 0xa0a },
{ 0xa0f, 0xa10 },
{ 0xa13, 0xa28 },
@@ -2840,30 +2958,41 @@ inline constexpr CharRange unicode_letter[] = {
{ 0xa32, 0xa33 },
{ 0xa35, 0xa36 },
{ 0xa38, 0xa39 },
+ { 0xa3e, 0xa42 },
+ { 0xa47, 0xa48 },
+ { 0xa4b, 0xa4c },
+ { 0xa51, 0xa51 },
{ 0xa59, 0xa5c },
{ 0xa5e, 0xa5e },
- { 0xa72, 0xa74 },
+ { 0xa70, 0xa75 },
+ { 0xa81, 0xa83 },
{ 0xa85, 0xa8d },
{ 0xa8f, 0xa91 },
{ 0xa93, 0xaa8 },
{ 0xaaa, 0xab0 },
{ 0xab2, 0xab3 },
{ 0xab5, 0xab9 },
- { 0xabd, 0xabd },
+ { 0xabd, 0xac5 },
+ { 0xac7, 0xac9 },
+ { 0xacb, 0xacc },
{ 0xad0, 0xad0 },
- { 0xae0, 0xae1 },
- { 0xaf9, 0xaf9 },
+ { 0xae0, 0xae3 },
+ { 0xaf9, 0xafc },
+ { 0xb01, 0xb03 },
{ 0xb05, 0xb0c },
{ 0xb0f, 0xb10 },
{ 0xb13, 0xb28 },
{ 0xb2a, 0xb30 },
{ 0xb32, 0xb33 },
{ 0xb35, 0xb39 },
- { 0xb3d, 0xb3d },
+ { 0xb3d, 0xb44 },
+ { 0xb47, 0xb48 },
+ { 0xb4b, 0xb4c },
+ { 0xb56, 0xb57 },
{ 0xb5c, 0xb5d },
- { 0xb5f, 0xb61 },
+ { 0xb5f, 0xb63 },
{ 0xb71, 0xb71 },
- { 0xb83, 0xb83 },
+ { 0xb82, 0xb83 },
{ 0xb85, 0xb8a },
{ 0xb8e, 0xb90 },
{ 0xb92, 0xb95 },
@@ -2873,65 +3002,80 @@ inline constexpr CharRange unicode_letter[] = {
{ 0xba3, 0xba4 },
{ 0xba8, 0xbaa },
{ 0xbae, 0xbb9 },
+ { 0xbbe, 0xbc2 },
+ { 0xbc6, 0xbc8 },
+ { 0xbca, 0xbcc },
{ 0xbd0, 0xbd0 },
- { 0xc05, 0xc0c },
+ { 0xbd7, 0xbd7 },
+ { 0xc00, 0xc0c },
{ 0xc0e, 0xc10 },
{ 0xc12, 0xc28 },
{ 0xc2a, 0xc39 },
- { 0xc3d, 0xc3d },
+ { 0xc3d, 0xc44 },
+ { 0xc46, 0xc48 },
+ { 0xc4a, 0xc4c },
+ { 0xc55, 0xc56 },
{ 0xc58, 0xc5a },
{ 0xc5d, 0xc5d },
- { 0xc60, 0xc61 },
- { 0xc80, 0xc80 },
+ { 0xc60, 0xc63 },
+ { 0xc80, 0xc83 },
{ 0xc85, 0xc8c },
{ 0xc8e, 0xc90 },
{ 0xc92, 0xca8 },
{ 0xcaa, 0xcb3 },
{ 0xcb5, 0xcb9 },
- { 0xcbd, 0xcbd },
+ { 0xcbd, 0xcc4 },
+ { 0xcc6, 0xcc8 },
+ { 0xcca, 0xccc },
+ { 0xcd5, 0xcd6 },
{ 0xcdd, 0xcde },
- { 0xce0, 0xce1 },
- { 0xcf1, 0xcf2 },
- { 0xd04, 0xd0c },
+ { 0xce0, 0xce3 },
+ { 0xcf1, 0xcf3 },
+ { 0xd00, 0xd0c },
{ 0xd0e, 0xd10 },
{ 0xd12, 0xd3a },
- { 0xd3d, 0xd3d },
+ { 0xd3d, 0xd44 },
+ { 0xd46, 0xd48 },
+ { 0xd4a, 0xd4c },
{ 0xd4e, 0xd4e },
- { 0xd54, 0xd56 },
- { 0xd5f, 0xd61 },
+ { 0xd54, 0xd57 },
+ { 0xd5f, 0xd63 },
{ 0xd7a, 0xd7f },
+ { 0xd81, 0xd83 },
{ 0xd85, 0xd96 },
{ 0xd9a, 0xdb1 },
{ 0xdb3, 0xdbb },
{ 0xdbd, 0xdbd },
{ 0xdc0, 0xdc6 },
- { 0xe01, 0xe30 },
- { 0xe32, 0xe33 },
+ { 0xdcf, 0xdd4 },
+ { 0xdd6, 0xdd6 },
+ { 0xdd8, 0xddf },
+ { 0xdf2, 0xdf3 },
+ { 0xe01, 0xe3a },
{ 0xe40, 0xe46 },
+ { 0xe4d, 0xe4d },
{ 0xe81, 0xe82 },
{ 0xe84, 0xe84 },
{ 0xe86, 0xe8a },
{ 0xe8c, 0xea3 },
{ 0xea5, 0xea5 },
- { 0xea7, 0xeb0 },
- { 0xeb2, 0xeb3 },
- { 0xebd, 0xebd },
+ { 0xea7, 0xeb9 },
+ { 0xebb, 0xebd },
{ 0xec0, 0xec4 },
{ 0xec6, 0xec6 },
+ { 0xecd, 0xecd },
{ 0xedc, 0xedf },
{ 0xf00, 0xf00 },
{ 0xf40, 0xf47 },
{ 0xf49, 0xf6c },
- { 0xf88, 0xf8c },
- { 0x1000, 0x102a },
- { 0x103f, 0x103f },
- { 0x1050, 0x1055 },
- { 0x105a, 0x105d },
- { 0x1061, 0x1061 },
- { 0x1065, 0x1066 },
- { 0x106e, 0x1070 },
- { 0x1075, 0x1081 },
- { 0x108e, 0x108e },
+ { 0xf71, 0xf83 },
+ { 0xf88, 0xf97 },
+ { 0xf99, 0xfbc },
+ { 0x1000, 0x1036 },
+ { 0x1038, 0x1038 },
+ { 0x103b, 0x103f },
+ { 0x1050, 0x108f },
+ { 0x109a, 0x109d },
{ 0x10a0, 0x10c5 },
{ 0x10c7, 0x10c7 },
{ 0x10cd, 0x10cd },
@@ -2959,37 +3103,44 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x166f, 0x167f },
{ 0x1681, 0x169a },
{ 0x16a0, 0x16ea },
- { 0x16f1, 0x16f8 },
- { 0x1700, 0x1711 },
- { 0x171f, 0x1731 },
- { 0x1740, 0x1751 },
+ { 0x16ee, 0x16f8 },
+ { 0x1700, 0x1713 },
+ { 0x171f, 0x1733 },
+ { 0x1740, 0x1753 },
{ 0x1760, 0x176c },
{ 0x176e, 0x1770 },
+ { 0x1772, 0x1773 },
{ 0x1780, 0x17b3 },
+ { 0x17b6, 0x17c8 },
{ 0x17d7, 0x17d7 },
{ 0x17dc, 0x17dc },
{ 0x1820, 0x1878 },
- { 0x1880, 0x1884 },
- { 0x1887, 0x18a8 },
- { 0x18aa, 0x18aa },
+ { 0x1880, 0x18aa },
{ 0x18b0, 0x18f5 },
{ 0x1900, 0x191e },
+ { 0x1920, 0x192b },
+ { 0x1930, 0x1938 },
{ 0x1950, 0x196d },
{ 0x1970, 0x1974 },
{ 0x1980, 0x19ab },
{ 0x19b0, 0x19c9 },
- { 0x1a00, 0x1a16 },
- { 0x1a20, 0x1a54 },
+ { 0x1a00, 0x1a1b },
+ { 0x1a20, 0x1a5e },
+ { 0x1a61, 0x1a74 },
{ 0x1aa7, 0x1aa7 },
- { 0x1b05, 0x1b33 },
+ { 0x1abf, 0x1ac0 },
+ { 0x1acc, 0x1ace },
+ { 0x1b00, 0x1b33 },
+ { 0x1b35, 0x1b43 },
{ 0x1b45, 0x1b4c },
- { 0x1b83, 0x1ba0 },
- { 0x1bae, 0x1baf },
+ { 0x1b80, 0x1ba9 },
+ { 0x1bac, 0x1baf },
{ 0x1bba, 0x1be5 },
- { 0x1c00, 0x1c23 },
+ { 0x1be7, 0x1bf1 },
+ { 0x1c00, 0x1c36 },
{ 0x1c4d, 0x1c4f },
{ 0x1c5a, 0x1c7d },
- { 0x1c80, 0x1c88 },
+ { 0x1c80, 0x1c8a },
{ 0x1c90, 0x1cba },
{ 0x1cbd, 0x1cbf },
{ 0x1ce9, 0x1cec },
@@ -2997,6 +3148,7 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x1cf5, 0x1cf6 },
{ 0x1cfa, 0x1cfa },
{ 0x1d00, 0x1dbf },
+ { 0x1dd3, 0x1df4 },
{ 0x1e00, 0x1f15 },
{ 0x1f18, 0x1f1d },
{ 0x1f20, 0x1f45 },
@@ -3032,7 +3184,8 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x213c, 0x213f },
{ 0x2145, 0x2149 },
{ 0x214e, 0x214e },
- { 0x2183, 0x2184 },
+ { 0x2160, 0x2188 },
+ { 0x24b6, 0x24e9 },
{ 0x2c00, 0x2ce4 },
{ 0x2ceb, 0x2cee },
{ 0x2cf2, 0x2cf3 },
@@ -3050,10 +3203,12 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x2dc8, 0x2dce },
{ 0x2dd0, 0x2dd6 },
{ 0x2dd8, 0x2dde },
+ { 0x2de0, 0x2dff },
{ 0x2e2f, 0x2e2f },
- { 0x3005, 0x3006 },
+ { 0x3005, 0x3007 },
+ { 0x3021, 0x3029 },
{ 0x3031, 0x3035 },
- { 0x303b, 0x303c },
+ { 0x3038, 0x303c },
{ 0x3041, 0x3096 },
{ 0x309d, 0x309f },
{ 0x30a1, 0x30fa },
@@ -3069,45 +3224,39 @@ inline constexpr CharRange unicode_letter[] = {
{ 0xa610, 0xa61f },
{ 0xa62a, 0xa62b },
{ 0xa640, 0xa66e },
- { 0xa67f, 0xa69d },
- { 0xa6a0, 0xa6e5 },
+ { 0xa674, 0xa67b },
+ { 0xa67f, 0xa6ef },
{ 0xa717, 0xa71f },
{ 0xa722, 0xa788 },
- { 0xa78b, 0xa7ca },
+ { 0xa78b, 0xa7cd },
{ 0xa7d0, 0xa7d1 },
{ 0xa7d3, 0xa7d3 },
- { 0xa7d5, 0xa7d9 },
- { 0xa7f2, 0xa801 },
- { 0xa803, 0xa805 },
- { 0xa807, 0xa80a },
- { 0xa80c, 0xa822 },
+ { 0xa7d5, 0xa7dc },
+ { 0xa7f2, 0xa805 },
+ { 0xa807, 0xa827 },
{ 0xa840, 0xa873 },
- { 0xa882, 0xa8b3 },
+ { 0xa880, 0xa8c3 },
+ { 0xa8c5, 0xa8c5 },
{ 0xa8f2, 0xa8f7 },
{ 0xa8fb, 0xa8fb },
- { 0xa8fd, 0xa8fe },
- { 0xa90a, 0xa925 },
- { 0xa930, 0xa946 },
+ { 0xa8fd, 0xa8ff },
+ { 0xa90a, 0xa92a },
+ { 0xa930, 0xa952 },
{ 0xa960, 0xa97c },
- { 0xa984, 0xa9b2 },
+ { 0xa980, 0xa9b2 },
+ { 0xa9b4, 0xa9bf },
{ 0xa9cf, 0xa9cf },
- { 0xa9e0, 0xa9e4 },
- { 0xa9e6, 0xa9ef },
+ { 0xa9e0, 0xa9ef },
{ 0xa9fa, 0xa9fe },
- { 0xaa00, 0xaa28 },
- { 0xaa40, 0xaa42 },
- { 0xaa44, 0xaa4b },
+ { 0xaa00, 0xaa36 },
+ { 0xaa40, 0xaa4d },
{ 0xaa60, 0xaa76 },
- { 0xaa7a, 0xaa7a },
- { 0xaa7e, 0xaaaf },
- { 0xaab1, 0xaab1 },
- { 0xaab5, 0xaab6 },
- { 0xaab9, 0xaabd },
+ { 0xaa7a, 0xaabe },
{ 0xaac0, 0xaac0 },
{ 0xaac2, 0xaac2 },
{ 0xaadb, 0xaadd },
- { 0xaae0, 0xaaea },
- { 0xaaf2, 0xaaf4 },
+ { 0xaae0, 0xaaef },
+ { 0xaaf2, 0xaaf5 },
{ 0xab01, 0xab06 },
{ 0xab09, 0xab0e },
{ 0xab11, 0xab16 },
@@ -3115,7 +3264,7 @@ inline constexpr CharRange unicode_letter[] = {
{ 0xab28, 0xab2e },
{ 0xab30, 0xab5a },
{ 0xab5c, 0xab69 },
- { 0xab70, 0xabe2 },
+ { 0xab70, 0xabea },
{ 0xac00, 0xd7a3 },
{ 0xd7b0, 0xd7c6 },
{ 0xd7cb, 0xd7fb },
@@ -3123,8 +3272,7 @@ inline constexpr CharRange unicode_letter[] = {
{ 0xfa70, 0xfad9 },
{ 0xfb00, 0xfb06 },
{ 0xfb13, 0xfb17 },
- { 0xfb1d, 0xfb1d },
- { 0xfb1f, 0xfb28 },
+ { 0xfb1d, 0xfb28 },
{ 0xfb2a, 0xfb36 },
{ 0xfb38, 0xfb3c },
{ 0xfb3e, 0xfb3e },
@@ -3151,15 +3299,16 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x1003f, 0x1004d },
{ 0x10050, 0x1005d },
{ 0x10080, 0x100fa },
+ { 0x10140, 0x10174 },
{ 0x10280, 0x1029c },
{ 0x102a0, 0x102d0 },
{ 0x10300, 0x1031f },
- { 0x1032d, 0x10340 },
- { 0x10342, 0x10349 },
- { 0x10350, 0x10375 },
+ { 0x1032d, 0x1034a },
+ { 0x10350, 0x1037a },
{ 0x10380, 0x1039d },
{ 0x103a0, 0x103c3 },
{ 0x103c8, 0x103cf },
+ { 0x103d1, 0x103d5 },
{ 0x10400, 0x1049d },
{ 0x104b0, 0x104d3 },
{ 0x104d8, 0x104fb },
@@ -3173,6 +3322,7 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x105a3, 0x105b1 },
{ 0x105b3, 0x105b9 },
{ 0x105bb, 0x105bc },
+ { 0x105c0, 0x105f3 },
{ 0x10600, 0x10736 },
{ 0x10740, 0x10755 },
{ 0x10760, 0x10767 },
@@ -3193,8 +3343,9 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x10920, 0x10939 },
{ 0x10980, 0x109b7 },
{ 0x109be, 0x109bf },
- { 0x10a00, 0x10a00 },
- { 0x10a10, 0x10a13 },
+ { 0x10a00, 0x10a03 },
+ { 0x10a05, 0x10a06 },
+ { 0x10a0c, 0x10a13 },
{ 0x10a15, 0x10a17 },
{ 0x10a19, 0x10a35 },
{ 0x10a60, 0x10a7c },
@@ -3208,104 +3359,143 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x10c00, 0x10c48 },
{ 0x10c80, 0x10cb2 },
{ 0x10cc0, 0x10cf2 },
- { 0x10d00, 0x10d23 },
+ { 0x10d00, 0x10d27 },
+ { 0x10d4a, 0x10d65 },
+ { 0x10d69, 0x10d69 },
+ { 0x10d6f, 0x10d85 },
{ 0x10e80, 0x10ea9 },
+ { 0x10eab, 0x10eac },
{ 0x10eb0, 0x10eb1 },
+ { 0x10ec2, 0x10ec4 },
+ { 0x10efc, 0x10efc },
{ 0x10f00, 0x10f1c },
{ 0x10f27, 0x10f27 },
{ 0x10f30, 0x10f45 },
{ 0x10f70, 0x10f81 },
{ 0x10fb0, 0x10fc4 },
{ 0x10fe0, 0x10ff6 },
- { 0x11003, 0x11037 },
- { 0x11071, 0x11072 },
- { 0x11075, 0x11075 },
- { 0x11083, 0x110af },
+ { 0x11000, 0x11045 },
+ { 0x11071, 0x11075 },
+ { 0x11080, 0x110b8 },
+ { 0x110c2, 0x110c2 },
{ 0x110d0, 0x110e8 },
- { 0x11103, 0x11126 },
- { 0x11144, 0x11144 },
- { 0x11147, 0x11147 },
+ { 0x11100, 0x11132 },
+ { 0x11144, 0x11147 },
{ 0x11150, 0x11172 },
{ 0x11176, 0x11176 },
- { 0x11183, 0x111b2 },
+ { 0x11180, 0x111bf },
{ 0x111c1, 0x111c4 },
+ { 0x111ce, 0x111cf },
{ 0x111da, 0x111da },
{ 0x111dc, 0x111dc },
{ 0x11200, 0x11211 },
- { 0x11213, 0x1122b },
- { 0x1123f, 0x11240 },
+ { 0x11213, 0x11234 },
+ { 0x11237, 0x11237 },
+ { 0x1123e, 0x11241 },
{ 0x11280, 0x11286 },
{ 0x11288, 0x11288 },
{ 0x1128a, 0x1128d },
{ 0x1128f, 0x1129d },
{ 0x1129f, 0x112a8 },
- { 0x112b0, 0x112de },
+ { 0x112b0, 0x112e8 },
+ { 0x11300, 0x11303 },
{ 0x11305, 0x1130c },
{ 0x1130f, 0x11310 },
{ 0x11313, 0x11328 },
{ 0x1132a, 0x11330 },
{ 0x11332, 0x11333 },
{ 0x11335, 0x11339 },
- { 0x1133d, 0x1133d },
+ { 0x1133d, 0x11344 },
+ { 0x11347, 0x11348 },
+ { 0x1134b, 0x1134c },
{ 0x11350, 0x11350 },
- { 0x1135d, 0x11361 },
- { 0x11400, 0x11434 },
+ { 0x11357, 0x11357 },
+ { 0x1135d, 0x11363 },
+ { 0x11380, 0x11389 },
+ { 0x1138b, 0x1138b },
+ { 0x1138e, 0x1138e },
+ { 0x11390, 0x113b5 },
+ { 0x113b7, 0x113c0 },
+ { 0x113c2, 0x113c2 },
+ { 0x113c5, 0x113c5 },
+ { 0x113c7, 0x113ca },
+ { 0x113cc, 0x113cd },
+ { 0x113d1, 0x113d1 },
+ { 0x113d3, 0x113d3 },
+ { 0x11400, 0x11441 },
+ { 0x11443, 0x11445 },
{ 0x11447, 0x1144a },
{ 0x1145f, 0x11461 },
- { 0x11480, 0x114af },
+ { 0x11480, 0x114c1 },
{ 0x114c4, 0x114c5 },
{ 0x114c7, 0x114c7 },
- { 0x11580, 0x115ae },
- { 0x115d8, 0x115db },
- { 0x11600, 0x1162f },
+ { 0x11580, 0x115b5 },
+ { 0x115b8, 0x115be },
+ { 0x115d8, 0x115dd },
+ { 0x11600, 0x1163e },
+ { 0x11640, 0x11640 },
{ 0x11644, 0x11644 },
- { 0x11680, 0x116aa },
+ { 0x11680, 0x116b5 },
{ 0x116b8, 0x116b8 },
{ 0x11700, 0x1171a },
+ { 0x1171d, 0x1172a },
{ 0x11740, 0x11746 },
- { 0x11800, 0x1182b },
+ { 0x11800, 0x11838 },
{ 0x118a0, 0x118df },
{ 0x118ff, 0x11906 },
{ 0x11909, 0x11909 },
{ 0x1190c, 0x11913 },
{ 0x11915, 0x11916 },
- { 0x11918, 0x1192f },
- { 0x1193f, 0x1193f },
- { 0x11941, 0x11941 },
+ { 0x11918, 0x11935 },
+ { 0x11937, 0x11938 },
+ { 0x1193b, 0x1193c },
+ { 0x1193f, 0x11942 },
{ 0x119a0, 0x119a7 },
- { 0x119aa, 0x119d0 },
+ { 0x119aa, 0x119d7 },
+ { 0x119da, 0x119df },
{ 0x119e1, 0x119e1 },
- { 0x119e3, 0x119e3 },
- { 0x11a00, 0x11a00 },
- { 0x11a0b, 0x11a32 },
- { 0x11a3a, 0x11a3a },
- { 0x11a50, 0x11a50 },
- { 0x11a5c, 0x11a89 },
+ { 0x119e3, 0x119e4 },
+ { 0x11a00, 0x11a32 },
+ { 0x11a35, 0x11a3e },
+ { 0x11a50, 0x11a97 },
{ 0x11a9d, 0x11a9d },
{ 0x11ab0, 0x11af8 },
+ { 0x11bc0, 0x11be0 },
{ 0x11c00, 0x11c08 },
- { 0x11c0a, 0x11c2e },
+ { 0x11c0a, 0x11c36 },
+ { 0x11c38, 0x11c3e },
{ 0x11c40, 0x11c40 },
{ 0x11c72, 0x11c8f },
+ { 0x11c92, 0x11ca7 },
+ { 0x11ca9, 0x11cb6 },
{ 0x11d00, 0x11d06 },
{ 0x11d08, 0x11d09 },
- { 0x11d0b, 0x11d30 },
- { 0x11d46, 0x11d46 },
+ { 0x11d0b, 0x11d36 },
+ { 0x11d3a, 0x11d3a },
+ { 0x11d3c, 0x11d3d },
+ { 0x11d3f, 0x11d41 },
+ { 0x11d43, 0x11d43 },
+ { 0x11d46, 0x11d47 },
{ 0x11d60, 0x11d65 },
{ 0x11d67, 0x11d68 },
- { 0x11d6a, 0x11d89 },
+ { 0x11d6a, 0x11d8e },
+ { 0x11d90, 0x11d91 },
+ { 0x11d93, 0x11d96 },
{ 0x11d98, 0x11d98 },
- { 0x11ee0, 0x11ef2 },
- { 0x11f02, 0x11f02 },
- { 0x11f04, 0x11f10 },
- { 0x11f12, 0x11f33 },
+ { 0x11ee0, 0x11ef6 },
+ { 0x11f00, 0x11f10 },
+ { 0x11f12, 0x11f3a },
+ { 0x11f3e, 0x11f40 },
{ 0x11fb0, 0x11fb0 },
{ 0x12000, 0x12399 },
+ { 0x12400, 0x1246e },
{ 0x12480, 0x12543 },
{ 0x12f90, 0x12ff0 },
{ 0x13000, 0x1342f },
{ 0x13441, 0x13446 },
+ { 0x13460, 0x143fa },
{ 0x14400, 0x14646 },
+ { 0x16100, 0x1612e },
{ 0x16800, 0x16a38 },
{ 0x16a40, 0x16a5e },
{ 0x16a70, 0x16abe },
@@ -3314,15 +3504,17 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x16b40, 0x16b43 },
{ 0x16b63, 0x16b77 },
{ 0x16b7d, 0x16b8f },
+ { 0x16d40, 0x16d6c },
{ 0x16e40, 0x16e7f },
{ 0x16f00, 0x16f4a },
- { 0x16f50, 0x16f50 },
- { 0x16f93, 0x16f9f },
+ { 0x16f4f, 0x16f87 },
+ { 0x16f8f, 0x16f9f },
{ 0x16fe0, 0x16fe1 },
{ 0x16fe3, 0x16fe3 },
+ { 0x16ff0, 0x16ff1 },
{ 0x17000, 0x187f7 },
{ 0x18800, 0x18cd5 },
- { 0x18d00, 0x18d08 },
+ { 0x18cff, 0x18d08 },
{ 0x1aff0, 0x1aff3 },
{ 0x1aff5, 0x1affb },
{ 0x1affd, 0x1affe },
@@ -3336,6 +3528,7 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x1bc70, 0x1bc7c },
{ 0x1bc80, 0x1bc88 },
{ 0x1bc90, 0x1bc99 },
+ { 0x1bc9e, 0x1bc9e },
{ 0x1d400, 0x1d454 },
{ 0x1d456, 0x1d49c },
{ 0x1d49e, 0x1d49f },
@@ -3368,19 +3561,28 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x1d7c4, 0x1d7cb },
{ 0x1df00, 0x1df1e },
{ 0x1df25, 0x1df2a },
+ { 0x1e000, 0x1e006 },
+ { 0x1e008, 0x1e018 },
+ { 0x1e01b, 0x1e021 },
+ { 0x1e023, 0x1e024 },
+ { 0x1e026, 0x1e02a },
{ 0x1e030, 0x1e06d },
+ { 0x1e08f, 0x1e08f },
{ 0x1e100, 0x1e12c },
{ 0x1e137, 0x1e13d },
{ 0x1e14e, 0x1e14e },
{ 0x1e290, 0x1e2ad },
{ 0x1e2c0, 0x1e2eb },
{ 0x1e4d0, 0x1e4eb },
+ { 0x1e5d0, 0x1e5ed },
+ { 0x1e5f0, 0x1e5f0 },
{ 0x1e7e0, 0x1e7e6 },
{ 0x1e7e8, 0x1e7eb },
{ 0x1e7ed, 0x1e7ee },
{ 0x1e7f0, 0x1e7fe },
{ 0x1e800, 0x1e8c4 },
{ 0x1e900, 0x1e943 },
+ { 0x1e947, 0x1e947 },
{ 0x1e94b, 0x1e94b },
{ 0x1ee00, 0x1ee03 },
{ 0x1ee05, 0x1ee1f },
@@ -3415,6 +3617,9 @@ inline constexpr CharRange unicode_letter[] = {
{ 0x1eea1, 0x1eea3 },
{ 0x1eea5, 0x1eea9 },
{ 0x1eeab, 0x1eebb },
+ { 0x1f130, 0x1f149 },
+ { 0x1f150, 0x1f169 },
+ { 0x1f170, 0x1f189 },
{ 0x20000, 0x2a6df },
{ 0x2a700, 0x2b739 },
{ 0x2b740, 0x2b81d },
diff --git a/core/string/char_utils.h b/core/string/char_utils.h
index 5753aa78f4..c5ef140b72 100644
--- a/core/string/char_utils.h
+++ b/core/string/char_utils.h
@@ -40,97 +40,97 @@
#define BSEARCH_CHAR_RANGE(m_array) \
int low = 0; \
int high = sizeof(m_array) / sizeof(m_array[0]) - 1; \
- int middle; \
+ int middle = (low + high) / 2; \
\
while (low <= high) { \
- middle = (low + high) / 2; \
- \
- if (c < m_array[middle].start) { \
+ if (p_char < m_array[middle].start) { \
high = middle - 1; \
- } else if (c > m_array[middle].end) { \
+ } else if (p_char > m_array[middle].end) { \
low = middle + 1; \
} else { \
return true; \
} \
+ \
+ middle = (low + high) / 2; \
} \
\
return false
-static _FORCE_INLINE_ bool is_unicode_identifier_start(char32_t c) {
+constexpr bool is_unicode_identifier_start(char32_t p_char) {
BSEARCH_CHAR_RANGE(xid_start);
}
-static _FORCE_INLINE_ bool is_unicode_identifier_continue(char32_t c) {
+constexpr bool is_unicode_identifier_continue(char32_t p_char) {
BSEARCH_CHAR_RANGE(xid_continue);
}
-static _FORCE_INLINE_ bool is_unicode_upper_case(char32_t c) {
+constexpr bool is_unicode_upper_case(char32_t p_char) {
BSEARCH_CHAR_RANGE(uppercase_letter);
}
-static _FORCE_INLINE_ bool is_unicode_lower_case(char32_t c) {
+constexpr bool is_unicode_lower_case(char32_t p_char) {
BSEARCH_CHAR_RANGE(lowercase_letter);
}
-static _FORCE_INLINE_ bool is_unicode_letter(char32_t c) {
+constexpr bool is_unicode_letter(char32_t p_char) {
BSEARCH_CHAR_RANGE(unicode_letter);
}
#undef BSEARCH_CHAR_RANGE
-static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) {
- return (c >= 'A' && c <= 'Z');
+constexpr bool is_ascii_upper_case(char32_t p_char) {
+ return (p_char >= 'A' && p_char <= 'Z');
}
-static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) {
- return (c >= 'a' && c <= 'z');
+constexpr bool is_ascii_lower_case(char32_t p_char) {
+ return (p_char >= 'a' && p_char <= 'z');
}
-static _FORCE_INLINE_ bool is_digit(char32_t c) {
- return (c >= '0' && c <= '9');
+constexpr bool is_digit(char32_t p_char) {
+ return (p_char >= '0' && p_char <= '9');
}
-static _FORCE_INLINE_ bool is_hex_digit(char32_t c) {
- return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+constexpr bool is_hex_digit(char32_t p_char) {
+ return (is_digit(p_char) || (p_char >= 'a' && p_char <= 'f') || (p_char >= 'A' && p_char <= 'F'));
}
-static _FORCE_INLINE_ bool is_binary_digit(char32_t c) {
- return (c == '0' || c == '1');
+constexpr bool is_binary_digit(char32_t p_char) {
+ return (p_char == '0' || p_char == '1');
}
-static _FORCE_INLINE_ bool is_ascii_alphabet_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+constexpr bool is_ascii_alphabet_char(char32_t p_char) {
+ return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z');
}
-static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
+constexpr bool is_ascii_alphanumeric_char(char32_t p_char) {
+ return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z') || (p_char >= '0' && p_char <= '9');
}
-static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+constexpr bool is_ascii_identifier_char(char32_t p_char) {
+ return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z') || (p_char >= '0' && p_char <= '9') || p_char == '_';
}
-static _FORCE_INLINE_ bool is_symbol(char32_t c) {
- return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
+constexpr bool is_symbol(char32_t p_char) {
+ return p_char != '_' && ((p_char >= '!' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '`') || (p_char >= '{' && p_char <= '~') || p_char == '\t' || p_char == ' ');
}
-static _FORCE_INLINE_ bool is_control(char32_t p_char) {
+constexpr bool is_control(char32_t p_char) {
return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f);
}
-static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
+constexpr bool is_whitespace(char32_t p_char) {
return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
}
-static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
+constexpr bool is_linebreak(char32_t p_char) {
return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
}
-static _FORCE_INLINE_ bool is_punct(char32_t p_char) {
+constexpr bool is_punct(char32_t p_char) {
return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f);
}
-static _FORCE_INLINE_ bool is_underscore(char32_t p_char) {
+constexpr bool is_underscore(char32_t p_char) {
return (p_char == '_');
}
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 127676a9f9..c6ce1ffbcb 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -1852,6 +1852,8 @@ String String::num(double p_num, int p_decimals) {
}
String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
+ ERR_FAIL_COND_V_MSG(base < 2 || base > 36, "", "Cannot convert to base " + itos(base) + ", since the value is " + (base < 2 ? "less than 2." : "greater than 36."));
+
bool sign = p_num < 0;
int64_t n = p_num;
@@ -1890,6 +1892,8 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
}
String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) {
+ ERR_FAIL_COND_V_MSG(base < 2 || base > 36, "", "Cannot convert to base " + itos(base) + ", since the value is " + (base < 2 ? "less than 2." : "greater than 36."));
+
uint64_t n = p_num;
int chars = 0;
diff --git a/core/templates/a_hash_map.cpp b/core/templates/a_hash_map.cpp
new file mode 100644
index 0000000000..ebb0373924
--- /dev/null
+++ b/core/templates/a_hash_map.cpp
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* a_hash_map.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* 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. */
+/**************************************************************************/
+
+#include "a_hash_map.h"
+#include "core/variant/variant.h"
+
+// Explicit instantiation.
+template class AHashMap<int, int>;
+template class AHashMap<String, int>;
+template class AHashMap<StringName, StringName>;
+template class AHashMap<StringName, Variant>;
+template class AHashMap<StringName, int>;
diff --git a/core/templates/a_hash_map.h b/core/templates/a_hash_map.h
new file mode 100644
index 0000000000..d390b3af0d
--- /dev/null
+++ b/core/templates/a_hash_map.h
@@ -0,0 +1,734 @@
+/**************************************************************************/
+/* a_hash_map.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* 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 A_HASH_MAP_H
+#define A_HASH_MAP_H
+
+#include "core/templates/hash_map.h"
+
+struct HashMapData {
+ union {
+ struct
+ {
+ uint32_t hash;
+ uint32_t hash_to_key;
+ };
+ uint64_t data;
+ };
+};
+
+static_assert(sizeof(HashMapData) == 8);
+
+/**
+ * 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
+ * allows you to access array elements by their index by using `get_by_index` method.
+ * Example:
+ * ```
+ * AHashMap<int, Object *> map;
+ *
+ * int get_object_id_by_number(int p_number) {
+ * int id = map.get_index(p_number);
+ * return id;
+ * }
+ *
+ * Object *get_object_by_id(int p_id) {
+ * map.get_by_index(p_id).value;
+ * }
+ * ```
+ * Still, don`t erase the elements because ID can break.
+ *
+ * When an element erase, its place is taken by the element from the end.
+ *
+ * <-------------
+ * | |
+ * 6 8 X 9 32 -1 5 -10 7 X X X
+ * 6 8 7 9 32 -1 5 -10 X X X X
+ *
+ *
+ * Use RBMap if you need to iterate over sorted elements.
+ *
+ * Use HashMap if:
+ * - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime.
+ * - You need to preserve the insertion order when using erase.
+ *
+ * It is recommended to use `HashMap` if `KeyValue` size is very large.
+ */
+template <typename TKey, typename TValue,
+ typename Hasher = HashMapHasherDefault,
+ typename Comparator = HashMapComparatorDefault<TKey>>
+class AHashMap {
+public:
+ // Must be a power of two.
+ static constexpr uint32_t INITIAL_CAPACITY = 16;
+ static constexpr uint32_t EMPTY_HASH = 0;
+ static_assert(EMPTY_HASH == 0, "EMPTY_HASH must always be 0 for the memcpy() optimization.");
+
+private:
+ typedef KeyValue<TKey, TValue> MapKeyValue;
+ MapKeyValue *elements = nullptr;
+ HashMapData *map_data = nullptr;
+
+ // Due to optimization, this is `capacity - 1`. Use + 1 to get normal capacity.
+ uint32_t capacity = 0;
+ uint32_t num_elements = 0;
+
+ uint32_t _hash(const TKey &p_key) const {
+ uint32_t hash = Hasher::hash(p_key);
+
+ if (unlikely(hash == EMPTY_HASH)) {
+ hash = EMPTY_HASH + 1;
+ }
+
+ return hash;
+ }
+
+ static _FORCE_INLINE_ uint32_t _get_resize_count(uint32_t p_capacity) {
+ return p_capacity ^ (p_capacity + 1) >> 2; // = get_capacity() * 0.75 - 1; Works only if p_capacity = 2^n - 1.
+ }
+
+ static _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_local_capacity) {
+ const uint32_t original_pos = p_hash & p_local_capacity;
+ return (p_pos - original_pos + p_local_capacity + 1) & p_local_capacity;
+ }
+
+ bool _lookup_pos(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos) const {
+ if (unlikely(elements == nullptr)) {
+ return false; // Failed lookups, no elements.
+ }
+ return _lookup_pos_with_hash(p_key, r_pos, r_hash_pos, _hash(p_key));
+ }
+
+ bool _lookup_pos_with_hash(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos, uint32_t p_hash) const {
+ if (unlikely(elements == nullptr)) {
+ return false; // Failed lookups, no elements.
+ }
+
+ uint32_t pos = p_hash & capacity;
+ HashMapData data = map_data[pos];
+ if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) {
+ r_pos = data.hash_to_key;
+ r_hash_pos = pos;
+ return true;
+ }
+
+ if (data.data == EMPTY_HASH) {
+ return false;
+ }
+
+ // A collision occurred.
+ pos = (pos + 1) & capacity;
+ uint32_t distance = 1;
+ while (true) {
+ data = map_data[pos];
+ if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) {
+ r_pos = data.hash_to_key;
+ r_hash_pos = pos;
+ return true;
+ }
+
+ if (data.data == EMPTY_HASH) {
+ return false;
+ }
+
+ if (distance > _get_probe_length(pos, data.hash, capacity)) {
+ return false;
+ }
+
+ pos = (pos + 1) & capacity;
+ distance++;
+ }
+ }
+
+ uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) {
+ uint32_t pos = p_hash & capacity;
+
+ if (map_data[pos].data == EMPTY_HASH) {
+ uint64_t data = ((uint64_t)p_index << 32) | p_hash;
+ map_data[pos].data = data;
+ return pos;
+ }
+
+ uint32_t distance = 1;
+ pos = (pos + 1) & capacity;
+ HashMapData c_data;
+ c_data.hash = p_hash;
+ c_data.hash_to_key = p_index;
+
+ while (true) {
+ if (map_data[pos].data == EMPTY_HASH) {
+#ifdef DEV_ENABLED
+ if (unlikely(distance > 12)) {
+ WARN_PRINT("Excessive collision count (" +
+ itos(distance) + "), is the right hash function being used?");
+ }
+#endif
+ map_data[pos] = c_data;
+ return pos;
+ }
+
+ // Not an empty slot, let's check the probing length of the existing one.
+ uint32_t existing_probe_len = _get_probe_length(pos, map_data[pos].hash, capacity);
+ if (existing_probe_len < distance) {
+ SWAP(c_data, map_data[pos]);
+ distance = existing_probe_len;
+ }
+
+ pos = (pos + 1) & capacity;
+ distance++;
+ }
+ }
+
+ void _resize_and_rehash(uint32_t p_new_capacity) {
+ uint32_t real_old_capacity = capacity + 1;
+ // Capacity can't be 0 and must be 2^n - 1.
+ capacity = MAX(4u, p_new_capacity);
+ uint32_t real_capacity = next_power_of_2(capacity);
+ capacity = real_capacity - 1;
+
+ HashMapData *old_map_data = map_data;
+
+ map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
+ elements = reinterpret_cast<MapKeyValue *>(Memory::realloc_static(elements, sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
+
+ memset(map_data, EMPTY_HASH, real_capacity * sizeof(HashMapData));
+
+ if (num_elements != 0) {
+ for (uint32_t i = 0; i < real_old_capacity; i++) {
+ HashMapData data = old_map_data[i];
+ if (data.data != EMPTY_HASH) {
+ _insert_with_hash(data.hash, data.hash_to_key);
+ }
+ }
+ }
+
+ Memory::free_static(old_map_data);
+ }
+
+ int32_t _insert_element(const TKey &p_key, const TValue &p_value, uint32_t p_hash) {
+ if (unlikely(elements == nullptr)) {
+ // Allocate on demand to save memory.
+
+ uint32_t real_capacity = capacity + 1;
+ map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
+ elements = reinterpret_cast<MapKeyValue *>(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
+
+ memset(map_data, EMPTY_HASH, real_capacity * sizeof(HashMapData));
+ }
+
+ if (unlikely(num_elements > _get_resize_count(capacity))) {
+ _resize_and_rehash(capacity * 2);
+ }
+
+ memnew_placement(&elements[num_elements], MapKeyValue(p_key, p_value));
+
+ _insert_with_hash(p_hash, num_elements);
+ num_elements++;
+ return num_elements - 1;
+ }
+
+ void _init_from(const AHashMap &p_other) {
+ capacity = p_other.capacity;
+ uint32_t real_capacity = capacity + 1;
+ num_elements = p_other.num_elements;
+
+ if (p_other.num_elements == 0) {
+ return;
+ }
+
+ map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
+ elements = reinterpret_cast<MapKeyValue *>(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
+
+ if constexpr (std::is_trivially_copyable_v<TKey> && std::is_trivially_copyable_v<TValue>) {
+ void *destination = elements;
+ const void *source = p_other.elements;
+ memcpy(destination, source, sizeof(MapKeyValue) * num_elements);
+ } else {
+ for (uint32_t i = 0; i < num_elements; i++) {
+ memnew_placement(&elements[i], MapKeyValue(p_other.elements[i]));
+ }
+ }
+
+ memcpy(map_data, p_other.map_data, sizeof(HashMapData) * real_capacity);
+ }
+
+public:
+ /* Standard Godot Container API */
+
+ _FORCE_INLINE_ uint32_t get_capacity() const { return capacity + 1; }
+ _FORCE_INLINE_ uint32_t size() const { return num_elements; }
+
+ _FORCE_INLINE_ bool is_empty() const {
+ return num_elements == 0;
+ }
+
+ void clear() {
+ if (elements == nullptr || num_elements == 0) {
+ return;
+ }
+
+ memset(map_data, EMPTY_HASH, (capacity + 1) * sizeof(HashMapData));
+ if constexpr (!(std::is_trivially_destructible_v<TKey> && std::is_trivially_destructible_v<TValue>)) {
+ for (uint32_t i = 0; i < num_elements; i++) {
+ elements[i].key.~TKey();
+ elements[i].value.~TValue();
+ }
+ }
+
+ num_elements = 0;
+ }
+
+ TValue &get(const TKey &p_key) {
+ uint32_t pos = 0;
+ uint32_t hash_pos = 0;
+ bool exists = _lookup_pos(p_key, pos, hash_pos);
+ CRASH_COND_MSG(!exists, "AHashMap key not found.");
+ return elements[pos].value;
+ }
+
+ const TValue &get(const TKey &p_key) const {
+ uint32_t pos = 0;
+ uint32_t hash_pos = 0;
+ bool exists = _lookup_pos(p_key, pos, hash_pos);
+ CRASH_COND_MSG(!exists, "AHashMap key not found.");
+ return elements[pos].value;
+ }
+
+ const TValue *getptr(const TKey &p_key) const {
+ uint32_t pos = 0;
+ uint32_t hash_pos = 0;
+ bool exists = _lookup_pos(p_key, pos, hash_pos);
+
+ if (exists) {
+ return &elements[pos].value;
+ }
+ return nullptr;
+ }
+
+ TValue *getptr(const TKey &p_key) {
+ uint32_t pos = 0;
+ uint32_t hash_pos = 0;
+ bool exists = _lookup_pos(p_key, pos, hash_pos);
+
+ if (exists) {
+ return &elements[pos].value;
+ }
+ return nullptr;
+ }
+
+ bool has(const TKey &p_key) const {
+ uint32_t _pos = 0;
+ uint32_t h_pos = 0;
+ return _lookup_pos(p_key, _pos, h_pos);
+ }
+
+ bool erase(const TKey &p_key) {
+ uint32_t pos = 0;
+ uint32_t element_pos = 0;
+ bool exists = _lookup_pos(p_key, element_pos, pos);
+
+ if (!exists) {
+ return false;
+ }
+
+ uint32_t next_pos = (pos + 1) & capacity;
+ while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) {
+ SWAP(map_data[next_pos], map_data[pos]);
+
+ pos = next_pos;
+ next_pos = (next_pos + 1) & capacity;
+ }
+
+ map_data[pos].data = EMPTY_HASH;
+ elements[element_pos].key.~TKey();
+ elements[element_pos].value.~TValue();
+ num_elements--;
+
+ if (element_pos < num_elements) {
+ void *destination = &elements[element_pos];
+ const void *source = &elements[num_elements];
+ memcpy(destination, source, sizeof(MapKeyValue));
+ uint32_t h_pos = 0;
+ _lookup_pos(elements[num_elements].key, pos, h_pos);
+ map_data[h_pos].hash_to_key = element_pos;
+ }
+
+ return true;
+ }
+
+ // Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration.
+ // p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key.
+ bool replace_key(const TKey &p_old_key, const TKey &p_new_key) {
+ if (p_old_key == p_new_key) {
+ return true;
+ }
+ uint32_t pos = 0;
+ uint32_t element_pos = 0;
+ ERR_FAIL_COND_V(_lookup_pos(p_new_key, element_pos, pos), false);
+ ERR_FAIL_COND_V(!_lookup_pos(p_old_key, element_pos, pos), false);
+ MapKeyValue &element = elements[element_pos];
+ const_cast<TKey &>(element.key) = p_new_key;
+
+ uint32_t next_pos = (pos + 1) & capacity;
+ while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) {
+ SWAP(map_data[next_pos], map_data[pos]);
+
+ pos = next_pos;
+ next_pos = (next_pos + 1) & capacity;
+ }
+
+ map_data[pos].data = EMPTY_HASH;
+
+ uint32_t hash = _hash(p_new_key);
+ _insert_with_hash(hash, element_pos);
+
+ return true;
+ }
+
+ // Reserves space for a number of elements, useful to avoid many resizes and rehashes.
+ // If adding a known (possibly large) number of elements at once, must be larger than old capacity.
+ void reserve(uint32_t p_new_capacity) {
+ ERR_FAIL_COND_MSG(p_new_capacity < get_capacity(), "It is impossible to reserve less capacity than is currently available.");
+ if (elements == nullptr) {
+ capacity = MAX(4u, p_new_capacity);
+ capacity = next_power_of_2(capacity) - 1;
+ return; // Unallocated yet.
+ }
+ _resize_and_rehash(p_new_capacity);
+ }
+
+ /** Iterator API **/
+
+ struct ConstIterator {
+ _FORCE_INLINE_ const MapKeyValue &operator*() const {
+ return *pair;
+ }
+ _FORCE_INLINE_ const MapKeyValue *operator->() const {
+ return pair;
+ }
+ _FORCE_INLINE_ ConstIterator &operator++() {
+ pair++;
+ return *this;
+ }
+
+ _FORCE_INLINE_ ConstIterator &operator--() {
+ pair--;
+ if (pair < begin) {
+ pair = end;
+ }
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return pair == b.pair; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return pair != b.pair; }
+
+ _FORCE_INLINE_ explicit operator bool() const {
+ return pair != end;
+ }
+
+ _FORCE_INLINE_ ConstIterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) {
+ pair = p_key;
+ begin = p_begin;
+ end = p_end;
+ }
+ _FORCE_INLINE_ ConstIterator() {}
+ _FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) {
+ pair = p_it.pair;
+ begin = p_it.begin;
+ end = p_it.end;
+ }
+ _FORCE_INLINE_ void operator=(const ConstIterator &p_it) {
+ pair = p_it.pair;
+ begin = p_it.begin;
+ end = p_it.end;
+ }
+
+ private:
+ MapKeyValue *pair = nullptr;
+ MapKeyValue *begin = nullptr;
+ MapKeyValue *end = nullptr;
+ };
+
+ struct Iterator {
+ _FORCE_INLINE_ MapKeyValue &operator*() const {
+ return *pair;
+ }
+ _FORCE_INLINE_ MapKeyValue *operator->() const {
+ return pair;
+ }
+ _FORCE_INLINE_ Iterator &operator++() {
+ pair++;
+ return *this;
+ }
+ _FORCE_INLINE_ Iterator &operator--() {
+ pair--;
+ if (pair < begin) {
+ pair = end;
+ }
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const Iterator &b) const { return pair == b.pair; }
+ _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return pair != b.pair; }
+
+ _FORCE_INLINE_ explicit operator bool() const {
+ return pair != end;
+ }
+
+ _FORCE_INLINE_ Iterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) {
+ pair = p_key;
+ begin = p_begin;
+ end = p_end;
+ }
+ _FORCE_INLINE_ Iterator() {}
+ _FORCE_INLINE_ Iterator(const Iterator &p_it) {
+ pair = p_it.pair;
+ begin = p_it.begin;
+ end = p_it.end;
+ }
+ _FORCE_INLINE_ void operator=(const Iterator &p_it) {
+ pair = p_it.pair;
+ begin = p_it.begin;
+ end = p_it.end;
+ }
+
+ operator ConstIterator() const {
+ return ConstIterator(pair, begin, end);
+ }
+
+ private:
+ MapKeyValue *pair = nullptr;
+ MapKeyValue *begin = nullptr;
+ MapKeyValue *end = nullptr;
+ };
+
+ _FORCE_INLINE_ Iterator begin() {
+ return Iterator(elements, elements, elements + num_elements);
+ }
+ _FORCE_INLINE_ Iterator end() {
+ return Iterator(elements + num_elements, elements, elements + num_elements);
+ }
+ _FORCE_INLINE_ Iterator last() {
+ if (unlikely(num_elements == 0)) {
+ return Iterator(nullptr, nullptr, nullptr);
+ }
+ return Iterator(elements + num_elements - 1, elements, elements + num_elements);
+ }
+
+ Iterator find(const TKey &p_key) {
+ uint32_t pos = 0;
+ uint32_t h_pos = 0;
+ bool exists = _lookup_pos(p_key, pos, h_pos);
+ if (!exists) {
+ return end();
+ }
+ return Iterator(elements + pos, elements, elements + num_elements);
+ }
+
+ void remove(const Iterator &p_iter) {
+ if (p_iter) {
+ erase(p_iter->key);
+ }
+ }
+
+ _FORCE_INLINE_ ConstIterator begin() const {
+ return ConstIterator(elements, elements, elements + num_elements);
+ }
+ _FORCE_INLINE_ ConstIterator end() const {
+ return ConstIterator(elements + num_elements, elements, elements + num_elements);
+ }
+ _FORCE_INLINE_ ConstIterator last() const {
+ if (unlikely(num_elements == 0)) {
+ return ConstIterator(nullptr, nullptr, nullptr);
+ }
+ return ConstIterator(elements + num_elements - 1, elements, elements + num_elements);
+ }
+
+ ConstIterator find(const TKey &p_key) const {
+ uint32_t pos = 0;
+ uint32_t h_pos = 0;
+ bool exists = _lookup_pos(p_key, pos, h_pos);
+ if (!exists) {
+ return end();
+ }
+ return ConstIterator(elements + pos, elements, elements + num_elements);
+ }
+
+ /* Indexing */
+
+ const TValue &operator[](const TKey &p_key) const {
+ uint32_t pos = 0;
+ uint32_t h_pos = 0;
+ bool exists = _lookup_pos(p_key, pos, h_pos);
+ CRASH_COND(!exists);
+ return elements[pos].value;
+ }
+
+ TValue &operator[](const TKey &p_key) {
+ uint32_t pos = 0;
+ uint32_t h_pos = 0;
+ uint32_t hash = _hash(p_key);
+ bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash);
+
+ if (exists) {
+ return elements[pos].value;
+ } else {
+ pos = _insert_element(p_key, TValue(), hash);
+ return elements[pos].value;
+ }
+ }
+
+ /* Insert */
+
+ Iterator insert(const TKey &p_key, const TValue &p_value) {
+ uint32_t pos = 0;
+ uint32_t h_pos = 0;
+ uint32_t hash = _hash(p_key);
+ bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash);
+
+ if (!exists) {
+ pos = _insert_element(p_key, p_value, hash);
+ } else {
+ elements[pos].value = p_value;
+ }
+ return Iterator(elements + pos, elements, elements + num_elements);
+ }
+
+ // Inserts an element without checking if it already exists.
+ void insert_new(const TKey &p_key, const TValue &p_value) {
+ DEV_ASSERT(!has(p_key));
+ uint32_t hash = _hash(p_key);
+ _insert_element(p_key, p_value, hash);
+ }
+
+ /* Array methods. */
+
+ // Unsafe. Changing keys and going outside the bounds of an array can lead to undefined behavior.
+ KeyValue<TKey, TValue> *get_elements_ptr() {
+ return elements;
+ }
+
+ // Returns the element index. If not found, returns -1.
+ int get_index(const TKey &p_key) {
+ uint32_t pos = 0;
+ uint32_t h_pos = 0;
+ bool exists = _lookup_pos(p_key, pos, h_pos);
+ if (!exists) {
+ return -1;
+ }
+ return pos;
+ }
+
+ KeyValue<TKey, TValue> &get_by_index(uint32_t p_index) {
+ CRASH_BAD_UNSIGNED_INDEX(p_index, num_elements);
+ return elements[p_index];
+ }
+
+ bool erase_by_index(uint32_t p_index) {
+ if (p_index >= size()) {
+ return false;
+ }
+ return erase(elements[p_index].key);
+ }
+
+ /* Constructors */
+
+ AHashMap(const AHashMap &p_other) {
+ _init_from(p_other);
+ }
+
+ AHashMap(const HashMap<TKey, TValue> &p_other) {
+ reserve(p_other.size());
+ for (const KeyValue<TKey, TValue> &E : p_other) {
+ uint32_t hash = _hash(E.key);
+ _insert_element(E.key, E.value, hash);
+ }
+ }
+
+ void operator=(const AHashMap &p_other) {
+ if (this == &p_other) {
+ return; // Ignore self assignment.
+ }
+
+ reset();
+
+ _init_from(p_other);
+ }
+
+ void operator=(const HashMap<TKey, TValue> &p_other) {
+ reset();
+ if (p_other.size() > get_capacity()) {
+ reserve(p_other.size());
+ }
+ for (const KeyValue<TKey, TValue> &E : p_other) {
+ uint32_t hash = _hash(E.key);
+ _insert_element(E.key, E.value, hash);
+ }
+ }
+
+ AHashMap(uint32_t p_initial_capacity) {
+ // Capacity can't be 0 and must be 2^n - 1.
+ capacity = MAX(4u, p_initial_capacity);
+ capacity = next_power_of_2(capacity) - 1;
+ }
+ AHashMap() :
+ capacity(INITIAL_CAPACITY - 1) {
+ }
+
+ void reset() {
+ if (elements != nullptr) {
+ if constexpr (!(std::is_trivially_destructible_v<TKey> && std::is_trivially_destructible_v<TValue>)) {
+ for (uint32_t i = 0; i < num_elements; i++) {
+ elements[i].key.~TKey();
+ elements[i].value.~TValue();
+ }
+ }
+ Memory::free_static(elements);
+ Memory::free_static(map_data);
+ elements = nullptr;
+ }
+ capacity = INITIAL_CAPACITY - 1;
+ num_elements = 0;
+ }
+
+ ~AHashMap() {
+ reset();
+ }
+};
+
+extern template class AHashMap<int, int>;
+extern template class AHashMap<String, int>;
+extern template class AHashMap<StringName, StringName>;
+extern template class AHashMap<StringName, Variant>;
+extern template class AHashMap<StringName, int>;
+
+#endif // A_HASH_MAP_H
diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h
index a640f18a65..fbe3f41fa7 100644
--- a/core/templates/hashfuncs.h
+++ b/core/templates/hashfuncs.h
@@ -395,6 +395,13 @@ struct HashMapHasherDefault {
}
};
+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; }
+};
+
// TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed
template <typename T>
struct HashableHasher {
diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp
index a44c553d06..5512551771 100644
--- a/core/variant/variant_op.cpp
+++ b/core/variant/variant_op.cpp
@@ -982,6 +982,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorInDictionaryHas<Color>>(Variant::OP_IN, Variant::COLOR, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<NodePath>>(Variant::OP_IN, Variant::NODE_PATH, Variant::DICTIONARY);
+ register_op<OperatorEvaluatorInDictionaryHas<::RID>>(Variant::OP_IN, Variant::RID, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHasObject>(Variant::OP_IN, Variant::OBJECT, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<Callable>>(Variant::OP_IN, Variant::CALLABLE, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<Signal>>(Variant::OP_IN, Variant::SIGNAL, Variant::DICTIONARY);
@@ -1023,6 +1024,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorInArrayFind<Color, Array>>(Variant::OP_IN, Variant::COLOR, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<StringName, Array>>(Variant::OP_IN, Variant::STRING_NAME, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<NodePath, Array>>(Variant::OP_IN, Variant::NODE_PATH, Variant::ARRAY);
+ register_op<OperatorEvaluatorInArrayFind<::RID, Array>>(Variant::OP_IN, Variant::RID, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFindObject>(Variant::OP_IN, Variant::OBJECT, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<Callable, Array>>(Variant::OP_IN, Variant::CALLABLE, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<Signal, Array>>(Variant::OP_IN, Variant::SIGNAL, Variant::ARRAY);
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 6830c632cf..4a20736164 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -110,6 +110,12 @@
Returns the volume of the bus at index [param bus_idx] in dB.
</description>
</method>
+ <method name="get_driver_name" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the name of the current audio driver. The default usually depends on the operating system, but may be overridden via the [code]--audio-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. [code]--headless[/code] also automatically sets the audio driver to [code]Dummy[/code]. See also [member ProjectSettings.audio/driver/driver].
+ </description>
+ </method>
<method name="get_input_device_list">
<return type="PackedStringArray" />
<description>
diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml
index 23d6bc3851..eb65765312 100644
--- a/doc/classes/BackBufferCopy.xml
+++ b/doc/classes/BackBufferCopy.xml
@@ -8,6 +8,7 @@
[b]Note:[/b] Since this node inherits from [Node2D] (and not [Control]), anchors and margins won't apply to child [Control]-derived nodes. This can be problematic when resizing the window. To avoid this, add [Control]-derived nodes as [i]siblings[/i] to the [BackBufferCopy] node instead of adding them as children.
</description>
<tutorials>
+ <link title="Screen-reading shaders">$DOCS_URL/tutorials/shaders/screen-reading_shaders.html</link>
</tutorials>
<members>
<member name="copy_mode" type="int" setter="set_copy_mode" getter="get_copy_mode" enum="BackBufferCopy.CopyMode" default="1">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 6902551461..7e26e2913f 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -65,7 +65,7 @@
[csharp]
public override bool _CanDropData(Vector2 atPosition, Variant data)
{
- return data.VariantType == Variant.Type.Dictionary &amp;&amp; dict.AsGodotDictionary().ContainsKey("color");
+ return data.VariantType == Variant.Type.Dictionary &amp;&amp; data.AsGodotDictionary().ContainsKey("color");
}
public override void _DropData(Vector2 atPosition, Variant data)
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 44f5022a88..02e5ca0333 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -2152,12 +2152,14 @@
<constant name="DISPLAY_HANDLE" value="0" enum="HandleType">
Display handle:
- Linux (X11): [code]X11::Display*[/code] for the display.
+ - Linux (Wayland): [code]wl_display[/code] for the display.
- Android: [code]EGLDisplay[/code] for the display.
</constant>
<constant name="WINDOW_HANDLE" value="1" enum="HandleType">
Window handle:
- Windows: [code]HWND[/code] for the window.
- Linux (X11): [code]X11::Window*[/code] for the window.
+ - Linux (Wayland): [code]wl_surface[/code] for the window.
- macOS: [code]NSWindow*[/code] for the window.
- iOS: [code]UIViewController*[/code] for the view controller.
- Android: [code]jObject[/code] for the activity.
@@ -2172,9 +2174,20 @@
OpenGL context (only with the GL Compatibility renderer):
- Windows: [code]HGLRC[/code] for the window (native GL), or [code]EGLContext[/code] for the window (ANGLE).
- Linux (X11): [code]GLXContext*[/code] for the window.
+ - Linux (Wayland): [code]EGLContext[/code] for the window.
- macOS: [code]NSOpenGLContext*[/code] for the window (native GL), or [code]EGLContext[/code] for the window (ANGLE).
- Android: [code]EGLContext[/code] for the window.
</constant>
+ <constant name="EGL_DISPLAY" value="4" enum="HandleType">
+ - Windows: [code]EGLDisplay[/code] for the window (ANGLE).
+ - macOS: [code]EGLDisplay[/code] for the window (ANGLE).
+ - Linux (Wayland): [code]EGLDisplay[/code] for the window.
+ </constant>
+ <constant name="EGL_CONFIG" value="5" enum="HandleType">
+ - Windows: [code]EGLConfig[/code] for the window (ANGLE).
+ - macOS: [code]EGLConfig[/code] for the window (ANGLE).
+ - Linux (Wayland): [code]EGLConfig[/code] for the window.
+ </constant>
<constant name="TTS_UTTERANCE_STARTED" value="0" enum="TTSUtteranceEvent">
Utterance has begun to be spoken.
</constant>
diff --git a/doc/classes/EditorDebuggerPlugin.xml b/doc/classes/EditorDebuggerPlugin.xml
index a519e43bc6..d513a4fce0 100644
--- a/doc/classes/EditorDebuggerPlugin.xml
+++ b/doc/classes/EditorDebuggerPlugin.xml
@@ -15,18 +15,20 @@
class ExampleEditorDebugger extends EditorDebuggerPlugin:
- func _has_capture(prefix):
- # Return true if you wish to handle message with this prefix.
- return prefix == "my_plugin"
+ func _has_capture(capture):
+ # Return true if you wish to handle messages with the prefix "my_plugin:".
+ return capture == "my_plugin"
func _capture(message, data, session_id):
if message == "my_plugin:ping":
get_session(session_id).send_message("my_plugin:echo", data)
+ return true
+ return false
func _setup_session(session_id):
# Add a new tab in the debugger session UI containing a label.
var label = Label.new()
- label.name = "Example plugin"
+ label.name = "Example plugin" # Will be used as the tab title.
label.text = "Example plugin"
var session = get_session(session_id)
# Listens to the session started and stopped signals.
@@ -43,6 +45,24 @@
remove_debugger_plugin(debugger)
[/gdscript]
[/codeblocks]
+ To connect on the running game side, use the [EngineDebugger] singleton:
+ [codeblocks]
+ [gdscript]
+ extends Node
+
+ func _ready():
+ EngineDebugger.register_message_capture("my_plugin", _capture)
+ EngineDebugger.send_message("my_plugin:ping", ["test"])
+
+ func _capture(message, data):
+ # Note that the "my_plugin:" prefix is not used here.
+ if message == "echo":
+ prints("Echo received:", data)
+ return true
+ return false
+ [/gdscript]
+ [/codeblocks]
+ [b]Note:[/b] While the game is running, [method @GlobalScope.print] and similar functions [i]called in the editor[/i] do not print anything, the Output Log prints only game messages.
</description>
<tutorials>
</tutorials>
@@ -68,7 +88,7 @@
<param index="1" name="data" type="Array" />
<param index="2" name="session_id" type="int" />
<description>
- Override this method to process incoming messages. The [param session_id] is the ID of the [EditorDebuggerSession] that received the message (which you can retrieve via [method get_session]).
+ Override this method to process incoming messages. The [param session_id] is the ID of the [EditorDebuggerSession] that received the [param message]. Use [method get_session] to retrieve the session. This method should return [code]true[/code] if the message is recognized.
</description>
</method>
<method name="_goto_script_line" qualifiers="virtual">
@@ -90,7 +110,7 @@
<return type="void" />
<param index="0" name="session_id" type="int" />
<description>
- Override this method to be notified whenever a new [EditorDebuggerSession] is created (the session may be inactive during this stage).
+ Override this method to be notified whenever a new [EditorDebuggerSession] is created. Note that the session may be inactive during this stage.
</description>
</method>
<method name="get_session">
diff --git a/doc/classes/EditorDebuggerSession.xml b/doc/classes/EditorDebuggerSession.xml
index b4e754cc7e..f5fa3e34d5 100644
--- a/doc/classes/EditorDebuggerSession.xml
+++ b/doc/classes/EditorDebuggerSession.xml
@@ -14,7 +14,7 @@
<return type="void" />
<param index="0" name="control" type="Control" />
<description>
- Adds the given [param control] to the debug session UI in the debugger bottom panel.
+ Adds the given [param control] to the debug session UI in the debugger bottom panel. The [param control]'s node name will be used as the tab title.
</description>
</method>
<method name="is_active">
diff --git a/doc/classes/EngineDebugger.xml b/doc/classes/EngineDebugger.xml
index bcc1ac5299..3540f098ba 100644
--- a/doc/classes/EngineDebugger.xml
+++ b/doc/classes/EngineDebugger.xml
@@ -113,7 +113,8 @@
<param index="1" name="callable" type="Callable" />
<description>
Registers a message capture with given [param name]. If [param name] is "my_message" then messages starting with "my_message:" will be called with the given callable.
- Callable must accept a message string and a data array as argument. If the message and data are valid then callable must return [code]true[/code] otherwise [code]false[/code].
+ The callable must accept a message string and a data array as argument. The callable should return [code]true[/code] if the message is recognized.
+ [b]Note:[/b] The callable will receive the message with the prefix stripped, unlike [method EditorDebuggerPlugin._capture]. See the [EditorDebuggerPlugin] description for an example.
</description>
</method>
<method name="register_profiler">
diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml
index 2dd76ad989..71e6cf93ae 100644
--- a/doc/classes/Geometry2D.xml
+++ b/doc/classes/Geometry2D.xml
@@ -9,6 +9,20 @@
<tutorials>
</tutorials>
<methods>
+ <method name="bresenham_line">
+ <return type="Vector2i[]" />
+ <param index="0" name="from" type="Vector2i" />
+ <param index="1" name="to" type="Vector2i" />
+ <description>
+ Returns the [url=https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm]Bresenham line[/url] between the [param from] and [param to] points. A Bresenham line is a series of pixels that draws a line and is always 1-pixel thick on every row and column of the drawing (never more, never less).
+ Example code to draw a line between two [Marker2D] nodes using a series of [method CanvasItem.draw_rect] calls:
+ [codeblock]
+ func _draw():
+ for pixel in Geometry2D.bresenham_line($MarkerA.position, $MarkerB.position):
+ draw_rect(Rect2(pixel, Vector2.ONE), Color.WHITE)
+ [/codeblock]
+ </description>
+ </method>
<method name="clip_polygons">
<return type="PackedVector2Array[]" />
<param index="0" name="polygon_a" type="PackedVector2Array" />
diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml
index 6efa675a71..a4adf4d1b1 100644
--- a/doc/classes/HTTPRequest.xml
+++ b/doc/classes/HTTPRequest.xml
@@ -275,6 +275,7 @@
Request successful.
</constant>
<constant name="RESULT_CHUNKED_BODY_SIZE_MISMATCH" value="1" enum="Result">
+ Request failed due to a mismatch between the expected and actual chunked body size during transfer. Possible causes include network errors, server misconfiguration, or issues with chunked encoding.
</constant>
<constant name="RESULT_CANT_CONNECT" value="2" enum="Result">
Request failed while connecting.
@@ -295,6 +296,7 @@
Request exceeded its maximum size limit, see [member body_size_limit].
</constant>
<constant name="RESULT_BODY_DECOMPRESS_FAILED" value="8" enum="Result">
+ Request failed due to an error while decompressing the response body. Possible causes include unsupported or incorrect compression format, corrupted data, or incomplete transfer.
</constant>
<constant name="RESULT_REQUEST_FAILED" value="9" enum="Result">
Request failed (currently unused).
diff --git a/doc/classes/InputEventMouseMotion.xml b/doc/classes/InputEventMouseMotion.xml
index 08edd84bd4..30fb33eddd 100644
--- a/doc/classes/InputEventMouseMotion.xml
+++ b/doc/classes/InputEventMouseMotion.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
Stores information about a mouse or a pen motion. This includes relative position, absolute position, and velocity. See [method Node._input].
- [b]Note:[/b] By default, this event is only emitted once per frame rendered at most. If you need more precise input reporting, set [member Input.use_accumulated_input] to [code]false[/code] to make events emitted as often as possible. If you use InputEventMouseMotion to draw lines, consider implementing [url=https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm]Bresenham's line algorithm[/url] as well to avoid visible gaps in lines if the user is moving the mouse quickly.
+ [b]Note:[/b] By default, this event is only emitted once per frame rendered at most. If you need more precise input reporting, set [member Input.use_accumulated_input] to [code]false[/code] to make events emitted as often as possible. If you use InputEventMouseMotion to draw lines, consider using [method Geometry2D.bresenham_line] as well to avoid visible gaps in lines if the user is moving the mouse quickly.
[b]Note:[/b] This event may be emitted even when the mouse hasn't moved, either by the operating system or by Redot itself. If you really need to know if the mouse has moved (e.g. to suppress displaying a tooltip), you should check that [code]relative.is_zero_approx()[/code] is [code]false[/code].
</description>
<tutorials>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index db2659f832..853958d1e5 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -10,6 +10,7 @@
[b]Overriding:[/b] Any project setting can be overridden by creating a file named [code]override.cfg[/code] in the project's root directory. This can also be used in exported projects by placing this file in the same directory as the project binary. Overriding will still take the base project settings' [url=$DOCS_URL/tutorials/export/feature_tags.html]feature tags[/url] in account. Therefore, make sure to [i]also[/i] override the setting with the desired feature tags if you want them to override base project settings on all platforms and configurations.
</description>
<tutorials>
+ <link title="Project Settings">$DOCS_URL/tutorials/editor/project_settings.html</link>
<link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/2747</link>
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/2748</link>
<link title="Operating System Testing Demo">https://godotengine.org/asset-library/asset/2789</link>
@@ -376,6 +377,7 @@
<member name="audio/driver/driver" type="String" setter="" getter="">
Specifies the audio driver to use. This setting is platform-dependent as each platform supports different audio drivers. If left empty, the default audio driver will be used.
The [code]Dummy[/code] audio driver disables all audio playback and recording, which is useful for non-game applications as it reduces CPU usage. It also prevents the engine from appearing as an application playing audio in the OS' audio mixer.
+ To query the value that is being used at run-time (which may be overridden by command-line arguments or headless mode), use [method AudioServer.get_driver_name].
[b]Note:[/b] The driver in use can be overridden at runtime via the [code]--audio-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
</member>
<member name="audio/driver/enable_input" type="bool" setter="" getter="" default="false">
diff --git a/doc/classes/ResourceImporterOBJ.xml b/doc/classes/ResourceImporterOBJ.xml
index a63dddb0e8..084638f62b 100644
--- a/doc/classes/ResourceImporterOBJ.xml
+++ b/doc/classes/ResourceImporterOBJ.xml
@@ -14,6 +14,19 @@
<member name="force_disable_mesh_compression" type="bool" setter="" getter="" default="false">
If [code]true[/code], mesh compression will not be used. Consider enabling if you notice blocky artifacts in your mesh normals or UVs, or if you have meshes that are larger than a few thousand meters in each direction.
</member>
+ <member name="generate_lightmap_uv2" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], generates UV2 on import for [LightmapGI] baking.
+ </member>
+ <member name="generate_lightmap_uv2_texel_size" type="float" setter="" getter="" default="0.2">
+ Controls the size of each texel on the baked lightmap. A smaller value results in more precise lightmaps, at the cost of larger lightmap sizes and longer bake times.
+ [b]Note:[/b] Only effective if [member generate_lightmap_uv2] is [code]true[/code].
+ </member>
+ <member name="generate_lods" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], generates lower detail variants of the mesh which will be displayed in the distance to improve rendering performance. Not all meshes benefit from LOD, especially if they are never rendered from far away. Disabling this can reduce output file size and speed up importing. See [url=$DOCS_URL/tutorials/3d/mesh_lod.html#doc-mesh-lod]Mesh level of detail (LOD)[/url] for more information.
+ </member>
+ <member name="generate_shadow_mesh" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], enables the generation of shadow meshes on import. This optimizes shadow rendering without reducing quality by welding vertices together when possible. This in turn reduces the memory bandwidth required to render shadows. Shadow mesh generation currently doesn't support using a lower detail level than the source mesh (but shadow rendering will make use of LODs when relevant).
+ </member>
<member name="generate_tangents" type="bool" setter="" getter="" default="true">
If [code]true[/code], generate vertex tangents using [url=http://www.mikktspace.com/]Mikktspace[/url] if the source mesh doesn't have tangent data. When possible, it's recommended to let the 3D modeling software generate tangents on export instead on relying on this option. Tangents are required for correct display of normal and height maps, along with any material/shader features that require tangents.
If you don't need material features that require tangents, disabling this can reduce output file size and speed up importing if the source 3D file doesn't contain tangents.
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index 1823851032..dc945395ec 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.cpp
@@ -252,7 +252,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon,
}
void AudioDriverCoreAudio::start() {
- if (!active) {
+ if (!active && audio_unit != nullptr) {
OSStatus result = AudioOutputUnitStart(audio_unit);
if (result != noErr) {
ERR_PRINT("AudioOutputUnitStart failed, code: " + itos(result));
diff --git a/drivers/egl/egl_manager.cpp b/drivers/egl/egl_manager.cpp
index 7dd01e6d55..c53628e9af 100644
--- a/drivers/egl/egl_manager.cpp
+++ b/drivers/egl/egl_manager.cpp
@@ -416,6 +416,30 @@ EGLContext EGLManager::get_context(DisplayServer::WindowID p_window_id) {
return display.egl_context;
}
+EGLDisplay EGLManager::get_display(DisplayServer::WindowID p_window_id) {
+ GLWindow &glwindow = windows[p_window_id];
+
+ if (!glwindow.initialized) {
+ return EGL_NO_CONTEXT;
+ }
+
+ GLDisplay &display = displays[glwindow.gldisplay_id];
+
+ return display.egl_display;
+}
+
+EGLConfig EGLManager::get_config(DisplayServer::WindowID p_window_id) {
+ GLWindow &glwindow = windows[p_window_id];
+
+ if (!glwindow.initialized) {
+ return nullptr;
+ }
+
+ GLDisplay &display = displays[glwindow.gldisplay_id];
+
+ return display.egl_config;
+}
+
Error EGLManager::initialize(void *p_native_display) {
#if defined(GLAD_ENABLED) && !defined(EGL_STATIC)
// Loading EGL with a new display gets us just the bare minimum API. We'll then
diff --git a/drivers/egl/egl_manager.h b/drivers/egl/egl_manager.h
index 386fb1daab..4f6523b7e2 100644
--- a/drivers/egl/egl_manager.h
+++ b/drivers/egl/egl_manager.h
@@ -115,6 +115,8 @@ public:
bool is_using_vsync() const;
EGLContext get_context(DisplayServer::WindowID p_window_id);
+ EGLDisplay get_display(DisplayServer::WindowID p_window_id);
+ EGLConfig get_config(DisplayServer::WindowID p_window_id);
Error initialize(void *p_native_display = nullptr);
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 89ac1fa8be..0a3d08a18b 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1239,6 +1239,8 @@ MaterialStorage::MaterialStorage() {
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
+ actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
+ actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
@@ -1278,8 +1280,6 @@ MaterialStorage::MaterialStorage() {
actions.renames["CUSTOM1"] = "custom1_attrib";
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
- actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
- actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz";
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 3c914f0cb1..c05bb049fc 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -45,6 +45,10 @@
/**** GENERIC ****/
/*****************/
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+static const uint32_t BREADCRUMB_BUFFER_ENTRIES = 512u;
+#endif
+
static const VkFormat RD_TO_VK_FORMAT[RDD::DATA_FORMAT_MAX] = {
VK_FORMAT_R4G4_UNORM_PACK8,
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
@@ -1372,7 +1376,10 @@ Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t
ERR_FAIL_COND_V(err != OK, err);
max_descriptor_sets_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool");
- breadcrumb_buffer = buffer_create(sizeof(uint32_t), BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
+
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+ breadcrumb_buffer = buffer_create(2u * sizeof(uint32_t) * BREADCRUMB_BUFFER_ENTRIES, BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
+#endif
return OK;
}
@@ -5006,10 +5013,65 @@ void RenderingDeviceDriverVulkan::command_end_label(CommandBufferID p_cmd_buffer
/**** DEBUG *****/
/****************/
void RenderingDeviceDriverVulkan::command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) {
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
if (p_data == BreadcrumbMarker::NONE) {
return;
}
- vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, 0, sizeof(uint32_t), p_data);
+
+ if (Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) {
+ // Force a full barrier so commands are not executed in parallel.
+ // This will mean that the last breadcrumb to see was actually the
+ // last (group of) command to be executed (hence, the one causing the crash).
+ VkMemoryBarrier memoryBarrier;
+ memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+ memoryBarrier.pNext = nullptr;
+ memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
+ VK_ACCESS_INDEX_READ_BIT |
+ VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
+ VK_ACCESS_UNIFORM_READ_BIT |
+ VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
+ VK_ACCESS_SHADER_READ_BIT |
+ VK_ACCESS_SHADER_WRITE_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_TRANSFER_READ_BIT |
+ VK_ACCESS_TRANSFER_WRITE_BIT |
+ VK_ACCESS_HOST_READ_BIT |
+ VK_ACCESS_HOST_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
+ VK_ACCESS_INDEX_READ_BIT |
+ VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
+ VK_ACCESS_UNIFORM_READ_BIT |
+ VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
+ VK_ACCESS_SHADER_READ_BIT |
+ VK_ACCESS_SHADER_WRITE_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_TRANSFER_READ_BIT |
+ VK_ACCESS_TRANSFER_WRITE_BIT |
+ VK_ACCESS_HOST_READ_BIT |
+ VK_ACCESS_HOST_WRITE_BIT;
+
+ vkCmdPipelineBarrier(
+ (VkCommandBuffer)p_cmd_buffer.id,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ 0, 1u, &memoryBarrier, 0u, nullptr, 0u, nullptr);
+ }
+
+ // We write to a circular buffer. If you're getting barrier sync errors here,
+ // increase the value of BREADCRUMB_BUFFER_ENTRIES.
+ vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset, sizeof(uint32_t), breadcrumb_id++);
+ vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset + sizeof(uint32_t), sizeof(uint32_t), p_data);
+ breadcrumb_offset += sizeof(uint32_t) * 2u;
+ if (breadcrumb_offset >= BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u) {
+ breadcrumb_offset = 0u;
+ }
+#endif
}
void RenderingDeviceDriverVulkan::on_device_lost() const {
@@ -5091,64 +5153,121 @@ void RenderingDeviceDriverVulkan::on_device_lost() const {
void RenderingDeviceDriverVulkan::print_lost_device_info() {
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
- void *breadcrumb_ptr;
- vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t));
- vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t));
-
- vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &breadcrumb_ptr);
- uint32_t last_breadcrumb = *(uint32_t *)breadcrumb_ptr;
- vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle);
- uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1));
- uint32_t user_data = last_breadcrumb & ((1 << 16) - 1);
- String error_msg = "Last known breadcrumb: ";
-
- switch (phase) {
- case BreadcrumbMarker::ALPHA_PASS:
- error_msg += "ALPHA_PASS";
- break;
- case BreadcrumbMarker::BLIT_PASS:
- error_msg += "BLIT_PASS";
- break;
- case BreadcrumbMarker::DEBUG_PASS:
- error_msg += "DEBUG_PASS";
- break;
- case BreadcrumbMarker::LIGHTMAPPER_PASS:
- error_msg += "LIGHTMAPPER_PASS";
- break;
- case BreadcrumbMarker::OPAQUE_PASS:
- error_msg += "OPAQUE_PASS";
- break;
- case BreadcrumbMarker::POST_PROCESSING_PASS:
- error_msg += "POST_PROCESSING_PASS";
- break;
- case BreadcrumbMarker::REFLECTION_PROBES:
- error_msg += "REFLECTION_PROBES";
- break;
- case BreadcrumbMarker::SHADOW_PASS_CUBE:
- error_msg += "SHADOW_PASS_CUBE";
- break;
- case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL:
- error_msg += "SHADOW_PASS_DIRECTIONAL";
- break;
- case BreadcrumbMarker::SKY_PASS:
- error_msg += "SKY_PASS";
- break;
- case BreadcrumbMarker::TRANSPARENT_PASS:
- error_msg += "TRANSPARENT_PASS";
- break;
- case BreadcrumbMarker::UI_PASS:
- error_msg += "UI_PASS";
- break;
- default:
- error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')';
- break;
+ {
+ String error_msg = "Printing last known breadcrumbs in reverse order (last executed first).";
+ if (!Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) {
+ error_msg += "\nSome of them might be inaccurate. Try running with --accurate-breadcrumbs for precise information.";
+ }
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg);
}
- if (user_data != 0) {
- error_msg += " | User data: " + itos(user_data);
- }
+ uint8_t *breadcrumb_ptr = nullptr;
+ VkResult map_result = VK_SUCCESS;
+
+ vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u);
+ vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u);
+ {
+ void *ptr = nullptr;
+ map_result = vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &ptr);
+ breadcrumb_ptr = reinterpret_cast<uint8_t *>(ptr);
+ }
+
+ if (breadcrumb_ptr && map_result == VK_SUCCESS) {
+ uint32_t last_breadcrumb_offset = 0;
+ {
+ _err_print_error_asap("Searching last breadcrumb. We've sent up to ID: " + itos(breadcrumb_id - 1u));
+
+ // Scan the whole buffer to find the offset with the highest ID.
+ // That means that was the last one to be written.
+ //
+ // We use "breadcrumb_id - id" to account for wraparound.
+ // e.g. breadcrumb_id = 2 and id = 4294967294; then 2 - 4294967294 = 4.
+ // The one with the smallest difference is the closest to breadcrumb_id, which means it's
+ // the last written command.
+ uint32_t biggest_id = 0u;
+ uint32_t smallest_id_diff = std::numeric_limits<uint32_t>::max();
+ const uint32_t *breadcrumb_ptr32 = reinterpret_cast<const uint32_t *>(breadcrumb_ptr);
+ for (size_t i = 0u; i < BREADCRUMB_BUFFER_ENTRIES; ++i) {
+ const uint32_t id = breadcrumb_ptr32[i * 2u];
+ const uint32_t id_diff = breadcrumb_id - id;
+ if (id_diff < smallest_id_diff) {
+ biggest_id = i;
+ smallest_id_diff = id_diff;
+ }
+ }
+
+ _err_print_error_asap("Last breadcrumb ID found: " + itos(breadcrumb_ptr32[biggest_id * 2u]));
+
+ last_breadcrumb_offset = biggest_id * sizeof(uint32_t) * 2u;
+ }
+
+ const size_t entries_to_print = 8u; // Note: The value is arbitrary.
+ for (size_t i = 0u; i < entries_to_print; ++i) {
+ const uint32_t last_breadcrumb = *reinterpret_cast<uint32_t *>(breadcrumb_ptr + last_breadcrumb_offset + sizeof(uint32_t));
+ const uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1));
+ const uint32_t user_data = last_breadcrumb & ((1 << 16) - 1);
+ String error_msg = "Last known breadcrumb: ";
+
+ switch (phase) {
+ case BreadcrumbMarker::ALPHA_PASS:
+ error_msg += "ALPHA_PASS";
+ break;
+ case BreadcrumbMarker::BLIT_PASS:
+ error_msg += "BLIT_PASS";
+ break;
+ case BreadcrumbMarker::DEBUG_PASS:
+ error_msg += "DEBUG_PASS";
+ break;
+ case BreadcrumbMarker::LIGHTMAPPER_PASS:
+ error_msg += "LIGHTMAPPER_PASS";
+ break;
+ case BreadcrumbMarker::OPAQUE_PASS:
+ error_msg += "OPAQUE_PASS";
+ break;
+ case BreadcrumbMarker::POST_PROCESSING_PASS:
+ error_msg += "POST_PROCESSING_PASS";
+ break;
+ case BreadcrumbMarker::REFLECTION_PROBES:
+ error_msg += "REFLECTION_PROBES";
+ break;
+ case BreadcrumbMarker::SHADOW_PASS_CUBE:
+ error_msg += "SHADOW_PASS_CUBE";
+ break;
+ case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL:
+ error_msg += "SHADOW_PASS_DIRECTIONAL";
+ break;
+ case BreadcrumbMarker::SKY_PASS:
+ error_msg += "SKY_PASS";
+ break;
+ case BreadcrumbMarker::TRANSPARENT_PASS:
+ error_msg += "TRANSPARENT_PASS";
+ break;
+ case BreadcrumbMarker::UI_PASS:
+ error_msg += "UI_PASS";
+ break;
+ default:
+ error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')';
+ break;
+ }
+
+ if (user_data != 0) {
+ error_msg += " | User data: " + itos(user_data);
+ }
+
+ _err_print_error_asap(error_msg);
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg);
+ if (last_breadcrumb_offset == 0u) {
+ // Decrement last_breadcrumb_idx, wrapping underflow.
+ last_breadcrumb_offset = BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u;
+ }
+ last_breadcrumb_offset -= sizeof(uint32_t) * 2u;
+ }
+
+ vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle);
+ breadcrumb_ptr = nullptr;
+ } else {
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Couldn't map breadcrumb buffer. VkResult = " + itos(map_result));
+ }
#endif
on_device_lost();
}
@@ -5419,7 +5538,9 @@ RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverV
}
RenderingDeviceDriverVulkan::~RenderingDeviceDriverVulkan() {
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
buffer_free(breadcrumb_buffer);
+#endif
while (small_allocs_pools.size()) {
HashMap<uint32_t, VmaPool>::Iterator E = small_allocs_pools.begin();
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index 016d45809e..7b0c061461 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -174,7 +174,12 @@ private:
VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index);
private:
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+ // It's a circular buffer.
BufferID breadcrumb_buffer;
+ uint32_t breadcrumb_offset = 0u;
+ uint32_t breadcrumb_id = 0u;
+#endif
public:
/*****************/
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index f2c7f0b795..6e1b69b829 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -829,7 +829,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data);
if (!parsed) {
- WARN_PRINT("unknown message " + p_msg);
+ WARN_PRINT("Unknown message: " + p_msg);
}
}
}
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index c0b10e63ed..3047affdfb 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1029,7 +1029,9 @@ void EditorFileSystem::scan() {
void EditorFileSystem::ScanProgress::increment() {
current++;
float ratio = current / MAX(hi, 1.0f);
- progress->step(ratio * 1000.0f);
+ if (progress) {
+ progress->step(ratio * 1000.0f);
+ }
EditorFileSystem::singleton->scan_total = ratio;
}
@@ -1295,7 +1297,7 @@ void EditorFileSystem::_process_removed_files(const HashSet<String> &p_processed
}
}
-void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress) {
+void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive) {
uint64_t current_mtime = FileAccess::get_modified_time(p_dir->get_path());
bool updated_dir = false;
@@ -1489,7 +1491,9 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
scan_actions.push_back(ia);
continue;
}
- _scan_fs_changes(p_dir->get_subdir(i), p_progress);
+ if (p_recursive) {
+ _scan_fs_changes(p_dir->get_subdir(i), p_progress);
+ }
}
nb_files_total = MAX(nb_files_total + diff_nb_files, 0);
@@ -2914,6 +2918,96 @@ void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file
emit_signal(SNAME("resources_reimported"), reloads);
}
+Error EditorFileSystem::_copy_file(const String &p_from, const String &p_to) {
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ if (FileAccess::exists(p_from + ".import")) {
+ Error err = da->copy(p_from, p_to);
+ if (err != OK) {
+ return err;
+ }
+
+ // Remove uid from .import file to avoid conflict.
+ Ref<ConfigFile> cfg;
+ cfg.instantiate();
+ cfg->load(p_from + ".import");
+ cfg->erase_section_key("remap", "uid");
+ err = cfg->save(p_to + ".import");
+ if (err != OK) {
+ return err;
+ }
+ } else if (ResourceLoader::get_resource_uid(p_from) == ResourceUID::INVALID_ID) {
+ // Files which do not use an uid can just be copied.
+ Error err = da->copy(p_from, p_to);
+ if (err != OK) {
+ return err;
+ }
+ } else {
+ // Load the resource and save it again in the new location (this generates a new UID).
+ Error err;
+ Ref<Resource> res = ResourceLoader::load(p_from, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
+ if (err == OK && res.is_valid()) {
+ err = ResourceSaver::save(res, p_to, ResourceSaver::FLAG_COMPRESS);
+ if (err != OK) {
+ return err;
+ }
+ } else if (err != OK) {
+ // When loading files like text files the error is OK but the resource is still null.
+ // We can ignore such files.
+ return err;
+ }
+ }
+ return OK;
+}
+
+bool EditorFileSystem::_copy_directory(const String &p_from, const String &p_to, List<CopiedFile> *p_files) {
+ Ref<DirAccess> old_dir = DirAccess::open(p_from);
+ ERR_FAIL_COND_V(old_dir.is_null(), false);
+
+ Error err = make_dir_recursive(p_to);
+ if (err != OK && err != ERR_ALREADY_EXISTS) {
+ return false;
+ }
+
+ bool success = true;
+ old_dir->set_include_navigational(false);
+ old_dir->list_dir_begin();
+
+ for (String F = old_dir->_get_next(); !F.is_empty(); F = old_dir->_get_next()) {
+ if (old_dir->current_is_dir()) {
+ success = _copy_directory(p_from.path_join(F), p_to.path_join(F), p_files) && success;
+ } else if (F.get_extension() != "import") {
+ CopiedFile copy;
+ copy.from = p_from.path_join(F);
+ copy.to = p_to.path_join(F);
+ p_files->push_back(copy);
+ }
+ }
+ return success;
+}
+
+void EditorFileSystem::_queue_refresh_filesystem() {
+ if (refresh_queued) {
+ return;
+ }
+ refresh_queued = true;
+ get_tree()->connect(SNAME("process_frame"), callable_mp(this, &EditorFileSystem::_refresh_filesystem), CONNECT_ONE_SHOT);
+}
+
+void EditorFileSystem::_refresh_filesystem() {
+ for (const ObjectID &id : folders_to_sort) {
+ EditorFileSystemDirectory *dir = Object::cast_to<EditorFileSystemDirectory>(ObjectDB::get_instance(id));
+ if (dir) {
+ dir->subdirs.sort_custom<DirectoryComparator>();
+ }
+ }
+ folders_to_sort.clear();
+
+ _update_scan_actions();
+
+ emit_signal(SNAME("filesystem_changed"));
+ refresh_queued = false;
+}
+
void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_import_data) {
int current_max = p_import_data->reimport_from + int(p_index);
p_import_data->max_index.exchange_if_greater(current_max);
@@ -3237,10 +3331,9 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p
const String path = da->get_current_dir();
EditorFileSystemDirectory *parent = get_filesystem_path(path);
ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND);
+ folders_to_sort.insert(parent->get_instance_id());
const PackedStringArray folders = p_path.trim_prefix(path).trim_suffix("/").split("/");
- bool first = true;
-
for (const String &folder : folders) {
const int current = parent->find_dir_index(folder);
if (current > -1) {
@@ -3252,18 +3345,59 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p
efd->parent = parent;
efd->name = folder;
parent->subdirs.push_back(efd);
-
- if (first) {
- parent->subdirs.sort_custom<DirectoryComparator>();
- first = false;
- }
parent = efd;
}
- emit_signal(SNAME("filesystem_changed"));
+ _queue_refresh_filesystem();
return OK;
}
+Error EditorFileSystem::copy_file(const String &p_from, const String &p_to) {
+ _copy_file(p_from, p_to);
+
+ EditorFileSystemDirectory *parent = get_filesystem_path(p_to.get_base_dir());
+ ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND);
+
+ ScanProgress sp;
+ _scan_fs_changes(parent, sp, false);
+
+ _queue_refresh_filesystem();
+ return OK;
+}
+
+Error EditorFileSystem::copy_directory(const String &p_from, const String &p_to) {
+ List<CopiedFile> files;
+ bool success = _copy_directory(p_from, p_to, &files);
+
+ EditorProgress *ep = nullptr;
+ if (files.size() > 10) {
+ ep = memnew(EditorProgress("_copy_files", TTR("Copying files..."), files.size()));
+ }
+
+ int i = 0;
+ for (const CopiedFile &F : files) {
+ if (_copy_file(F.from, F.to) != OK) {
+ success = false;
+ }
+ if (ep) {
+ ep->step(F.from.get_file(), i++, false);
+ }
+ }
+ memdelete_notnull(ep);
+
+ EditorFileSystemDirectory *efd = get_filesystem_path(p_to);
+ ERR_FAIL_NULL_V(efd, FAILED);
+ ERR_FAIL_NULL_V(efd->get_parent(), FAILED);
+
+ folders_to_sort.insert(efd->get_parent()->get_instance_id());
+
+ ScanProgress sp;
+ _scan_fs_changes(efd, sp);
+
+ _queue_refresh_filesystem();
+ return success ? OK : FAILED;
+}
+
ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate) {
if (!p_path.is_resource_file() || p_path.begins_with(ProjectSettings::get_singleton()->get_project_data_path())) {
// Saved externally (configuration file) or internal file, do not assign an ID.
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 169fe48b57..0b119f7936 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -241,7 +241,7 @@ class EditorFileSystem : public Node {
bool _find_file(const String &p_file, EditorFileSystemDirectory **r_d, int &r_file_pos) const;
- void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress);
+ void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive = true);
void _delete_internal_files(const String &p_file);
int _insert_actions_delete_files_directory(EditorFileSystemDirectory *p_dir);
@@ -326,6 +326,19 @@ class EditorFileSystem : public Node {
HashSet<String> group_file_cache;
HashMap<String, String> file_icon_cache;
+ struct CopiedFile {
+ String from;
+ String to;
+ };
+
+ bool refresh_queued = false;
+ HashSet<ObjectID> folders_to_sort;
+
+ Error _copy_file(const String &p_from, const String &p_to);
+ bool _copy_directory(const String &p_from, const String &p_to, List<CopiedFile> *p_files);
+ void _queue_refresh_filesystem();
+ void _refresh_filesystem();
+
struct ImportThreadData {
const ImportFile *reimport_files;
int reimport_from;
@@ -380,6 +393,8 @@ public:
void move_group_file(const String &p_path, const String &p_new_path);
Error make_dir_recursive(const String &p_path, const String &p_base_path = String());
+ Error copy_file(const String &p_from, const String &p_to);
+ Error copy_directory(const String &p_from, const String &p_to);
static bool _should_skip_directory(const String &p_path);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index dc14f927f3..8ccb9cf352 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2650,7 +2650,7 @@ Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool
return Variant();
}
-void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
+void EditorPropertyNodePath::_node_selected(const NodePath &p_path, bool p_absolute) {
NodePath path = p_path;
Node *base_node = get_base_node();
@@ -2660,7 +2660,7 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
path = get_tree()->get_edited_scene_root()->get_path_to(to_node);
}
- if (base_node) { // for AnimationTrackKeyEdit
+ if (p_absolute && base_node) { // for AnimationTrackKeyEdit
path = base_node->get_path().rel_path_to(p_path);
}
@@ -2682,7 +2682,7 @@ void EditorPropertyNodePath::_node_assign() {
scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
scene_tree->set_valid_types(valid_types);
add_child(scene_tree);
- scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected));
+ scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected).bind(true));
}
Variant val = get_edited_property_value();
@@ -2750,7 +2750,7 @@ void EditorPropertyNodePath::_accept_text() {
void EditorPropertyNodePath::_text_submitted(const String &p_text) {
NodePath np = p_text;
- emit_changed(get_edited_property(), np);
+ _node_selected(np, false);
edit->hide();
assign->show();
menu->show();
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index dabde86c57..f413aaa379 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -630,7 +630,7 @@ class EditorPropertyNodePath : public EditorProperty {
bool editing_node = false;
Vector<StringName> valid_types;
- void _node_selected(const NodePath &p_path);
+ void _node_selected(const NodePath &p_path, bool p_absolute = true);
void _node_assign();
Node *get_base_node();
void _update_menu();
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 79d5a8b8e4..3f229a34fa 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -1493,76 +1493,22 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin
EditorNode::get_singleton()->add_io_error(TTR("Cannot move a folder into itself.") + "\n" + old_path + "\n");
return;
}
- Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (p_item.is_file) {
print_verbose("Duplicating " + old_path + " -> " + new_path);
// Create the directory structure.
- da->make_dir_recursive(new_path.get_base_dir());
-
- if (FileAccess::exists(old_path + ".import")) {
- Error err = da->copy(old_path, new_path);
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n");
- return;
- }
-
- // Remove uid from .import file to avoid conflict.
- Ref<ConfigFile> cfg;
- cfg.instantiate();
- cfg->load(old_path + ".import");
- cfg->erase_section_key("remap", "uid");
- err = cfg->save(new_path + ".import");
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ".import: " + error_names[err] + "\n");
- return;
- }
- } else {
- // Files which do not use an uid can just be copied.
- if (ResourceLoader::get_resource_uid(old_path) == ResourceUID::INVALID_ID) {
- Error err = da->copy(old_path, new_path);
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n");
- }
- return;
- }
+ EditorFileSystem::get_singleton()->make_dir_recursive(p_new_path.get_base_dir());
- // Load the resource and save it again in the new location (this generates a new UID).
- Error err;
- Ref<Resource> res = ResourceLoader::load(old_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
- if (err == OK && res.is_valid()) {
- err = ResourceSaver::save(res, new_path, ResourceSaver::FLAG_COMPRESS);
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + " " + vformat(TTR("Failed to save resource at %s: %s"), new_path, error_names[err]));
- }
- } else if (err != OK) {
- // When loading files like text files the error is OK but the resource is still null.
- // We can ignore such files.
- EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + " " + vformat(TTR("Failed to load resource at %s: %s"), new_path, error_names[err]));
- }
+ Error err = EditorFileSystem::get_singleton()->copy_file(old_path, new_path);
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n");
}
} else {
- da->make_dir(new_path);
-
- // Recursively duplicate all files inside the folder.
- Ref<DirAccess> old_dir = DirAccess::open(old_path);
- ERR_FAIL_COND(old_dir.is_null());
-
- Ref<FileAccess> file_access = FileAccess::create(FileAccess::ACCESS_RESOURCES);
- old_dir->set_include_navigational(false);
- old_dir->list_dir_begin();
- for (String f = old_dir->_get_next(); !f.is_empty(); f = old_dir->_get_next()) {
- if (f.get_extension() == "import") {
- continue;
- }
- if (file_access->file_exists(old_path + f)) {
- _try_duplicate_item(FileOrFolder(old_path + f, true), new_path + f);
- } else if (da->dir_exists(old_path + f)) {
- _try_duplicate_item(FileOrFolder(old_path + f, false), new_path + f);
- }
+ Error err = EditorFileSystem::get_singleton()->copy_directory(old_path, new_path);
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Error duplicating directory:") + "\n" + old_path + "\n");
}
- old_dir->list_dir_end();
}
}
@@ -1868,21 +1814,15 @@ void FileSystemDock::_rename_operation_confirm() {
}
void FileSystemDock::_duplicate_operation_confirm(const String &p_path) {
- String base_dir = p_path.trim_suffix("/").get_base_dir();
- Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- if (!da->dir_exists(base_dir)) {
- Error err = da->make_dir_recursive(base_dir);
-
+ const String base_dir = p_path.trim_suffix("/").get_base_dir();
+ if (!DirAccess::dir_exists_absolute(base_dir)) {
+ Error err = EditorFileSystem::get_singleton()->make_dir_recursive(base_dir);
if (err != OK) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Could not create base directory: %s"), error_names[err]));
return;
}
}
_try_duplicate_item(to_duplicate, p_path);
-
- // Rescan everything.
- print_verbose("FileSystem: calling rescan.");
- _rescan();
}
void FileSystemDock::_overwrite_dialog_action(bool p_overwrite) {
@@ -3433,7 +3373,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect
const bool is_directory = fpath.ends_with("/");
p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL);
- p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal"));
+ p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Folder in Terminal"));
if (!is_directory) {
p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("ExternalLink")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL);
@@ -4062,17 +4002,17 @@ FileSystemDock::FileSystemDock() {
// `KeyModifierMask::CMD_OR_CTRL | Key::C` conflicts with other editor shortcuts.
ED_SHORTCUT("filesystem_dock/copy_path", TTR("Copy Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C);
- ED_SHORTCUT("filesystem_dock/copy_absolute_path", TTR("Copy Absolute Path"));
- ED_SHORTCUT("filesystem_dock/copy_uid", TTR("Copy UID"));
+ ED_SHORTCUT("filesystem_dock/copy_absolute_path", TTR("Copy Absolute Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::C);
+ ED_SHORTCUT("filesystem_dock/copy_uid", TTR("Copy UID"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | KeyModifierMask::SHIFT | Key::C);
ED_SHORTCUT("filesystem_dock/duplicate", TTR("Duplicate..."), KeyModifierMask::CMD_OR_CTRL | Key::D);
ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), Key::KEY_DELETE);
ED_SHORTCUT("filesystem_dock/rename", TTR("Rename..."), Key::F2);
ED_SHORTCUT_OVERRIDE("filesystem_dock/rename", "macos", Key::ENTER);
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
// Opening the system file manager or opening in an external program is not supported on the Android and web editors.
- ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager"));
- ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program"));
- ED_SHORTCUT("filesystem_dock/open_in_terminal", TTR("Open in Terminal"));
+ ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::R);
+ ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::E);
+ ED_SHORTCUT("filesystem_dock/open_in_terminal", TTR("Open in Terminal"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::T);
#endif
// Properly translating color names would require a separate HashMap, so for simplicity they are provided as comments.
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 607279d7ef..d39447fd49 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -39,10 +39,6 @@
#include "editor/themes/editor_scale.h"
#include "scene/theme/theme_db.h"
-bool EditorSpinSlider::is_text_field() const {
- return true;
-}
-
String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const {
if (!read_only && grabber->is_visible()) {
Key key = (OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios")) ? Key::META : Key::CTRL;
diff --git a/editor/gui/editor_spin_slider.h b/editor/gui/editor_spin_slider.h
index e22ef27952..0e7625e67a 100644
--- a/editor/gui/editor_spin_slider.h
+++ b/editor/gui/editor_spin_slider.h
@@ -103,8 +103,6 @@ protected:
void _focus_entered();
public:
- virtual bool is_text_field() const override;
-
String get_tooltip(const Point2 &p_pos) const override;
String get_text_value() const;
diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp
index c94a7b998c..bf36afc21c 100644
--- a/editor/import/3d/resource_importer_obj.cpp
+++ b/editor/import/3d/resource_importer_obj.cpp
@@ -204,12 +204,12 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
return OK;
}
-static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List<String> *r_missing_deps) {
+static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_generate_lods, bool p_generate_shadow_mesh, bool p_generate_lightmap_uv2, float p_generate_lightmap_uv2_texel_size, const PackedByteArray &p_src_lightmap_cache, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, Vector<Vector<uint8_t>> &r_lightmap_caches, List<String> *r_missing_deps) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path));
- // Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree)
- // This should only match, if it's indeed a COFF file header
+ // Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree).
+ // This should only match if it's indeed a COFF file header.
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
const int first_bytes = f->get_16();
static const Vector<int> coff_header_machines{
@@ -447,6 +447,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
}
mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, array, TypedArray<Array>(), Dictionary(), material, name, mesh_flags);
+
print_verbose("OBJ: Added surface :" + mesh->get_surface_name(mesh->get_surface_count() - 1));
if (!current_material.is_empty()) {
@@ -510,6 +511,43 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
}
}
+ if (p_generate_lightmap_uv2) {
+ Vector<uint8_t> lightmap_cache;
+ mesh->lightmap_unwrap_cached(Transform3D(), p_generate_lightmap_uv2_texel_size, p_src_lightmap_cache, lightmap_cache);
+
+ if (!lightmap_cache.is_empty()) {
+ if (r_lightmap_caches.is_empty()) {
+ r_lightmap_caches.push_back(lightmap_cache);
+ } else {
+ // MD5 is stored at the beginning of the cache data.
+ const String new_md5 = String::md5(lightmap_cache.ptr());
+
+ for (int i = 0; i < r_lightmap_caches.size(); i++) {
+ const String md5 = String::md5(r_lightmap_caches[i].ptr());
+ if (new_md5 < md5) {
+ r_lightmap_caches.insert(i, lightmap_cache);
+ break;
+ }
+
+ if (new_md5 == md5) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ mesh->optimize_indices_for_cache();
+
+ if (p_generate_lods) {
+ // Use normal merge/split angles that match the defaults used for 3D scene importing.
+ mesh->generate_lods(60.0f, 25.0f, {});
+ }
+
+ if (p_generate_shadow_mesh) {
+ mesh->create_shadow_mesh();
+ }
+
if (p_single_mesh && mesh->get_surface_count() > 0) {
r_meshes.push_back(mesh);
}
@@ -520,7 +558,10 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
List<Ref<ImporterMesh>> meshes;
- Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps);
+ // LOD, shadow mesh and lightmap UV2 generation are handled by ResourceImporterScene in this case,
+ // so disable it within the OBJ mesh import.
+ Vector<Vector<uint8_t>> mesh_lightmap_caches;
+ Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, false, false, 0.2, PackedByteArray(), Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, mesh_lightmap_caches, r_missing_deps);
if (err != OK) {
if (r_err) {
@@ -589,19 +630,51 @@ String ResourceImporterOBJ::get_preset_name(int p_idx) const {
void ResourceImporterOBJ::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_lods"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_shadow_mesh"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_lightmap_uv2", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "generate_lightmap_uv2_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1)));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0)));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_disable_mesh_compression"), false));
}
bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
+ if (p_option == "generate_lightmap_uv2_texel_size" && !p_options["generate_lightmap_uv2"]) {
+ // Only display the lightmap texel size import option when lightmap UV2 generation is enabled.
+ return false;
+ }
+
return true;
}
Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
List<Ref<ImporterMesh>> meshes;
- Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr);
+ Vector<uint8_t> src_lightmap_cache;
+ Vector<Vector<uint8_t>> mesh_lightmap_caches;
+
+ Error err;
+ {
+ src_lightmap_cache = FileAccess::get_file_as_bytes(p_source_file + ".unwrap_cache", &err);
+ if (err != OK) {
+ src_lightmap_cache.clear();
+ }
+ }
+
+ err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["generate_lods"], p_options["generate_shadow_mesh"], p_options["generate_lightmap_uv2"], p_options["generate_lightmap_uv2_texel_size"], src_lightmap_cache, p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], mesh_lightmap_caches, nullptr);
+
+ if (mesh_lightmap_caches.size()) {
+ Ref<FileAccess> f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE);
+ if (f.is_valid()) {
+ f->store_32(mesh_lightmap_caches.size());
+ for (int i = 0; i < mesh_lightmap_caches.size(); i++) {
+ String md5 = String::md5(mesh_lightmap_caches[i].ptr());
+ f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size());
+ }
+ }
+ }
+ err = OK;
ERR_FAIL_COND_V(err != OK, err);
ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG);
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index 2fb24a245f..bb779e81b4 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -114,7 +114,15 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
}
/* GET FILESIZE */
- file->get_32(); // filesize
+
+ // The file size in header is 8 bytes less than the actual size.
+ // See https://docs.fileformat.com/audio/wav/
+ const int FILE_SIZE_HEADER_OFFSET = 8;
+ uint32_t file_size_header = file->get_32() + FILE_SIZE_HEADER_OFFSET;
+ uint64_t file_size = file->get_length();
+ if (file_size != file_size_header) {
+ WARN_PRINT(vformat("File size %d is %s than the expected size %d. (%s)", file_size, file_size > file_size_header ? "larger" : "smaller", file_size_header, p_source_file));
+ }
/* CHECK WAVE */
@@ -200,7 +208,12 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
break;
}
+ uint64_t remaining_bytes = file_size - file_pos;
frames = chunksize;
+ if (remaining_bytes < chunksize) {
+ WARN_PRINT(vformat("Data chunk size is smaller than expected. Proceeding with actual data size. (%s)", p_source_file));
+ frames = remaining_bytes;
+ }
if (format_channels == 0) {
ERR_FAIL_COND_V(format_channels == 0, ERR_INVALID_DATA);
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index 6aa865d6db..6ae373d000 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -792,23 +792,14 @@ ImportDock::ImportDock() {
import->set_text(TTR("Reimport"));
import->set_disabled(true);
import->connect(SceneStringName(pressed), callable_mp(this, &ImportDock::_reimport_pressed));
- if (!DisplayServer::get_singleton()->get_swap_cancel_ok()) {
- advanced_spacer = hb->add_spacer();
- advanced = memnew(Button);
- advanced->set_text(TTR("Advanced..."));
- hb->add_child(advanced);
- }
+ advanced_spacer = hb->add_spacer();
+ advanced = memnew(Button);
+ advanced->set_text(TTR("Advanced..."));
+ hb->add_child(advanced);
hb->add_spacer();
hb->add_child(import);
hb->add_spacer();
- if (DisplayServer::get_singleton()->get_swap_cancel_ok()) {
- advanced = memnew(Button);
- advanced->set_text(TTR("Advanced..."));
- hb->add_child(advanced);
- advanced_spacer = hb->add_spacer();
- }
-
advanced->hide();
advanced_spacer->hide();
advanced->connect(SceneStringName(pressed), callable_mp(this, &ImportDock::_advanced_options));
diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
index 1628857fb9..6e6f94dda5 100644
--- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
@@ -95,7 +95,7 @@ String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g
}
if (Object::cast_to<CylinderShape3D>(*s)) {
- return p_id == 0 ? "Radius" : "Height";
+ return helper->cylinder_get_handle_name(p_id);
}
if (Object::cast_to<SeparationRayShape3D>(*s)) {
@@ -221,25 +221,15 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i
}
if (Object::cast_to<CylinderShape3D>(*s)) {
- Vector3 axis;
- axis[p_id == 0 ? 0 : 1] = 1.0;
Ref<CylinderShape3D> cs2 = s;
- Vector3 ra, rb;
- Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
- float d = axis.dot(ra);
- if (Node3DEditor::get_singleton()->is_snap_enabled()) {
- d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
- }
- if (d < 0.001) {
- d = 0.001;
- }
-
- if (p_id == 0) {
- cs2->set_radius(d);
- } else if (p_id == 1) {
- cs2->set_height(d * 2.0);
- }
+ real_t height = cs2->get_height();
+ real_t radius = cs2->get_radius();
+ Vector3 position;
+ helper->cylinder_set_handle(sg, p_id, height, radius, position);
+ cs2->set_height(height);
+ cs2->set_radius(radius);
+ cs->set_global_position(position);
}
}
@@ -295,31 +285,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
if (Object::cast_to<CylinderShape3D>(*s)) {
Ref<CylinderShape3D> ss = s;
- if (p_cancel) {
- if (p_id == 0) {
- ss->set_radius(p_restore);
- } else {
- ss->set_height(p_restore);
- }
- return;
- }
-
- EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
- if (p_id == 0) {
- ur->create_action(TTR("Change Cylinder Shape Radius"));
- ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
- ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
- } else {
- ur->create_action(
- ///
-
- ////////
- TTR("Change Cylinder Shape Height"));
- ur->add_do_method(ss.ptr(), "set_height", ss->get_height());
- ur->add_undo_method(ss.ptr(), "set_height", p_restore);
- }
-
- ur->commit_action();
+ helper->cylinder_commit_handle(p_id, TTR("Change Cylinder Shape Radius"), TTR("Change Cylinder Shape Height"), p_cancel, cs, *ss, *ss);
}
if (Object::cast_to<SeparationRayShape3D>(*s)) {
@@ -536,10 +502,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_segments(collision_segments);
- Vector<Vector3> handles = {
- Vector3(cs2->get_radius(), 0, 0),
- Vector3(0, cs2->get_height() * 0.5, 0)
- };
+ Vector<Vector3> handles = helper->cylinder_get_handles(cs2->get_height(), cs2->get_radius());
p_gizmo->add_handles(handles, handles_material);
}
diff --git a/editor/plugins/gizmos/gizmo_3d_helper.cpp b/editor/plugins/gizmos/gizmo_3d_helper.cpp
index 432ef14709..1c6a905e0c 100644
--- a/editor/plugins/gizmos/gizmo_3d_helper.cpp
+++ b/editor/plugins/gizmos/gizmo_3d_helper.cpp
@@ -141,3 +141,98 @@ void Gizmo3DHelper::box_commit_handle(const String &p_action_name, bool p_cancel
ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
ur->commit_action();
}
+
+Vector<Vector3> Gizmo3DHelper::cylinder_get_handles(real_t p_height, real_t p_radius) {
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(p_radius, 0, 0));
+ handles.push_back(Vector3(0, p_height * 0.5, 0));
+ handles.push_back(Vector3(0, p_height * -0.5, 0));
+ return handles;
+}
+
+String Gizmo3DHelper::cylinder_get_handle_name(int p_id) const {
+ if (p_id == 0) {
+ return "Radius";
+ } else {
+ return "Height";
+ }
+}
+
+void Gizmo3DHelper::cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position) {
+ int sign = p_id == 2 ? -1 : 1;
+ int axis = p_id == 0 ? 0 : 1;
+
+ Vector3 axis_vector;
+ axis_vector[axis] = sign;
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(axis_vector * -4096, axis_vector * 4096, p_segment[0], p_segment[1], ra, rb);
+ float d = axis_vector.dot(ra);
+
+ // Snap to grid.
+ if (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (p_id == 0) {
+ // Adjust radius.
+ if (d < 0.001) {
+ d = 0.001;
+ }
+ r_radius = d;
+ r_cylinder_position = initial_transform.get_origin();
+ } else if (p_id == 1 || p_id == 2) {
+ real_t initial_height = initial_value;
+
+ // Adjust height.
+ if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
+ r_height = d * 2.0;
+ } else {
+ r_height = (initial_height * 0.5) + d;
+ }
+
+ if (r_height < 0.001) {
+ r_height = 0.001;
+ }
+
+ // Adjust position.
+ if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
+ r_cylinder_position = initial_transform.get_origin();
+ } else {
+ Vector3 offset;
+ offset[axis] = (r_height - initial_height) * 0.5 * sign;
+ r_cylinder_position = initial_transform.xform(offset);
+ }
+ }
+}
+
+void Gizmo3DHelper::cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object, Object *p_radius_object, const StringName &p_position_property, const StringName &p_height_property, const StringName &p_radius_property) {
+ if (!p_height_object) {
+ p_height_object = p_position_object;
+ }
+ if (!p_radius_object) {
+ p_radius_object = p_position_object;
+ }
+
+ if (p_cancel) {
+ if (p_id == 0) {
+ p_radius_object->set(p_radius_property, initial_value);
+ } else {
+ p_height_object->set(p_height_property, initial_value);
+ }
+ p_position_object->set(p_position_property, initial_transform.get_origin());
+ return;
+ }
+
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(p_id == 0 ? p_radius_action_name : p_height_action_name);
+ if (p_id == 0) {
+ ur->add_do_property(p_radius_object, p_radius_property, p_radius_object->get(p_radius_property));
+ ur->add_undo_property(p_radius_object, p_radius_property, initial_value);
+ } else {
+ ur->add_do_property(p_height_object, p_height_property, p_height_object->get(p_height_property));
+ ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property));
+ ur->add_undo_property(p_height_object, p_height_property, initial_value);
+ ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
+ }
+ ur->commit_action();
+}
diff --git a/editor/plugins/gizmos/gizmo_3d_helper.h b/editor/plugins/gizmos/gizmo_3d_helper.h
index 7022b13a03..39721ede3d 100644
--- a/editor/plugins/gizmos/gizmo_3d_helper.h
+++ b/editor/plugins/gizmos/gizmo_3d_helper.h
@@ -52,6 +52,11 @@ public:
String box_get_handle_name(int p_id) const;
void box_set_handle(const Vector3 p_segment[2], int p_id, Vector3 &r_box_size, Vector3 &r_box_position);
void box_commit_handle(const String &p_action_name, bool p_cancel, Object *p_position_object, Object *p_size_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_size_property = "size");
+
+ Vector<Vector3> cylinder_get_handles(real_t p_height, real_t p_radius);
+ String cylinder_get_handle_name(int p_id) const;
+ void cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position);
+ void cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius");
};
#endif // GIZMO_3D_HELPER_H
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 91d3144f48..98aa0b8ee1 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -158,11 +158,12 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) {
}
if (ED_IS_SHORTCUT("scene_tree/rename", p_event)) {
- // Prevent renaming if a button is focused
- // to avoid conflict with Enter shortcut on macOS
- if (!focus_owner || !Object::cast_to<BaseButton>(focus_owner)) {
- _tool_selected(TOOL_RENAME);
+ // Prevent renaming if a button or a range is focused
+ // to avoid conflict with Enter shortcut on macOS.
+ if (focus_owner && (Object::cast_to<BaseButton>(focus_owner) || Object::cast_to<Range>(focus_owner))) {
+ return;
}
+ _tool_selected(TOOL_RENAME);
#ifdef MODULE_REGEX_ENABLED
} else if (ED_IS_SHORTCUT("scene_tree/batch_rename", p_event)) {
_tool_selected(TOOL_BATCH_RENAME);
diff --git a/main/main.cpp b/main/main.cpp
index 37c2d88beb..5014047ae1 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -632,6 +632,7 @@ void Main::print_help(const char *p_binary) {
print_help_option("--generate-spirv-debug-info", "Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
print_help_option("--extra-gpu-memory-tracking", "Enables additional memory tracking (see class reference for `RenderingDevice.get_driver_and_device_memory_report()` and linked methods). Currently only implemented for Vulkan. Enabling this feature may cause crashes on some systems due to buggy drivers or bugs in the Vulkan Loader. See https://github.com/godotengine/godot/issues/95967\n");
+ print_help_option("--accurate-breadcrumbs", "Force barriers between breadcrumbs. Useful for narrowing down a command causing GPU resets. Currently only implemented for Vulkan.\n");
#endif
print_help_option("--remote-debug <uri>", "Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
print_help_option("--single-threaded-scene", "Force scene tree to run in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
@@ -1238,8 +1239,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#endif
} else if (arg == "--generate-spirv-debug-info") {
Engine::singleton->generate_spirv_debug_info = true;
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
} else if (arg == "--extra-gpu-memory-tracking") {
Engine::singleton->extra_gpu_memory_tracking = true;
+ } else if (arg == "--accurate-breadcrumbs") {
+ Engine::singleton->accurate_breadcrumbs = true;
+#endif
} else if (arg == "--tablet-driver") {
if (N) {
tablet_driver = N->get();
@@ -2501,7 +2506,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF_RST_NOVAL("audio/driver/driver", AudioDriverManager::get_driver(0)->get_name());
if (audio_driver.is_empty()) { // Specified in project.godot.
- audio_driver = GLOBAL_GET("audio/driver/driver");
+ if (project_manager) {
+ // The project manager doesn't need to play sound (TTS audio output is not emitted by Godot, but by the system itself).
+ // Disable audio output so it doesn't appear in the list of applications outputting sound in the OS.
+ // On macOS, this also prevents the project manager from inhibiting suspend.
+ audio_driver = "Dummy";
+ } else {
+ audio_driver = GLOBAL_GET("audio/driver/driver");
+ }
}
// Make sure that dummy is the last one, which it is assumed to be by design.
diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp
index 20054b58a5..6253d96b8c 100644
--- a/modules/csg/editor/csg_gizmos.cpp
+++ b/modules/csg/editor/csg_gizmos.cpp
@@ -280,24 +280,13 @@ void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_i
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
- Vector3 axis;
- axis[p_id == 0 ? 0 : 1] = 1.0;
- Vector3 ra, rb;
- Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
- float d = axis.dot(ra);
- if (Node3DEditor::get_singleton()->is_snap_enabled()) {
- d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
- }
-
- if (d < 0.001) {
- d = 0.001;
- }
-
- if (p_id == 0) {
- s->set_radius(d);
- } else if (p_id == 1) {
- s->set_height(d * 2.0);
- }
+ real_t height = s->get_height();
+ real_t radius = s->get_radius();
+ Vector3 position;
+ helper->cylinder_set_handle(sg, p_id, height, radius, position);
+ s->set_height(height);
+ s->set_radius(radius);
+ s->set_global_position(position);
}
if (Object::cast_to<CSGTorus3D>(cs)) {
@@ -342,32 +331,11 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
}
if (Object::cast_to<CSGBox3D>(cs)) {
- helper->box_commit_handle(TTR("Change Box Shape Size"), p_cancel, cs);
+ helper->box_commit_handle(TTR("Change CSG Box Size"), p_cancel, cs);
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
- CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
- if (p_cancel) {
- if (p_id == 0) {
- s->set_radius(p_restore);
- } else {
- s->set_height(p_restore);
- }
- return;
- }
-
- EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
- if (p_id == 0) {
- ur->create_action(TTR("Change Cylinder Radius"));
- ur->add_do_method(s, "set_radius", s->get_radius());
- ur->add_undo_method(s, "set_radius", p_restore);
- } else {
- ur->create_action(TTR("Change Cylinder Height"));
- ur->add_do_method(s, "set_height", s->get_height());
- ur->add_undo_method(s, "set_height", p_restore);
- }
-
- ur->commit_action();
+ helper->cylinder_commit_handle(p_id, TTR("Change CSG Cylinder Radius"), TTR("Change CSG Cylinder Height"), p_cancel, cs);
}
if (Object::cast_to<CSGTorus3D>(cs)) {
@@ -508,9 +476,7 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
- Vector<Vector3> handles;
- handles.push_back(Vector3(s->get_radius(), 0, 0));
- handles.push_back(Vector3(0, s->get_height() * 0.5, 0));
+ Vector<Vector3> handles = helper->cylinder_get_handles(s->get_height(), s->get_radius());
p_gizmo->add_handles(handles, handles_material);
}
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 21365da7cc..ede4ce6617 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -61,7 +61,7 @@
<method name="convert" deprecated="Use [method @GlobalScope.type_convert] instead.">
<return type="Variant" />
<param index="0" name="what" type="Variant" />
- <param index="1" name="type" type="int" />
+ <param index="1" name="type" type="int" enum="Variant.Type" />
<description>
Converts [param what] to [param type] in the best way possible. The [param type] uses the [enum Variant.Type] values.
[codeblock]
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index d1e4c4ecf1..1278cac8ad 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1077,6 +1077,26 @@ void GDScript::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &GDScript::_new, MethodInfo("new"));
}
+void GDScript::set_path_cache(const String &p_path) {
+ if (ResourceCache::has(p_path)) {
+ set_path(p_path, true);
+ return;
+ }
+
+ if (is_root_script()) {
+ Script::set_path_cache(p_path);
+ }
+
+ String old_path = path;
+ path = p_path;
+ path_valid = true;
+ GDScriptCache::move_script(old_path, p_path);
+
+ for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) {
+ kv.value->set_path_cache(p_path);
+ }
+}
+
void GDScript::set_path(const String &p_path, bool p_take_over) {
if (is_root_script()) {
Script::set_path(p_path, p_take_over);
@@ -2555,11 +2575,11 @@ void GDScriptLanguage::reload_all_scripts() {
}
}
}
-#endif
+#endif // TOOLS_ENABLED
}
reload_scripts(scripts, true);
-#endif
+#endif // DEBUG_ENABLED
}
void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload) {
@@ -2629,7 +2649,7 @@ void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload
}
}
-#endif
+#endif // TOOLS_ENABLED
for (const KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : scr->pending_reload_state) {
map[F.key] = F.value; //pending to reload, use this one instead
@@ -2697,7 +2717,7 @@ void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload
//if instance states were saved, set them!
}
-#endif
+#endif // DEBUG_ENABLED
}
void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 527fe55ba6..3471a29d96 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -302,6 +302,7 @@ public:
virtual Error reload(bool p_keep_state = false) override;
+ virtual void set_path_cache(const String &p_path) override;
virtual void set_path(const String &p_path, bool p_take_over = false) override;
String get_script_path() const;
Error load_source_code(const String &p_path);
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index cd950662fd..5ac444ca3e 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -942,8 +942,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
Finally finally([&]() {
ensure_cached_external_parser_for_class(member.get_datatype().class_type, p_class, "Trying to resolve datatype of class member", p_source);
GDScriptParser::DataType member_type = member.get_datatype();
- if (member_type.has_container_element_type(0)) {
- ensure_cached_external_parser_for_class(member_type.get_container_element_type(0).class_type, p_class, "Trying to resolve datatype of class member", p_source);
+ for (int i = 0; i < member_type.get_container_element_type_count(); ++i) {
+ ensure_cached_external_parser_for_class(member_type.get_container_element_type(i).class_type, p_class, "Trying to resolve datatype of class member", p_source);
}
});
@@ -3818,6 +3818,12 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
}
Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class(const GDScriptParser::ClassNode *p_class, const GDScriptParser::ClassNode *p_from_class, const char *p_context, const GDScriptParser::Node *p_source) {
+ // Delicate piece of code that intentionally doesn't use the GDScript cache or `get_depended_parser_for`.
+ // Search dependencies for the parser that owns `p_class` and make a cache entry for it.
+ // Required for how we store pointers to classes owned by other parser trees and need to call `resolve_class_member` and such on the same parser tree.
+ // Since https://github.com/godotengine/godot/pull/94871 there can technically be multiple parsers for the same script in the same parser tree.
+ // Even if unlikely, getting the wrong parser could lead to strange undefined behavior without errors.
+
if (p_class == nullptr) {
return nullptr;
}
@@ -3834,8 +3840,6 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class
p_from_class = parser->head;
}
- String script_path = p_class->get_datatype().script_path;
-
Ref<GDScriptParserRef> parser_ref;
for (const GDScriptParser::ClassNode *look_class = p_from_class; look_class != nullptr; look_class = look_class->base_type.class_type) {
if (parser->has_class(look_class)) {
@@ -5869,7 +5873,7 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier
parent = ClassDB::get_parent_class(parent);
}
}
-#endif
+#endif // DEBUG_ENABLED
GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source) {
// Unary version.
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 27dd792f58..c0ad9b80ce 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -314,7 +314,7 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_e
Ref<GDScript> script;
script.instantiate();
- script->set_path(p_path, true);
+ script->set_path_cache(p_path);
if (remapped_path.get_extension().to_lower() == "gdc") {
Vector<uint8_t> buffer = get_binary_tokens(remapped_path);
if (buffer.is_empty()) {
@@ -362,6 +362,7 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
return script;
}
}
+ script->set_path(p_path, true);
const String remapped_path = ResourceLoader::path_remap(p_path);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index b82fedf180..55a9c2db40 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -3166,7 +3166,9 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
HashMap<String, ScriptLanguage::CodeCompletionOption> options;
GDScriptParser::CompletionContext completion_context = parser.get_completion_context();
- completion_context.base = p_owner;
+ if (completion_context.current_class != nullptr && completion_context.current_class->outer == nullptr) {
+ completion_context.base = p_owner;
+ }
bool is_function = false;
switch (completion_context.type) {
@@ -3536,13 +3538,13 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
return OK;
}
-#else
+#else // !TOOLS_ENABLED
Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) {
return OK;
}
-#endif
+#endif // TOOLS_ENABLED
//////// END COMPLETION //////////
@@ -4127,4 +4129,4 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return ERR_CANT_RESOLVE;
}
-#endif
+#endif // TOOLS_ENABLED
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index cef94ff337..59ebaaa4e3 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -246,7 +246,7 @@ void GDScriptParser::apply_pending_warnings() {
pending_warnings.clear();
}
-#endif
+#endif // DEBUG_ENABLED
void GDScriptParser::override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument) {
if (!for_completion) {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 2de638ebb9..06b097f554 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -167,6 +167,10 @@ public:
container_element_types.write[p_index] = DataType(p_type);
}
+ _FORCE_INLINE_ int get_container_element_type_count() const {
+ return container_element_types.size();
+ }
+
_FORCE_INLINE_ DataType get_container_element_type(int p_index) const {
ERR_FAIL_INDEX_V(p_index, container_element_types.size(), get_variant_type());
return container_element_types[p_index];
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index 573df2a355..84be7128db 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -36,7 +36,6 @@
#include "core/io/resource_loader.h"
#include "core/object/class_db.h"
-#include "core/object/method_bind.h"
#include "core/object/object.h"
#include "core/templates/oa_hash_map.h"
#include "core/templates/vector.h"
@@ -44,101 +43,105 @@
#ifdef DEBUG_ENABLED
-#define VALIDATE_ARG_COUNT(m_count) \
- if (p_arg_count < m_count) { \
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
- r_error.expected = m_count; \
+#define DEBUG_VALIDATE_ARG_COUNT(m_min_count, m_max_count) \
+ if (unlikely(p_arg_count < m_min_count)) { \
*r_ret = Variant(); \
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
+ r_error.expected = m_min_count; \
return; \
} \
- if (p_arg_count > m_count) { \
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
- r_error.expected = m_count; \
+ if (unlikely(p_arg_count > m_max_count)) { \
*r_ret = Variant(); \
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
+ r_error.expected = m_max_count; \
return; \
}
-#define VALIDATE_ARG_INT(m_arg) \
- if (p_args[m_arg]->get_type() != Variant::INT) { \
+#define DEBUG_VALIDATE_ARG_TYPE(m_arg, m_type) \
+ if (unlikely(!Variant::can_convert_strict(p_args[m_arg]->get_type(), m_type))) { \
+ *r_ret = Variant(); \
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+ r_error.argument = m_arg; \
+ r_error.expected = m_type; \
+ return; \
+ }
+
+#define DEBUG_VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ *r_ret = m_msg; \
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
r_error.argument = m_arg; \
- r_error.expected = Variant::INT; \
- *r_ret = Variant(); \
+ r_error.expected = m_type; \
return; \
}
-#define VALIDATE_ARG_NUM(m_arg) \
- if (!p_args[m_arg]->is_num()) { \
+#else // !DEBUG_ENABLED
+
+#define DEBUG_VALIDATE_ARG_COUNT(m_min_count, m_max_count)
+#define DEBUG_VALIDATE_ARG_TYPE(m_arg, m_type)
+#define DEBUG_VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg)
+
+#endif // DEBUG_ENABLED
+
+#define VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ *r_ret = m_msg; \
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
r_error.argument = m_arg; \
- r_error.expected = Variant::FLOAT; \
- *r_ret = Variant(); \
+ r_error.expected = m_type; \
return; \
}
-#else
-
-#define VALIDATE_ARG_COUNT(m_count)
-#define VALIDATE_ARG_INT(m_arg)
-#define VALIDATE_ARG_NUM(m_arg)
-
-#endif
+#define GDFUNC_FAIL_COND_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ *r_ret = m_msg; \
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; \
+ return; \
+ }
struct GDScriptUtilityFunctionsDefinitions {
#ifndef DISABLE_DEPRECATED
static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_INT(1);
+ DEBUG_VALIDATE_ARG_COUNT(2, 2);
+ DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT);
+
int type = *p_args[1];
- if (type < 0 || type >= Variant::VARIANT_MAX) {
- *r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::INT;
- return;
+ DEBUG_VALIDATE_ARG_CUSTOM(1, Variant::INT, type < 0 || type >= Variant::VARIANT_MAX,
+ RTR("Invalid type argument to convert(), use TYPE_* constants."));
- } else {
- Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
- if (r_error.error != Callable::CallError::CALL_OK) {
- *r_ret = vformat(RTR(R"(Cannot convert "%s" to "%s".)"), Variant::get_type_name(p_args[0]->get_type()), Variant::get_type_name(Variant::Type(type)));
- }
- }
+ Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
}
#endif // DISABLE_DEPRECATED
static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(1);
+ DEBUG_VALIDATE_ARG_COUNT(1, 1);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::STRING_NAME);
*r_ret = ClassDB::class_exists(*p_args[0]);
}
static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_INT(0);
+ DEBUG_VALIDATE_ARG_COUNT(1, 1);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
char32_t result[2] = { *p_args[0], 0 };
*r_ret = String(result);
}
static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ DEBUG_VALIDATE_ARG_COUNT(1, 3);
switch (p_arg_count) {
- case 0: {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.expected = 1;
- *r_ret = Variant();
- } break;
case 1: {
- VALIDATE_ARG_NUM(0);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
+
int count = *p_args[0];
+
Array arr;
if (count <= 0) {
*r_ret = arr;
return;
}
+
Error err = arr.resize(count);
- if (err != OK) {
- *r_ret = RTR("Cannot resize array.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return;
- }
+ GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array."));
for (int i = 0; i < count; i++) {
arr[i] = i;
@@ -147,8 +150,8 @@ struct GDScriptUtilityFunctionsDefinitions {
*r_ret = arr;
} break;
case 2: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
+ DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT);
int from = *p_args[0];
int to = *p_args[1];
@@ -158,30 +161,26 @@ struct GDScriptUtilityFunctionsDefinitions {
*r_ret = arr;
return;
}
+
Error err = arr.resize(to - from);
- if (err != OK) {
- *r_ret = RTR("Cannot resize array.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return;
- }
+ GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array."));
+
for (int i = from; i < to; i++) {
arr[i - from] = i;
}
+
*r_ret = arr;
} break;
case 3: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
+ DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT);
+ DEBUG_VALIDATE_ARG_TYPE(2, Variant::INT);
int from = *p_args[0];
int to = *p_args[1];
int incr = *p_args[2];
- if (incr == 0) {
- *r_ret = RTR("Step argument is zero!");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return;
- }
+
+ VALIDATE_ARG_CUSTOM(2, Variant::INT, incr == 0, RTR("Step argument is zero!"));
Array arr;
if (from >= to && incr > 0) {
@@ -202,12 +201,7 @@ struct GDScriptUtilityFunctionsDefinitions {
}
Error err = arr.resize(count);
-
- if (err != OK) {
- *r_ret = RTR("Cannot resize array.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return;
- }
+ GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array."));
if (incr > 0) {
int idx = 0;
@@ -223,138 +217,79 @@ struct GDScriptUtilityFunctionsDefinitions {
*r_ret = arr;
} break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.expected = 3;
- *r_ret = Variant();
-
- } break;
}
}
static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(1);
- if (!p_args[0]->is_string()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- *r_ret = Variant();
- } else {
- *r_ret = ResourceLoader::load(*p_args[0]);
- }
+ DEBUG_VALIDATE_ARG_COUNT(1, 1);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::STRING);
+ *r_ret = ResourceLoader::load(*p_args[0]);
}
static inline void inst_to_dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(1);
+ DEBUG_VALIDATE_ARG_COUNT(1, 1);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::OBJECT);
if (p_args[0]->get_type() == Variant::NIL) {
*r_ret = Variant();
- } else if (p_args[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
+ return;
+ }
+
+ Object *obj = *p_args[0];
+ if (!obj) {
*r_ret = Variant();
- } else {
- Object *obj = *p_args[0];
- if (!obj) {
- *r_ret = Variant();
+ return;
+ }
- } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- *r_ret = RTR("Not a script with an instance");
- return;
- } else {
- GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
- Ref<GDScript> base = ins->get_script();
- if (base.is_null()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- *r_ret = RTR("Not based on a script");
- return;
- }
+ VALIDATE_ARG_CUSTOM(0, Variant::OBJECT,
+ !obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton(),
+ RTR("Not a script with an instance."));
- GDScript *p = base.ptr();
- String path = p->get_script_path();
- Vector<StringName> sname;
+ GDScriptInstance *inst = static_cast<GDScriptInstance *>(obj->get_script_instance());
- while (p->_owner) {
- sname.push_back(p->local_name);
- p = p->_owner;
- }
- sname.reverse();
+ Ref<GDScript> base = inst->get_script();
+ VALIDATE_ARG_CUSTOM(0, Variant::OBJECT, base.is_null(), RTR("Not based on a script."));
- if (!path.is_resource_file()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- *r_ret = Variant();
+ GDScript *p = base.ptr();
+ String path = p->get_script_path();
+ Vector<StringName> sname;
- *r_ret = RTR("Not based on a resource file");
+ while (p->_owner) {
+ sname.push_back(p->local_name);
+ p = p->_owner;
+ }
+ sname.reverse();
- return;
- }
+ VALIDATE_ARG_CUSTOM(0, Variant::OBJECT, !path.is_resource_file(), RTR("Not based on a resource file."));
- NodePath cp(sname, Vector<StringName>(), false);
+ NodePath cp(sname, Vector<StringName>(), false);
- Dictionary d;
- d["@subpath"] = cp;
- d["@path"] = path;
+ Dictionary d;
+ d["@subpath"] = cp;
+ d["@path"] = path;
- for (const KeyValue<StringName, GDScript::MemberInfo> &E : base->member_indices) {
- if (!d.has(E.key)) {
- d[E.key] = ins->members[E.value.index];
- }
- }
- *r_ret = d;
+ for (const KeyValue<StringName, GDScript::MemberInfo> &E : base->member_indices) {
+ if (!d.has(E.key)) {
+ d[E.key] = inst->members[E.value.index];
}
}
+
+ *r_ret = d;
}
static inline void dict_to_inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::DICTIONARY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- *r_ret = Variant();
-
- return;
- }
+ DEBUG_VALIDATE_ARG_COUNT(1, 1);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::DICTIONARY);
Dictionary d = *p_args[0];
- if (!d.has("@path")) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- *r_ret = RTR("Invalid instance dictionary format (missing @path)");
-
- return;
- }
+ VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, !d.has("@path"), RTR("Invalid instance dictionary format (missing @path)."));
Ref<Script> scr = ResourceLoader::load(d["@path"]);
- if (!scr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- *r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
- return;
- }
+ VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, !scr.is_valid(), RTR("Invalid instance dictionary format (can't load script at @path)."));
Ref<GDScript> gdscr = scr;
-
- if (!gdscr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- *r_ret = Variant();
- *r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
- return;
- }
+ VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, !gdscr.is_valid(), RTR("Invalid instance dictionary format (invalid script at @path)."));
NodePath sub;
if (d.has("@subpath")) {
@@ -363,54 +298,35 @@ struct GDScriptUtilityFunctionsDefinitions {
for (int i = 0; i < sub.get_name_count(); i++) {
gdscr = gdscr->subclasses[sub.get_name(i)];
- if (!gdscr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- *r_ret = Variant();
- *r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
- return;
- }
+ VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, !gdscr.is_valid(), RTR("Invalid instance dictionary (invalid subclasses)."));
}
- *r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
+ *r_ret = gdscr->_new(nullptr, -1 /* skip initializer */, r_error);
if (r_error.error != Callable::CallError::CALL_OK) {
*r_ret = RTR("Cannot instantiate GDScript class.");
return;
}
- GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
- Ref<GDScript> gd_ref = ins->get_script();
+ GDScriptInstance *inst = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
+ Ref<GDScript> gd_ref = inst->get_script();
for (KeyValue<StringName, GDScript::MemberInfo> &E : gd_ref->member_indices) {
if (d.has(E.key)) {
- ins->members.write[E.value.index] = d[E.key];
+ inst->members.write[E.value.index] = d[E.key];
}
}
}
static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (p_arg_count < 3) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.expected = 3;
- *r_ret = Variant();
- return;
- }
- if (p_arg_count > 4) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.expected = 4;
- *r_ret = Variant();
- return;
- }
-
- VALIDATE_ARG_INT(0);
- VALIDATE_ARG_INT(1);
- VALIDATE_ARG_INT(2);
+ DEBUG_VALIDATE_ARG_COUNT(3, 4);
+ DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
+ DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT);
+ DEBUG_VALIDATE_ARG_TYPE(2, Variant::INT);
Color color((int64_t)*p_args[0] / 255.0f, (int64_t)*p_args[1] / 255.0f, (int64_t)*p_args[2] / 255.0f);
if (p_arg_count == 4) {
- VALIDATE_ARG_INT(3);
+ DEBUG_VALIDATE_ARG_TYPE(3, Variant::INT);
color.a = (int64_t)*p_args[3] / 255.0f;
}
@@ -437,7 +353,8 @@ struct GDScriptUtilityFunctionsDefinitions {
}
static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(0);
+ DEBUG_VALIDATE_ARG_COUNT(0, 0);
+
if (Thread::get_caller_id() != Thread::get_main_id()) {
print_line("Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()));
return;
@@ -451,7 +368,8 @@ struct GDScriptUtilityFunctionsDefinitions {
}
static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(0);
+ DEBUG_VALIDATE_ARG_COUNT(0, 0);
+
if (Thread::get_caller_id() != Thread::get_main_id()) {
*r_ret = TypedArray<Dictionary>();
return;
@@ -470,7 +388,7 @@ struct GDScriptUtilityFunctionsDefinitions {
}
static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(1);
+ DEBUG_VALIDATE_ARG_COUNT(1, 1);
switch (p_args[0]->get_type()) {
case Variant::STRING:
case Variant::STRING_NAME: {
@@ -526,56 +444,34 @@ struct GDScriptUtilityFunctionsDefinitions {
*r_ret = d.size();
} break;
default: {
+ *r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type()));
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::NIL;
- *r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type()));
- }
+ } break;
}
}
static inline void is_instance_of(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- VALIDATE_ARG_COUNT(2);
+ DEBUG_VALIDATE_ARG_COUNT(2, 2);
if (p_args[1]->get_type() == Variant::INT) {
int builtin_type = *p_args[1];
- if (builtin_type < 0 || builtin_type >= Variant::VARIANT_MAX) {
- *r_ret = RTR("Invalid type argument for is_instance_of(), use TYPE_* constants for built-in types.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::NIL;
- return;
- }
+ DEBUG_VALIDATE_ARG_CUSTOM(1, Variant::NIL, builtin_type < 0 || builtin_type >= Variant::VARIANT_MAX,
+ RTR("Invalid type argument for is_instance_of(), use TYPE_* constants for built-in types."));
*r_ret = p_args[0]->get_type() == builtin_type;
return;
}
bool was_type_freed = false;
Object *type_object = p_args[1]->get_validated_object_with_check(was_type_freed);
- if (was_type_freed) {
- *r_ret = RTR("Type argument is a previously freed instance.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::NIL;
- return;
- }
- if (!type_object) {
- *r_ret = RTR("Invalid type argument for is_instance_of(), should be a TYPE_* constant, a class or a script.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::NIL;
- return;
- }
+ VALIDATE_ARG_CUSTOM(1, Variant::NIL, was_type_freed, RTR("Type argument is a previously freed instance."));
+ VALIDATE_ARG_CUSTOM(1, Variant::NIL, !type_object,
+ RTR("Invalid type argument for is_instance_of(), should be a TYPE_* constant, a class or a script."));
bool was_value_freed = false;
Object *value_object = p_args[0]->get_validated_object_with_check(was_value_freed);
- if (was_value_freed) {
- *r_ret = RTR("Value argument is a previously freed instance.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::NIL;
- return;
- }
+ VALIDATE_ARG_CUSTOM(0, Variant::NIL, was_value_freed, RTR("Value argument is a previously freed instance."));
if (!value_object) {
*r_ret = false;
return;
@@ -620,113 +516,77 @@ struct GDScriptUtilityFunctionInfo {
static OAHashMap<StringName, GDScriptUtilityFunctionInfo> utility_function_table;
static List<StringName> utility_function_name_table;
-static void _register_function(const String &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) {
- StringName sname(p_name);
-
- ERR_FAIL_COND(utility_function_table.has(sname));
+static void _register_function(const StringName &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) {
+ ERR_FAIL_COND(utility_function_table.has(p_name));
GDScriptUtilityFunctionInfo function;
function.function = p_function;
function.info = p_method_info;
function.is_constant = p_is_const;
- utility_function_table.insert(sname, function);
- utility_function_name_table.push_back(sname);
+ utility_function_table.insert(p_name, function);
+ utility_function_name_table.push_back(p_name);
}
-#define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...) \
+#define REGISTER_FUNC(m_func, m_is_const, m_return, m_args, m_is_vararg, m_default_args) \
{ \
String name(#m_func); \
if (name.begins_with("_")) { \
- name = name.substr(1, name.length() - 1); \
+ name = name.substr(1); \
} \
- MethodInfo info = MethodInfo(name, __VA_ARGS__); \
- info.return_val.type = m_return_type; \
- _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
- }
-
-#define REGISTER_FUNC_NO_ARGS(m_func, m_is_const, m_return_type) \
- { \
- String name(#m_func); \
- if (name.begins_with("_")) { \
- name = name.substr(1, name.length() - 1); \
+ MethodInfo info = m_args; \
+ info.name = name; \
+ info.return_val = m_return; \
+ info.default_arguments = m_default_args; \
+ if (m_is_vararg) { \
+ info.flags |= METHOD_FLAG_VARARG; \
} \
- MethodInfo info = MethodInfo(name); \
- info.return_val.type = m_return_type; \
_register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
}
-#define REGISTER_VARARG_FUNC(m_func, m_is_const, m_return_type) \
- { \
- String name(#m_func); \
- if (name.begins_with("_")) { \
- name = name.substr(1, name.length() - 1); \
- } \
- MethodInfo info = MethodInfo(name); \
- info.return_val.type = m_return_type; \
- info.flags |= METHOD_FLAG_VARARG; \
- _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
- }
+#define RET(m_type) \
+ PropertyInfo(Variant::m_type, "")
-#define REGISTER_VARIANT_FUNC(m_func, m_is_const, ...) \
- { \
- String name(#m_func); \
- if (name.begins_with("_")) { \
- name = name.substr(1, name.length() - 1); \
- } \
- MethodInfo info = MethodInfo(name, __VA_ARGS__); \
- info.return_val.type = Variant::NIL; \
- info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; \
- _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
- }
+#define RETVAR \
+ PropertyInfo(Variant::NIL, "", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)
-#define REGISTER_CLASS_FUNC(m_func, m_is_const, m_return_type, ...) \
- { \
- String name(#m_func); \
- if (name.begins_with("_")) { \
- name = name.substr(1, name.length() - 1); \
- } \
- MethodInfo info = MethodInfo(name, __VA_ARGS__); \
- info.return_val.type = Variant::OBJECT; \
- info.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; \
- info.return_val.class_name = m_return_type; \
- _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
- }
+#define RETCLS(m_class) \
+ PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, m_class)
-#define REGISTER_FUNC_DEF(m_func, m_is_const, m_default, m_return_type, ...) \
- { \
- String name(#m_func); \
- if (name.begins_with("_")) { \
- name = name.substr(1, name.length() - 1); \
- } \
- MethodInfo info = MethodInfo(name, __VA_ARGS__); \
- info.return_val.type = m_return_type; \
- info.default_arguments.push_back(m_default); \
- _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
- }
+#define NOARGS \
+ MethodInfo()
+
+#define ARGS(...) \
+ MethodInfo("", __VA_ARGS__)
#define ARG(m_name, m_type) \
- PropertyInfo(m_type, m_name)
+ PropertyInfo(Variant::m_type, m_name)
+
+#define ARGVAR(m_name) \
+ PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)
-#define VARARG(m_name) \
- PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)
+#define ARGTYPE(m_name) \
+ PropertyInfo(Variant::INT, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "Variant.Type")
void GDScriptUtilityFunctions::register_functions() {
+ /* clang-format off */
#ifndef DISABLE_DEPRECATED
- REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT));
+ REGISTER_FUNC( convert, true, RETVAR, ARGS( ARGVAR("what"), ARGTYPE("type") ), false, varray( ));
#endif // DISABLE_DEPRECATED
- REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME));
- REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT));
- REGISTER_VARARG_FUNC(range, false, Variant::ARRAY);
- REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING));
- REGISTER_FUNC(inst_to_dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
- REGISTER_FUNC(dict_to_inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY));
- REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8", Variant::INT), ARG("g8", Variant::INT), ARG("b8", Variant::INT), ARG("a8", Variant::INT));
- REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL);
- REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL);
- REGISTER_FUNC_NO_ARGS(get_stack, false, Variant::ARRAY);
- REGISTER_FUNC(len, true, Variant::INT, VARARG("var"));
- REGISTER_FUNC(is_instance_of, true, Variant::BOOL, VARARG("value"), VARARG("type"));
+ REGISTER_FUNC( type_exists, true, RET(BOOL), ARGS( ARG("type", STRING_NAME) ), false, varray( ));
+ REGISTER_FUNC( _char, true, RET(STRING), ARGS( ARG("char", INT) ), false, varray( ));
+ REGISTER_FUNC( range, false, RET(ARRAY), NOARGS, true, varray( ));
+ REGISTER_FUNC( load, false, RETCLS("Resource"), ARGS( ARG("path", STRING) ), false, varray( ));
+ REGISTER_FUNC( inst_to_dict, false, RET(DICTIONARY), ARGS( ARG("instance", OBJECT) ), false, varray( ));
+ REGISTER_FUNC( dict_to_inst, false, RET(OBJECT), ARGS( ARG("dictionary", DICTIONARY) ), false, varray( ));
+ REGISTER_FUNC( Color8, true, RET(COLOR), ARGS( ARG("r8", INT), ARG("g8", INT),
+ ARG("b8", INT), ARG("a8", INT) ), false, varray( 255 ));
+ REGISTER_FUNC( print_debug, false, RET(NIL), NOARGS, true, varray( ));
+ REGISTER_FUNC( print_stack, false, RET(NIL), NOARGS, false, varray( ));
+ REGISTER_FUNC( get_stack, false, RET(ARRAY), NOARGS, false, varray( ));
+ REGISTER_FUNC( len, true, RET(INT), ARGS( ARGVAR("var") ), false, varray( ));
+ REGISTER_FUNC( is_instance_of, true, RET(BOOL), ARGS( ARGVAR("value"), ARGVAR("type") ), false, varray( ));
+ /* clang-format on */
}
void GDScriptUtilityFunctions::unregister_functions() {
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index a68cf258d5..0c96928776 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -399,32 +399,36 @@ void (*type_init_function_table[])(Variant *) = {
#define OPCODES_OUT \
OPSOUT:
#define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
+
#ifdef DEBUG_ENABLED
#define DISPATCH_OPCODE \
last_opcode = _code_ptr[ip]; \
goto *switch_table_ops[last_opcode]
-#else
+#else // !DEBUG_ENABLED
#define DISPATCH_OPCODE goto *switch_table_ops[_code_ptr[ip]]
-#endif
+#endif // DEBUG_ENABLED
+
#define OPCODE_BREAK goto OPSEXIT
#define OPCODE_OUT goto OPSOUT
-#else
+#else // !(defined(__GNUC__) || defined(__clang__))
#define OPCODES_TABLE
#define OPCODE(m_op) case m_op:
#define OPCODE_WHILE(m_test) while (m_test)
#define OPCODES_END
#define OPCODES_OUT
#define DISPATCH_OPCODE continue
+
#ifdef _MSC_VER
#define OPCODE_SWITCH(m_test) \
__assume(m_test <= OPCODE_END); \
switch (m_test)
-#else
+#else // !_MSC_VER
#define OPCODE_SWITCH(m_test) switch (m_test)
-#endif
+#endif // _MSC_VER
+
#define OPCODE_BREAK break
#define OPCODE_OUT break
-#endif
+#endif // defined(__GNUC__) || defined(__clang__)
// Helpers for VariantInternal methods in macros.
#define OP_GET_BOOL get_bool
@@ -665,7 +669,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_BREAK; \
}
-#else
+#else // !DEBUG_ENABLED
#define GD_ERR_BREAK(m_cond)
#define CHECK_SPACE(m_space)
@@ -678,7 +682,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_BREAK; \
}
-#endif
+#endif // DEBUG_ENABLED
#define LOAD_INSTRUCTION_ARGS \
int instr_arg_count = _code_ptr[ip + 1]; \
@@ -1967,7 +1971,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
err_text = _get_call_error("function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs, temp_ret, err);
OPCODE_BREAK;
}
-#endif
+#endif // DEBUG_ENABLED
ip += 3;
}
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript_utility_implicit_conversion.gd b/modules/gdscript/tests/scripts/runtime/features/gdscript_utility_implicit_conversion.gd
new file mode 100644
index 0000000000..59bdb6eceb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript_utility_implicit_conversion.gd
@@ -0,0 +1,12 @@
+func test():
+ const COLOR = Color8(255, 0.0, false)
+ var false_value := false
+ @warning_ignore("narrowing_conversion")
+ var color = Color8(255, 0.0, false_value)
+ print(var_to_str(COLOR))
+ print(var_to_str(color))
+
+ var string := "Node"
+ var string_name := &"Node"
+ print(type_exists(string))
+ print(type_exists(string_name))
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript_utility_implicit_conversion.out b/modules/gdscript/tests/scripts/runtime/features/gdscript_utility_implicit_conversion.out
new file mode 100644
index 0000000000..00913faa49
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript_utility_implicit_conversion.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+Color(1, 0, 0, 1)
+Color(1, 0, 0, 1)
+true
+true
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index dd6a921440..48c87bcd59 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -26,7 +26,7 @@ elif env["platform"] == "linuxbsd":
env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])
if env["wayland"]:
- env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_WAYLAND"])
+ env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_EGL"])
# FIXME: Review what needs to be set for Android and macOS.
env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
index ffe3662e1e..9d08d864ed 100644
--- a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
@@ -66,6 +66,9 @@ HashMap<String, bool *> OpenXROpenGLExtension::get_requested_extensions() {
#else
request_extensions[XR_KHR_OPENGL_ENABLE_EXTENSION_NAME] = nullptr;
#endif
+#if defined(LINUXBSD_ENABLED) && defined(EGL_ENABLED)
+ request_extensions[XR_MNDX_EGL_ENABLE_EXTENSION_NAME] = &egl_extension_enabled;
+#endif
return request_extensions;
}
@@ -130,9 +133,14 @@ bool OpenXROpenGLExtension::check_graphics_api_support(XrVersion p_desired_versi
XrGraphicsBindingOpenGLWin32KHR OpenXROpenGLExtension::graphics_binding_gl;
#elif defined(ANDROID_ENABLED)
XrGraphicsBindingOpenGLESAndroidKHR OpenXROpenGLExtension::graphics_binding_gl;
-#elif defined(X11_ENABLED)
+#elif defined(LINUXBSD_ENABLED)
+#ifdef X11_ENABLED
XrGraphicsBindingOpenGLXlibKHR OpenXROpenGLExtension::graphics_binding_gl;
#endif
+#ifdef EGL_ENABLED
+XrGraphicsBindingEGLMNDX OpenXROpenGLExtension::graphics_binding_egl;
+#endif
+#endif
void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
XrVersion desired_version = XR_MAKE_VERSION(3, 3, 0);
@@ -144,10 +152,6 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
DisplayServer *display_server = DisplayServer::get_singleton();
-#ifdef WAYLAND_ENABLED
- ERR_FAIL_COND_V_MSG(display_server->get_name() == "Wayland", p_next_pointer, "OpenXR is not yet supported on OpenGL Wayland.");
-#endif
-
#ifdef WIN32
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR,
graphics_binding_gl.next = p_next_pointer;
@@ -161,7 +165,23 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
graphics_binding_gl.display = (void *)display_server->window_get_native_handle(DisplayServer::DISPLAY_HANDLE);
graphics_binding_gl.config = (EGLConfig)0; // https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/master/src/tests/hello_xr/graphicsplugin_opengles.cpp#L122
graphics_binding_gl.context = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
-#elif defined(X11_ENABLED)
+#else
+#if defined(EGL_ENABLED) && defined(WAYLAND_ENABLED)
+ if (display_server->get_name() == "Wayland") {
+ ERR_FAIL_COND_V_MSG(!egl_extension_enabled, p_next_pointer, "OpenXR cannot initialize on Wayland without the XR_MNDX_egl_enable extension.");
+
+ graphics_binding_egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX;
+ graphics_binding_egl.next = p_next_pointer;
+
+ graphics_binding_egl.getProcAddress = eglGetProcAddress;
+ graphics_binding_egl.display = (void *)display_server->window_get_native_handle(DisplayServer::EGL_DISPLAY);
+ graphics_binding_egl.config = (void *)display_server->window_get_native_handle(DisplayServer::EGL_CONFIG);
+ graphics_binding_egl.context = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
+
+ return &graphics_binding_egl;
+ }
+#endif
+#if defined(X11_ENABLED)
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
graphics_binding_gl.next = p_next_pointer;
@@ -177,8 +197,13 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
graphics_binding_gl.visualid = 0;
graphics_binding_gl.glxFBConfig = 0;
#endif
+#endif
+#if defined(WIN32) || defined(ANDROID_ENABLED) || defined(X11_ENABLED)
return &graphics_binding_gl;
+#else
+ return p_next_pointer;
+#endif
}
void OpenXROpenGLExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.h b/modules/openxr/extensions/platform/openxr_opengl_extension.h
index 57a9fc7d31..de6c2ba292 100644
--- a/modules/openxr/extensions/platform/openxr_opengl_extension.h
+++ b/modules/openxr/extensions/platform/openxr_opengl_extension.h
@@ -66,9 +66,18 @@ private:
static XrGraphicsBindingOpenGLWin32KHR graphics_binding_gl;
#elif defined(ANDROID_ENABLED)
static XrGraphicsBindingOpenGLESAndroidKHR graphics_binding_gl;
-#else // Linux/X11
+#elif defined(LINUXBSD_ENABLED)
+#ifdef X11_ENABLED
static XrGraphicsBindingOpenGLXlibKHR graphics_binding_gl;
#endif
+#ifdef EGL_ENABLED
+ static XrGraphicsBindingEGLMNDX graphics_binding_egl;
+
+ bool egl_extension_enabled = false;
+#endif
+#else
+#error "OpenXR with OpenGL isn't supported on this platform"
+#endif
struct SwapchainGraphicsData {
bool is_multiview;
diff --git a/modules/openxr/openxr_platform_inc.h b/modules/openxr/openxr_platform_inc.h
index 4d29d186ef..93c8f6fb18 100644
--- a/modules/openxr/openxr_platform_inc.h
+++ b/modules/openxr/openxr_platform_inc.h
@@ -51,6 +51,13 @@
#else
#define XR_USE_GRAPHICS_API_OPENGL
#endif // ANDROID_ENABLED
+#if defined(LINUXBSD_ENABLED) && defined(EGL_ENABLED)
+#ifdef GLAD_ENABLED
+#include "thirdparty/glad/glad/egl.h"
+#else
+#include <EGL/egl.h>
+#endif // GLAD_ENABLED
+#endif // defined(LINUXBSD_ENABLED) && defined(EGL_ENABLED)
#ifdef X11_ENABLED
#define GL_GLEXT_PROTOTYPES 1
#define GL3_PROTOTYPES 1
diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp
index 60232f01fe..d0bf88a7b1 100644
--- a/modules/openxr/scene/openxr_composition_layer.cpp
+++ b/modules/openxr/scene/openxr_composition_layer.cpp
@@ -58,6 +58,10 @@ OpenXRCompositionLayer::OpenXRCompositionLayer(XrCompositionLayerBaseHeader *p_c
openxr_api = OpenXRAPI::get_singleton();
composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton();
+ if (openxr_api) {
+ openxr_session_running = openxr_api->is_running();
+ }
+
Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
if (openxr_interface.is_valid()) {
openxr_interface->connect("session_begun", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_begun));
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index cda181ce46..c0f780672a 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -183,17 +183,7 @@ void RaycastOcclusionCull::RaycastHZBuffer::sort_rays(const Vector3 &p_camera_di
}
int k = tile_i * TILE_SIZE + tile_j;
int tile_index = i * tile_grid_size.x + j;
- float d = camera_rays[tile_index].ray.tfar[k];
-
- if (!p_orthogonal) {
- const float &dir_x = camera_rays[tile_index].ray.dir_x[k];
- const float &dir_y = camera_rays[tile_index].ray.dir_y[k];
- const float &dir_z = camera_rays[tile_index].ray.dir_z[k];
- float cos_theta = p_camera_dir.x * dir_x + p_camera_dir.y * dir_y + p_camera_dir.z * dir_z;
- d *= cos_theta;
- }
-
- mips[0][y * buffer_size.x + x] = d;
+ mips[0][y * buffer_size.x + x] = camera_rays[tile_index].ray.tfar[k];
}
}
}
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 233e74364f..937bdbaa07 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -5,6 +5,7 @@ import sys
from typing import TYPE_CHECKING
from methods import print_error, print_warning
+from platform_methods import validate_arch
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -98,12 +99,7 @@ def install_ndk_if_needed(env: "SConsEnvironment"):
def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
- if env["arch"] not in supported_arches:
- print_error(
- 'Unsupported CPU architecture "%s" for Android. Supported architectures are: %s.'
- % (env["arch"], ", ".join(supported_arches))
- )
- sys.exit(255)
+ validate_arch(env["arch"], get_name(), supported_arches)
if get_min_sdk_version(env["ndk_platform"]) < get_min_target_api():
print_warning(
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 77019ef0d0..2fb54b95be 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -391,6 +391,14 @@ int64_t DisplayServerAndroid::window_get_native_handle(HandleType p_handle_type,
}
return 0;
}
+ case EGL_DISPLAY: {
+ // @todo Find a way to get this from the Java side.
+ return 0;
+ }
+ case EGL_CONFIG: {
+ // @todo Find a way to get this from the Java side.
+ return 0;
+ }
#endif
default: {
return 0;
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 317dbd3f4a..0f7f938852 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -3,6 +3,7 @@ import sys
from typing import TYPE_CHECKING
from methods import detect_darwin_sdk_path, print_error, print_warning
+from platform_methods import validate_arch
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -60,12 +61,7 @@ def get_flags():
def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
- if env["arch"] not in supported_arches:
- print_error(
- 'Unsupported CPU architecture "%s" for iOS. Supported architectures are: %s.'
- % (env["arch"], ", ".join(supported_arches))
- )
- sys.exit(255)
+ validate_arch(env["arch"], get_name(), supported_arches)
## LTO
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 961167c036..85e7472c02 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -4,7 +4,7 @@ import sys
from typing import TYPE_CHECKING
from methods import get_compiler_version, print_error, print_warning, using_gcc
-from platform_methods import detect_arch
+from platform_methods import detect_arch, validate_arch
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -74,12 +74,7 @@ def get_flags():
def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"]
- if env["arch"] not in supported_arches:
- print_error(
- 'Unsupported CPU architecture "%s" for Linux / *BSD. Supported architectures are: %s.'
- % (env["arch"], ", ".join(supported_arches))
- )
- sys.exit(255)
+ validate_arch(env["arch"], get_name(), supported_arches)
## Build type
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index da50a7061a..e1cc9d4ad2 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -629,6 +629,18 @@ int64_t DisplayServerWayland::window_get_native_handle(HandleType p_handle_type,
}
return 0;
} break;
+ case EGL_DISPLAY: {
+ if (egl_manager) {
+ return (int64_t)egl_manager->get_display(p_window);
+ }
+ return 0;
+ }
+ case EGL_CONFIG: {
+ if (egl_manager) {
+ return (int64_t)egl_manager->get_config(p_window);
+ }
+ return 0;
+ }
#endif // GLES3_ENABLED
default: {
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 5745dd3347..e0e5405c6f 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -1863,6 +1863,18 @@ int64_t DisplayServerX11::window_get_native_handle(HandleType p_handle_type, Win
}
return 0;
}
+ case EGL_DISPLAY: {
+ if (gl_manager_egl) {
+ return (int64_t)gl_manager_egl->get_display(p_window);
+ }
+ return 0;
+ }
+ case EGL_CONFIG: {
+ if (gl_manager_egl) {
+ return (int64_t)gl_manager_egl->get_config(p_window);
+ }
+ return 0;
+ }
#endif
default: {
return 0;
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index a8968b592e..cab91fd33c 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -3,7 +3,7 @@ import sys
from typing import TYPE_CHECKING
from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error, print_warning
-from platform_methods import detect_arch, detect_mvk
+from platform_methods import detect_arch, detect_mvk, validate_arch
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -68,12 +68,7 @@ def get_flags():
def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
- if env["arch"] not in supported_arches:
- print_error(
- 'Unsupported CPU architecture "%s" for macOS. Supported architectures are: %s.'
- % (env["arch"], ", ".join(supported_arches))
- )
- sys.exit(255)
+ validate_arch(env["arch"], get_name(), supported_arches)
## Build type
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 41b4445516..530ce188b9 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -2678,6 +2678,18 @@ int64_t DisplayServerMacOS::window_get_native_handle(HandleType p_handle_type, W
}
return 0;
}
+ case EGL_DISPLAY: {
+ if (gl_manager_angle) {
+ return (int64_t)gl_manager_angle->get_display(p_window);
+ }
+ return 0;
+ }
+ case EGL_CONFIG: {
+ if (gl_manager_angle) {
+ return (int64_t)gl_manager_angle->get_config(p_window);
+ }
+ return 0;
+ }
#endif
default: {
return 0;
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 1847cf6a6c..f4a6073dbd 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -14,6 +14,7 @@ from emscripten_helpers import (
from SCons.Util import WhereIs
from methods import get_compiler_version, print_error, print_warning
+from platform_methods import validate_arch
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -86,12 +87,7 @@ def get_flags():
def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["wasm32"]
- if env["arch"] not in supported_arches:
- print_error(
- 'Unsupported CPU architecture "%s" for Web. Supported architectures are: %s.'
- % (env["arch"], ", ".join(supported_arches))
- )
- sys.exit(255)
+ validate_arch(env["arch"], get_name(), supported_arches)
try:
env["initial_memory"] = int(env["initial_memory"])
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 608153ed0c..1312d795a7 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
import methods
from methods import print_error, print_warning
-from platform_methods import detect_arch
+from platform_methods import detect_arch, validate_arch
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -931,12 +931,7 @@ def configure_mingw(env: "SConsEnvironment"):
def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
- if env["arch"] not in supported_arches:
- print_error(
- 'Unsupported CPU architecture "%s" for Windows. Supported architectures are: %s.'
- % (env["arch"], ", ".join(supported_arches))
- )
- sys.exit(255)
+ validate_arch(env["arch"], get_name(), supported_arches)
# At this point the env has been set up with basic tools/compilers.
env.Prepend(CPPPATH=["#platform/windows"])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index d488fc298e..ed0edbca95 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -1667,6 +1667,18 @@ int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type,
}
return 0;
}
+ case EGL_DISPLAY: {
+ if (gl_manager_angle) {
+ return (int64_t)gl_manager_angle->get_display(p_window);
+ }
+ return 0;
+ }
+ case EGL_CONFIG: {
+ if (gl_manager_angle) {
+ return (int64_t)gl_manager_angle->get_config(p_window);
+ }
+ return 0;
+ }
#endif
default: {
return 0;
diff --git a/platform_methods.py b/platform_methods.py
index 2b157da22b..2c4eb0d1dd 100644
--- a/platform_methods.py
+++ b/platform_methods.py
@@ -1,6 +1,7 @@
import os
import platform
import subprocess
+import sys
import methods
@@ -40,6 +41,15 @@ def detect_arch():
return "x86_64"
+def validate_arch(arch, platform_name, supported_arches):
+ if arch not in supported_arches:
+ methods.print_error(
+ 'Unsupported CPU architecture "%s" for %s. Supported architectures are: %s.'
+ % (arch, platform_name, ", ".join(supported_arches))
+ )
+ sys.exit(255)
+
+
def get_build_version(short):
import version
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index c803e5ed78..f49994b9b9 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -33,6 +33,7 @@
#ifndef SKELETON_3D_H
#define SKELETON_3D_H
+#include "core/templates/a_hash_map.h"
#include "scene/3d/node_3d.h"
#include "scene/resources/3d/skin.h"
@@ -161,7 +162,7 @@ private:
bool process_order_dirty = false;
Vector<int> parentless_bones;
- HashMap<String, int> name_to_bone_index;
+ AHashMap<String, int> name_to_bone_index;
mutable StringName concatenated_bone_names = StringName();
void _update_bone_names() const;
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 5f4eb02e68..8538e07524 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -565,6 +565,7 @@ void AnimationMixer::_clear_caches() {
memdelete(K.value);
}
track_cache.clear();
+ animation_track_num_to_track_cashe.clear();
cache_valid = false;
capture_cache.clear();
@@ -924,6 +925,27 @@ bool AnimationMixer::_update_caches() {
idx++;
}
+ for (KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
+ K.value->blend_idx = track_map[K.value->path];
+ }
+
+ animation_track_num_to_track_cashe.clear();
+ LocalVector<TrackCache *> track_num_to_track_cashe;
+ for (const StringName &E : sname_list) {
+ Ref<Animation> anim = get_animation(E);
+ const Vector<Animation::Track *> tracks = anim->get_tracks();
+ track_num_to_track_cashe.resize(tracks.size());
+ for (int i = 0; i < tracks.size(); i++) {
+ TrackCache **track_ptr = track_cache.getptr(tracks[i]->thash);
+ if (track_ptr == nullptr) {
+ track_num_to_track_cashe[i] = nullptr;
+ } else {
+ track_num_to_track_cashe[i] = *track_ptr;
+ }
+ }
+ animation_track_num_to_track_cashe.insert(anim, track_num_to_track_cashe);
+ }
+
track_count = idx;
cache_valid = true;
@@ -948,7 +970,7 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) {
clear_animation_instances();
}
-Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) {
+Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant &p_value, ObjectID p_object_id, int p_object_sub_idx) {
#ifndef _3D_DISABLED
switch (p_anim->track_get_type(p_track)) {
case Animation::TYPE_POSITION_3D: {
@@ -1035,7 +1057,7 @@ void AnimationMixer::_blend_init() {
}
}
-bool AnimationMixer::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
+bool AnimationMixer::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
return true;
}
@@ -1086,26 +1108,30 @@ void AnimationMixer::_blend_calc_total_weight() {
real_t weight = ai.playback_info.weight;
const real_t *track_weights_ptr = ai.playback_info.track_weights.ptr();
int track_weights_count = ai.playback_info.track_weights.size();
- static LocalVector<Animation::TypeHash> processed_hashes;
+ ERR_CONTINUE_EDMSG(!animation_track_num_to_track_cashe.has(a), "No animation in cache.");
+ LocalVector<TrackCache *> &track_num_to_track_cashe = animation_track_num_to_track_cashe[a];
+ thread_local HashSet<Animation::TypeHash, HashHasher> processed_hashes;
processed_hashes.clear();
const Vector<Animation::Track *> tracks = a->get_tracks();
- for (const Animation::Track *animation_track : tracks) {
+ Animation::Track *const *tracks_ptr = tracks.ptr();
+ int count = tracks.size();
+ for (int i = 0; i < count; i++) {
+ Animation::Track *animation_track = tracks_ptr[i];
if (!animation_track->enabled) {
continue;
}
Animation::TypeHash thash = animation_track->thash;
- TrackCache **track_ptr = track_cache.getptr(thash);
- if (track_ptr == nullptr || processed_hashes.has(thash)) {
+ TrackCache *track = track_num_to_track_cashe[i];
+ if (track == nullptr || processed_hashes.has(thash)) {
// No path, but avoid error spamming.
// Or, there is the case different track type with same path; These can be distinguished by hash. So don't add the weight doubly.
continue;
}
- TrackCache *track = *track_ptr;
- int blend_idx = track_map[track->path];
+ int blend_idx = track->blend_idx;
ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count);
real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight;
track->total_weight += blend;
- processed_hashes.push_back(thash);
+ processed_hashes.insert(thash);
}
}
}
@@ -1132,6 +1158,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
#ifndef _3D_DISABLED
bool calc_root = !seeked || is_external_seeking;
#endif // _3D_DISABLED
+ ERR_CONTINUE_EDMSG(!animation_track_num_to_track_cashe.has(a), "No animation in cache.");
+ LocalVector<TrackCache *> &track_num_to_track_cashe = animation_track_num_to_track_cashe[a];
const Vector<Animation::Track *> tracks = a->get_tracks();
Animation::Track *const *tracks_ptr = tracks.ptr();
real_t a_length = a->get_length();
@@ -1141,15 +1169,11 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (!animation_track->enabled) {
continue;
}
- Animation::TypeHash thash = animation_track->thash;
- TrackCache **track_ptr = track_cache.getptr(thash);
- if (track_ptr == nullptr) {
+ TrackCache *track = track_num_to_track_cashe[i];
+ if (track == nullptr) {
continue; // No path, but avoid error spamming.
}
- TrackCache *track = *track_ptr;
- int *blend_idx_ptr = track_map.getptr(track->path);
- ERR_CONTINUE(blend_idx_ptr == nullptr);
- int blend_idx = *blend_idx_ptr;
+ int blend_idx = track->blend_idx;
ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count);
real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight;
if (!deterministic) {
@@ -1583,7 +1607,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
track_info.loop = a->get_loop_mode() != Animation::LOOP_NONE;
track_info.backward = backward;
track_info.use_blend = a->audio_track_is_use_blend(i);
- HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
+ AHashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
// Main process to fire key is started from here.
if (p_update_only) {
@@ -1852,7 +1876,7 @@ void AnimationMixer::_blend_apply() {
PlayingAudioTrackInfo &track_info = L.value;
float db = Math::linear_to_db(track_info.use_blend ? track_info.volume : 1.0);
LocalVector<int> erase_streams;
- HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
+ AHashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
for (const KeyValue<int, PlayingAudioStreamInfo> &M : map) {
PlayingAudioStreamInfo pasi = M.value;
@@ -2136,7 +2160,7 @@ void AnimationMixer::restore(const Ref<AnimatedValuesBackup> &p_backup) {
ERR_FAIL_COND(p_backup.is_null());
track_cache = p_backup->get_data();
_blend_apply();
- track_cache = HashMap<Animation::TypeHash, AnimationMixer::TrackCache *>();
+ track_cache = AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher>();
cache_valid = false;
}
@@ -2372,7 +2396,7 @@ AnimationMixer::AnimationMixer() {
AnimationMixer::~AnimationMixer() {
}
-void AnimatedValuesBackup::set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data) {
+void AnimatedValuesBackup::set_data(const AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> p_data) {
clear_data();
for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : p_data) {
@@ -2385,7 +2409,7 @@ void AnimatedValuesBackup::set_data(const HashMap<Animation::TypeHash, Animation
}
}
-HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() const {
+AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> AnimatedValuesBackup::get_data() const {
HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> ret;
for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : data) {
AnimationMixer::TrackCache *track = get_cache_copy(E.value);
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index 8cc76d8339..a2ad0e7ef1 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -33,6 +33,7 @@
#ifndef ANIMATION_MIXER_H
#define ANIMATION_MIXER_H
+#include "core/templates/a_hash_map.h"
#include "scene/animation/tween.h"
#include "scene/main/node.h"
#include "scene/resources/animation.h"
@@ -104,7 +105,7 @@ public:
protected:
/* ---- Data lists ---- */
LocalVector<AnimationLibraryData> animation_libraries;
- HashMap<StringName, AnimationData> animation_set; // HashMap<Library name + Animation name, AnimationData>
+ AHashMap<StringName, AnimationData> animation_set; // HashMap<Library name + Animation name, AnimationData>
TypedArray<StringName> _get_animation_library_list() const;
Vector<String> _get_animation_list() const {
@@ -150,6 +151,7 @@ protected:
uint64_t setup_pass = 0;
Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION;
NodePath path;
+ int blend_idx = -1;
ObjectID object_id;
real_t total_weight = 0.0;
@@ -271,7 +273,7 @@ protected:
// Audio track information for mixng and ending.
struct PlayingAudioTrackInfo {
- HashMap<int, PlayingAudioStreamInfo> stream_info;
+ AHashMap<int, PlayingAudioStreamInfo> stream_info;
double length = 0.0;
double time = 0.0;
real_t volume = 0.0;
@@ -310,7 +312,8 @@ protected:
};
RootMotionCache root_motion_cache;
- HashMap<Animation::TypeHash, TrackCache *> track_cache;
+ AHashMap<Animation::TypeHash, TrackCache *, HashHasher> track_cache;
+ AHashMap<Ref<Animation>, LocalVector<TrackCache *>> animation_track_num_to_track_cashe;
HashSet<TrackCache *> playing_caches;
Vector<Node *> playing_audio_stream_players;
@@ -326,7 +329,7 @@ protected:
/* ---- Blending processor ---- */
LocalVector<AnimationInstance> animation_instances;
- HashMap<NodePath, int> track_map;
+ AHashMap<NodePath, int> track_map;
int track_count = 0;
bool deterministic = false;
@@ -361,12 +364,12 @@ protected:
virtual void _process_animation(double p_delta, bool p_update_only = false);
// For post process with retrieved key value during blending.
- virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
+ virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant &p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int);
void _blend_init();
- virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map);
+ virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map);
virtual void _blend_capture(double p_delta);
void _blend_calc_total_weight(); // For undeterministic blending.
void _blend_process(double p_delta, bool p_update_only = false);
@@ -487,11 +490,11 @@ public:
class AnimatedValuesBackup : public RefCounted {
GDCLASS(AnimatedValuesBackup, RefCounted);
- HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> data;
+ AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> data;
public:
- void set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data);
- HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> get_data() const;
+ void set_data(const AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> p_data);
+ AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> get_data() const;
void clear_data();
AnimationMixer::TrackCache *get_cache_copy(AnimationMixer::TrackCache *p_cache) const;
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index eba2de69f2..33b8e314f3 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -1621,7 +1621,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachine::_process(const AnimationM
playback_new = playback_new->duplicate(); // Don't process original when testing.
}
- return playback_new->process(node_state.base_path, this, p_playback_info, p_test_only);
+ return playback_new->process(node_state.get_base_path(), this, p_playback_info, p_test_only);
}
String AnimationNodeStateMachine::get_caption() const {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 5defc53688..4179fb5806 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -135,7 +135,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> anim_names;
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
- HashMap<StringName, StringName>::ConstIterator F = animation_next_set.find(E.key);
+ AHashMap<StringName, StringName>::ConstIterator F = animation_next_set.find(E.key);
if (F && F->value != StringName()) {
anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
@@ -301,7 +301,7 @@ void AnimationPlayer::_blend_playback_data(double p_delta, bool p_started) {
}
}
-bool AnimationPlayer::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
+bool AnimationPlayer::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
if (!playback.current.from) {
_set_process(false);
return false;
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 37e463f2ac..659817bff5 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -54,7 +54,7 @@ public:
#endif // DISABLE_DEPRECATED
private:
- HashMap<StringName, StringName> animation_next_set; // For auto advance.
+ AHashMap<StringName, StringName> animation_next_set; // For auto advance.
float speed_scale = 1.0;
double default_blend_time = 0.0;
@@ -140,7 +140,7 @@ protected:
static void _bind_methods();
// Make animation instances.
- virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) override;
+ virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) override;
virtual void _blend_capture(double p_delta) override;
virtual void _blend_post_process() override;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 77abdd33f5..2bf680c66c 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -77,20 +77,34 @@ void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_val
if (process_state->is_testing) {
return;
}
+
+ const AHashMap<StringName, int>::Iterator it = property_cache.find(p_name);
+ if (it) {
+ process_state->tree->property_map.get_by_index(it->value).value.first = p_value;
+ return;
+ }
+
ERR_FAIL_COND(!process_state->tree->property_parent_map.has(node_state.base_path));
ERR_FAIL_COND(!process_state->tree->property_parent_map[node_state.base_path].has(p_name));
StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name];
-
- process_state->tree->property_map[path].first = p_value;
+ int idx = process_state->tree->property_map.get_index(path);
+ property_cache.insert_new(p_name, idx);
+ process_state->tree->property_map.get_by_index(idx).value.first = p_value;
}
Variant AnimationNode::get_parameter(const StringName &p_name) const {
ERR_FAIL_NULL_V(process_state, Variant());
+ const AHashMap<StringName, int>::ConstIterator it = property_cache.find(p_name);
+ if (it) {
+ return process_state->tree->property_map.get_by_index(it->value).value.first;
+ }
ERR_FAIL_COND_V(!process_state->tree->property_parent_map.has(node_state.base_path), Variant());
ERR_FAIL_COND_V(!process_state->tree->property_parent_map[node_state.base_path].has(p_name), Variant());
StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name];
- return process_state->tree->property_map[path].first;
+ int idx = process_state->tree->property_map.get_index(path);
+ property_cache.insert_new(p_name, idx);
+ return process_state->tree->property_map.get_by_index(idx).value.first;
}
void AnimationNode::set_node_time_info(const NodeTimeInfo &p_node_time_info) {
@@ -205,7 +219,7 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node
}
for (const KeyValue<NodePath, bool> &E : filter) {
- const HashMap<NodePath, int> &map = *process_state->track_map;
+ const AHashMap<NodePath, int> &map = *process_state->track_map;
if (!map.has(E.key)) {
continue;
}
@@ -294,7 +308,7 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node
// This process, which depends on p_sync is needed to process sync correctly in the case of
// that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync.
- p_node->node_state.base_path = new_path;
+ p_node->set_node_state_base_path(new_path);
p_node->node_state.parent = new_parent;
if (!p_playback_info.seeked && !p_sync && !any_valid) {
p_playback_info.delta = 0.0;
@@ -605,7 +619,7 @@ Ref<AnimationRootNode> AnimationTree::get_root_animation_node() const {
return root_animation_node;
}
-bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
+bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
_update_properties(); // If properties need updating, update them.
if (!root_animation_node.is_valid()) {
@@ -629,7 +643,7 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const
for (int i = 0; i < p_track_count; i++) {
src_blendsw[i] = 1.0; // By default all go to 1 for the root input.
}
- root_animation_node->node_state.base_path = SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data());
+ root_animation_node->set_node_state_base_path(SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data()));
root_animation_node->node_state.parent = nullptr;
}
@@ -734,7 +748,7 @@ void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringN
void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> p_node) {
ERR_FAIL_COND(p_node.is_null());
if (!property_parent_map.has(p_base_path)) {
- property_parent_map[p_base_path] = HashMap<StringName, StringName>();
+ property_parent_map[p_base_path] = AHashMap<StringName, StringName>();
}
if (!property_reference_map.has(p_node->get_instance_id())) {
property_reference_map[p_node->get_instance_id()] = p_base_path;
@@ -769,7 +783,7 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<A
pinfo.name = p_base_path + key;
properties.push_back(pinfo);
}
-
+ p_node->make_cache_dirty();
List<AnimationNode::ChildNode> children;
p_node->get_child_nodes(&children);
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index eff7637764..07b20f7684 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -62,7 +62,7 @@ public:
bool closable = false;
Vector<Input> inputs;
- HashMap<NodePath, bool> filter;
+ AHashMap<NodePath, bool> filter;
bool filter_enabled = false;
// To propagate information from upstream for use in estimation of playback progress.
@@ -99,22 +99,57 @@ public:
// Temporary state for blending process which needs to be stored in each AnimationNodes.
struct NodeState {
+ friend AnimationNode;
+
+ private:
StringName base_path;
+
+ public:
AnimationNode *parent = nullptr;
Vector<StringName> connections;
Vector<real_t> track_weights;
+
+ const StringName get_base_path() const {
+ return base_path;
+ }
+
} node_state;
// Temporary state for blending process which needs to be started in the AnimationTree, pass through the AnimationNodes, and then return to the AnimationTree.
struct ProcessState {
AnimationTree *tree = nullptr;
- const HashMap<NodePath, int> *track_map; // TODO: Is there a better way to manage filter/tracks?
+ const AHashMap<NodePath, int> *track_map; // TODO: Is there a better way to manage filter/tracks?
bool is_testing = false;
bool valid = false;
String invalid_reasons;
uint64_t last_pass = 0;
} *process_state = nullptr;
+private:
+ mutable AHashMap<StringName, int> property_cache;
+
+public:
+ void set_node_state_base_path(const StringName p_base_path) {
+ if (p_base_path != node_state.base_path) {
+ node_state.base_path = p_base_path;
+ make_cache_dirty();
+ }
+ }
+
+ void set_node_state_base_path(const String p_base_path) {
+ if (p_base_path != node_state.base_path) {
+ node_state.base_path = p_base_path;
+ make_cache_dirty();
+ }
+ }
+
+ const StringName get_node_state_base_path() const {
+ return node_state.get_base_path();
+ }
+
+ void make_cache_dirty() {
+ property_cache.clear();
+ }
Array _get_filters() const;
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
@@ -252,9 +287,9 @@ private:
friend class AnimationNode;
List<PropertyInfo> properties;
- HashMap<StringName, HashMap<StringName, StringName>> property_parent_map;
- HashMap<ObjectID, StringName> property_reference_map;
- HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
+ AHashMap<StringName, AHashMap<StringName, StringName>> property_parent_map;
+ AHashMap<ObjectID, StringName> property_reference_map;
+ AHashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
bool properties_dirty = true;
@@ -288,7 +323,7 @@ private:
virtual void _set_active(bool p_active) override;
// Make animation instances.
- virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) override;
+ virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) override;
#ifndef DISABLE_DEPRECATED
void _set_process_callback_bind_compat_80813(AnimationProcessCallback p_mode);
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 23ae1e9f63..f9dea97d04 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -298,19 +298,12 @@ void Button::_notification(int p_what) {
}
} break;
case DRAW_HOVER_PRESSED: {
- // Edge case for CheckButton and CheckBox.
- if (has_theme_stylebox("hover_pressed")) {
- if (has_theme_color(SNAME("font_hover_pressed_color"))) {
- font_color = theme_cache.font_hover_pressed_color;
- }
- if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
- icon_modulate_color = theme_cache.icon_hover_pressed_color;
- }
-
- break;
+ font_color = theme_cache.font_hover_pressed_color;
+ if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
+ icon_modulate_color = theme_cache.icon_hover_pressed_color;
}
- }
- [[fallthrough]];
+
+ } break;
case DRAW_PRESSED: {
if (has_theme_color(SNAME("font_pressed_color"))) {
font_color = theme_cache.font_pressed_color;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index ebe4872af7..3a5c057caf 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -1552,23 +1552,21 @@ void ColorPicker::_pick_button_pressed_legacy() {
picker_texture_rect = memnew(TextureRect);
picker_texture_rect->set_anchors_preset(Control::PRESET_FULL_RECT);
+ picker_texture_rect->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
picker_window->add_child(picker_texture_rect);
picker_texture_rect->set_default_cursor_shape(CURSOR_POINTING_HAND);
picker_texture_rect->connect(SceneStringName(gui_input), callable_mp(this, &ColorPicker::_picker_texture_input));
- picker_preview = memnew(Panel);
- picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
- picker_preview->set_mouse_filter(MOUSE_FILTER_IGNORE);
- picker_window->add_child(picker_preview);
-
picker_preview_label = memnew(Label);
- picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
+ picker_preview_label->set_anchors_preset(Control::PRESET_CENTER_TOP);
picker_preview_label->set_text(ETR("Color Picking active"));
- picker_preview->add_child(picker_preview_label);
- picker_preview_style_box = (Ref<StyleBoxFlat>)memnew(StyleBoxFlat);
+ picker_preview_style_box.instantiate();
picker_preview_style_box->set_bg_color(Color(1.0, 1.0, 1.0));
- picker_preview->add_theme_style_override(SceneStringName(panel), picker_preview_style_box);
+ picker_preview_style_box->set_content_margin_all(4.0);
+ picker_preview_label->add_theme_style_override(CoreStringName(normal), picker_preview_style_box);
+
+ picker_window->add_child(picker_preview_label);
}
Rect2i screen_rect;
@@ -1610,7 +1608,7 @@ void ColorPicker::_pick_button_pressed_legacy() {
}
picker_window->set_size(screen_rect.size);
- picker_preview->set_size(screen_rect.size / 10.0); // 10% of size in each axis.
+ picker_preview_label->set_custom_minimum_size(screen_rect.size / 10); // 10% of size in each axis.
picker_window->popup();
}
@@ -1633,7 +1631,7 @@ void ColorPicker::_picker_texture_input(const Ref<InputEvent> &p_event) {
Vector2 ofs = mev->get_position();
picker_color = img->get_pixel(ofs.x, ofs.y);
picker_preview_style_box->set_bg_color(picker_color);
- picker_preview_label->set_self_modulate(picker_color.get_luminance() < 0.5 ? Color(1.0f, 1.0f, 1.0f) : Color(0.0f, 0.0f, 0.0f));
+ picker_preview_label->add_theme_color_override(SceneStringName(font_color), picker_color.get_luminance() < 0.5 ? Color(1.0f, 1.0f, 1.0f) : Color(0.0f, 0.0f, 0.0f));
}
}
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 8ee1b9fb43..8383d8a8f4 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -132,7 +132,6 @@ private:
Popup *picker_window = nullptr;
// Legacy color picking.
TextureRect *picker_texture_rect = nullptr;
- Panel *picker_preview = nullptr;
Label *picker_preview_label = nullptr;
Ref<StyleBoxFlat> picker_preview_style_box;
Color picker_color;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index a71a083726..50d09fdc42 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -963,19 +963,17 @@ TreeItem *TreeItem::_get_prev_in_tree(bool p_wrap, bool p_include_invisible) {
if (!prev_item) {
current = current->parent;
- if (current == tree->root && tree->hide_root) {
- return nullptr;
- } else if (!current) {
- if (p_wrap) {
- current = this;
- TreeItem *temp = get_next_visible();
- while (temp) {
- current = temp;
- temp = temp->get_next_visible();
- }
- } else {
+ if (!current || (current == tree->root && tree->hide_root)) {
+ if (!p_wrap) {
return nullptr;
}
+ // Wrap around to the last visible item.
+ current = this;
+ TreeItem *temp = get_next_visible();
+ while (temp) {
+ current = temp;
+ temp = temp->get_next_visible();
+ }
}
} else {
current = prev_item;
diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp
index 31d69af3ea..d2f5bf9529 100644
--- a/scene/resources/2d/tile_set.cpp
+++ b/scene/resources/2d/tile_set.cpp
@@ -6482,9 +6482,9 @@ int TileData::get_terrain_set() const {
}
void TileData::set_terrain(int p_terrain) {
- ERR_FAIL_COND(terrain_set < 0);
ERR_FAIL_COND(p_terrain < -1);
- if (tile_set) {
+ ERR_FAIL_COND(terrain_set < 0 && p_terrain != -1);
+ if (tile_set && terrain_set >= 0) {
ERR_FAIL_COND(p_terrain >= tile_set->get_terrains_count(terrain_set));
}
terrain = p_terrain;
@@ -6497,9 +6497,9 @@ int TileData::get_terrain() const {
void TileData::set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) {
ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX);
- ERR_FAIL_COND(terrain_set < 0);
ERR_FAIL_COND(p_terrain_index < -1);
- if (tile_set) {
+ ERR_FAIL_COND(terrain_set < 0 && p_terrain_index != -1);
+ if (tile_set && terrain_set >= 0) {
ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set));
ERR_FAIL_COND(!is_valid_terrain_peering_bit(p_peering_bit));
}
diff --git a/scene/resources/style_box_flat.cpp b/scene/resources/style_box_flat.cpp
index 346c3e8594..c117e64dee 100644
--- a/scene/resources/style_box_flat.cpp
+++ b/scene/resources/style_box_flat.cpp
@@ -598,10 +598,10 @@ void StyleBoxFlat::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew");
ADD_GROUP("Border Width", "border_width_");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM);
ADD_GROUP("Border", "border_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color");
@@ -609,18 +609,18 @@ void StyleBoxFlat::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "border_blend"), "set_border_blend", "get_border_blend");
ADD_GROUP("Corner Radius", "corner_radius_");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT);
ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail");
ADD_GROUP("Expand Margins", "expand_margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 29c3185f6c..e2c01f82fe 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -1442,6 +1442,10 @@ uint64_t AudioServer::get_mixed_frames() const {
return mix_frames;
}
+String AudioServer::get_driver_name() const {
+ return AudioDriver::get_singleton()->get_name();
+}
+
void AudioServer::notify_listener_changed() {
for (CallbackItem *ci : listener_changed_callback_list) {
ci->callback(ci->userdata);
@@ -1949,6 +1953,8 @@ void AudioServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode);
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate);
+ ClassDB::bind_method(D_METHOD("get_driver_name"), &AudioServer::get_driver_name);
+
ClassDB::bind_method(D_METHOD("get_output_device_list"), &AudioServer::get_output_device_list);
ClassDB::bind_method(D_METHOD("get_output_device"), &AudioServer::get_output_device);
ClassDB::bind_method(D_METHOD("set_output_device", "name"), &AudioServer::set_output_device);
diff --git a/servers/audio_server.h b/servers/audio_server.h
index 97ce6feec6..58971fb1ca 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -429,6 +429,8 @@ public:
uint64_t get_mix_count() const;
uint64_t get_mixed_frames() const;
+ String get_driver_name() const;
+
void notify_listener_changed();
virtual void init();
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index b5511acb9e..8e1f42e427 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -1151,6 +1151,8 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(WINDOW_HANDLE);
BIND_ENUM_CONSTANT(WINDOW_VIEW);
BIND_ENUM_CONSTANT(OPENGL_CONTEXT);
+ BIND_ENUM_CONSTANT(EGL_DISPLAY);
+ BIND_ENUM_CONSTANT(EGL_CONFIG);
BIND_ENUM_CONSTANT(TTS_UTTERANCE_STARTED);
BIND_ENUM_CONSTANT(TTS_UTTERANCE_ENDED);
diff --git a/servers/display_server.h b/servers/display_server.h
index dc20337214..3f5248a2cd 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -84,6 +84,8 @@ public:
WINDOW_HANDLE,
WINDOW_VIEW,
OPENGL_CONTEXT,
+ EGL_DISPLAY,
+ EGL_CONFIG,
};
enum Context {
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 50003b3a6c..ccb6dd4dc5 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -612,6 +612,8 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
+ actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
+ actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["VIEWPORT_SIZE"] = "read_viewport_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
@@ -651,8 +653,6 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.renames["CUSTOM1"] = "custom1_attrib";
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
- actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
- actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index 4c14a02a22..91771d5454 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -523,6 +523,8 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
+ actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
+ actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["VIEWPORT_SIZE"] = "read_viewport_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
@@ -562,8 +564,6 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.renames["CUSTOM1"] = "custom1_attrib";
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
- actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
- actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
diff --git a/servers/rendering/renderer_scene_occlusion_cull.h b/servers/rendering/renderer_scene_occlusion_cull.h
index 0fbea9ae6d..ae833a7a32 100644
--- a/servers/rendering/renderer_scene_occlusion_cull.h
+++ b/servers/rendering/renderer_scene_occlusion_cull.h
@@ -74,7 +74,7 @@ public:
return false;
}
- float min_depth = -closest_point_view.z * 0.95f;
+ float min_depth = (closest_point - p_cam_position).length();
Vector2 rect_min = Vector2(FLT_MAX, FLT_MAX);
Vector2 rect_max = Vector2(FLT_MIN, FLT_MIN);
@@ -85,6 +85,10 @@ public:
Vector3 corner = Vector3(p_bounds[0] * c.x + p_bounds[3] * nc.x, p_bounds[1] * c.y + p_bounds[4] * nc.y, p_bounds[2] * c.z + p_bounds[5] * nc.z);
Vector3 view = p_cam_inv_transform.xform(corner);
+ if (p_cam_projection.is_orthogonal()) {
+ min_depth = MIN(min_depth, view.z);
+ }
+
Plane vp = Plane(view, 1.0);
Plane projected = p_cam_projection.xform4(vp);
diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp
index 4a7392a71c..af8781fab4 100644
--- a/servers/rendering/rendering_device_graph.cpp
+++ b/servers/rendering/rendering_device_graph.cpp
@@ -36,7 +36,6 @@
#define FORCE_FULL_ACCESS_BITS 0
#define PRINT_RESOURCE_TRACKER_TOTAL 0
#define PRINT_COMMAND_RECORDING 0
-#define INSERT_BREADCRUMBS 1
RenderingDeviceGraph::RenderingDeviceGraph() {
driver_honors_barriers = false;
@@ -835,7 +834,7 @@ void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedC
const RecordedDrawListCommand *draw_list_command = reinterpret_cast<const RecordedDrawListCommand *>(command);
const VectorView clear_values(draw_list_command->clear_values(), draw_list_command->clear_values_count);
-#if INSERT_BREADCRUMBS
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
driver->command_insert_breadcrumb(r_command_buffer, draw_list_command->breadcrumb);
#endif
driver->command_begin_render_pass(r_command_buffer, draw_list_command->render_pass, draw_list_command->framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values);
@@ -1418,7 +1417,9 @@ void RenderingDeviceGraph::add_buffer_update(RDD::BufferID p_dst, ResourceTracke
void RenderingDeviceGraph::add_compute_list_begin(RDD::BreadcrumbMarker p_phase, uint32_t p_breadcrumb_data) {
compute_instruction_list.clear();
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
compute_instruction_list.breadcrumb = p_breadcrumb_data | (p_phase & ((1 << 16) - 1));
+#endif
compute_instruction_list.index++;
}
@@ -1514,7 +1515,9 @@ void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass,
draw_instruction_list.render_pass = p_render_pass;
draw_instruction_list.framebuffer = p_framebuffer;
draw_instruction_list.region = p_region;
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
draw_instruction_list.breadcrumb = p_breadcrumb;
+#endif
draw_instruction_list.clear_values.resize(p_clear_values.size());
for (uint32_t i = 0; i < p_clear_values.size(); i++) {
draw_instruction_list.clear_values[i] = p_clear_values[i];
@@ -1725,7 +1728,9 @@ void RenderingDeviceGraph::add_draw_list_end() {
command->framebuffer = draw_instruction_list.framebuffer;
command->command_buffer_type = command_buffer_type;
command->region = draw_instruction_list.region;
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
command->breadcrumb = draw_instruction_list.breadcrumb;
+#endif
command->clear_values_count = draw_instruction_list.clear_values.size();
RDD::RenderPassClearValue *clear_values = command->clear_values();
diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h
index d7b1c2cb61..095c2eb5da 100644
--- a/servers/rendering/rendering_device_graph.h
+++ b/servers/rendering/rendering_device_graph.h
@@ -223,14 +223,18 @@ private:
};
struct ComputeInstructionList : InstructionList {
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
uint32_t breadcrumb;
+#endif
};
struct DrawInstructionList : InstructionList {
RDD::RenderPassID render_pass;
RDD::FramebufferID framebuffer;
Rect2i region;
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
uint32_t breadcrumb;
+#endif
LocalVector<RDD::RenderPassClearValue> clear_values;
};
@@ -319,7 +323,9 @@ private:
RDD::FramebufferID framebuffer;
RDD::CommandBufferType command_buffer_type;
Rect2i region;
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
uint32_t breadcrumb = 0;
+#endif
uint32_t clear_values_count = 0;
_FORCE_INLINE_ RDD::RenderPassClearValue *clear_values() {
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index 70a7bdfb08..c27c8f019e 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -65,6 +65,8 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3;
@@ -98,8 +100,6 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MODELVIEW_MATRIX"] = ShaderLanguage::TYPE_MAT4;
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MODELVIEW_NORMAL_MATRIX"] = ShaderLanguage::TYPE_MAT3;
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2);
- shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MAIN_CAM_INV_VIEW_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NODE_POSITION_WORLD"] = constt(ShaderLanguage::TYPE_VEC3);
@@ -161,9 +161,6 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_RIGHT"] = constt(ShaderLanguage::TYPE_INT);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["EYE_OFFSET"] = constt(ShaderLanguage::TYPE_VEC3);
- shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT);
-
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["MODEL_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["MODEL_NORMAL_MATRIX"] = constt(ShaderLanguage::TYPE_MAT3);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4);
@@ -205,8 +202,6 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ROUGHNESS"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["DIFFUSE_LIGHT"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["SPECULAR_LIGHT"] = ShaderLanguage::TYPE_VEC3;
- shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
- shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["light"].can_discard = true;
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 7733105c27..63b85b4b28 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -462,11 +462,27 @@ TEST_CASE("[String] Number to string") {
CHECK(String::num(-0.0) == "-0"); // Includes sign even for zero.
CHECK(String::num(3.141593) == "3.141593");
CHECK(String::num(3.141593, 3) == "3.142");
+ CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros.
CHECK(String::num_scientific(30000000) == "3e+07");
+
+ // String::num_int64 tests.
CHECK(String::num_int64(3141593) == "3141593");
+ CHECK(String::num_int64(-3141593) == "-3141593");
CHECK(String::num_int64(0xA141593, 16) == "a141593");
CHECK(String::num_int64(0xA141593, 16, true) == "A141593");
- CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros.
+ ERR_PRINT_OFF;
+ CHECK(String::num_int64(3141593, 1) == ""); // Invalid base < 2.
+ CHECK(String::num_int64(3141593, 37) == ""); // Invalid base > 36.
+ ERR_PRINT_ON;
+
+ // String::num_uint64 tests.
+ CHECK(String::num_uint64(4294967295) == "4294967295");
+ CHECK(String::num_uint64(0xF141593, 16) == "f141593");
+ CHECK(String::num_uint64(0xF141593, 16, true) == "F141593");
+ ERR_PRINT_OFF;
+ CHECK(String::num_uint64(4294967295, 1) == ""); // Invalid base < 2.
+ CHECK(String::num_uint64(4294967295, 37) == ""); // Invalid base > 36.
+ ERR_PRINT_ON;
// String::num_real tests.
CHECK(String::num_real(1.0) == "1.0");
diff --git a/tests/core/templates/test_a_hash_map.h b/tests/core/templates/test_a_hash_map.h
new file mode 100644
index 0000000000..19e5e06057
--- /dev/null
+++ b/tests/core/templates/test_a_hash_map.h
@@ -0,0 +1,297 @@
+/**************************************************************************/
+/* test_a_hash_map.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* 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 TEST_A_HASH_MAP_H
+#define TEST_A_HASH_MAP_H
+
+#include "core/templates/a_hash_map.h"
+
+#include "tests/test_macros.h"
+
+namespace TestAHashMap {
+
+TEST_CASE("[AHashMap] Insert element") {
+ AHashMap<int, int> map;
+ AHashMap<int, int>::Iterator e = map.insert(42, 84);
+
+ CHECK(e);
+ CHECK(e->key == 42);
+ CHECK(e->value == 84);
+ CHECK(map[42] == 84);
+ CHECK(map.has(42));
+ CHECK(map.find(42));
+}
+
+TEST_CASE("[AHashMap] Overwrite element") {
+ AHashMap<int, int> map;
+ map.insert(42, 84);
+ map.insert(42, 1234);
+
+ CHECK(map[42] == 1234);
+}
+
+TEST_CASE("[AHashMap] Erase via element") {
+ AHashMap<int, int> map;
+ AHashMap<int, int>::Iterator e = map.insert(42, 84);
+ map.remove(e);
+ CHECK(!map.has(42));
+ CHECK(!map.find(42));
+}
+
+TEST_CASE("[AHashMap] Erase via key") {
+ AHashMap<int, int> map;
+ map.insert(42, 84);
+ map.erase(42);
+ CHECK(!map.has(42));
+ CHECK(!map.find(42));
+}
+
+TEST_CASE("[AHashMap] Size") {
+ AHashMap<int, int> map;
+ map.insert(42, 84);
+ map.insert(123, 84);
+ map.insert(123, 84);
+ map.insert(0, 84);
+ map.insert(123485, 84);
+
+ CHECK(map.size() == 4);
+}
+
+TEST_CASE("[AHashMap] Iteration") {
+ AHashMap<int, int> map;
+
+ map.insert(42, 84);
+ map.insert(123, 12385);
+ map.insert(0, 12934);
+ map.insert(123485, 1238888);
+ map.insert(123, 111111);
+
+ Vector<Pair<int, int>> expected;
+ expected.push_back(Pair<int, int>(42, 84));
+ expected.push_back(Pair<int, int>(123, 111111));
+ expected.push_back(Pair<int, int>(0, 12934));
+ expected.push_back(Pair<int, int>(123485, 1238888));
+
+ int idx = 0;
+ for (const KeyValue<int, int> &E : map) {
+ CHECK(expected[idx] == Pair<int, int>(E.key, E.value));
+ idx++;
+ }
+
+ idx--;
+ for (AHashMap<int, int>::Iterator it = map.last(); it; --it) {
+ CHECK(expected[idx] == Pair<int, int>(it->key, it->value));
+ idx--;
+ }
+}
+
+TEST_CASE("[AHashMap] Const iteration") {
+ AHashMap<int, int> map;
+ map.insert(42, 84);
+ map.insert(123, 12385);
+ map.insert(0, 12934);
+ map.insert(123485, 1238888);
+ map.insert(123, 111111);
+
+ const AHashMap<int, int> const_map = map;
+
+ Vector<Pair<int, int>> expected;
+ expected.push_back(Pair<int, int>(42, 84));
+ expected.push_back(Pair<int, int>(123, 111111));
+ expected.push_back(Pair<int, int>(0, 12934));
+ expected.push_back(Pair<int, int>(123485, 1238888));
+ expected.push_back(Pair<int, int>(123, 111111));
+
+ int idx = 0;
+ for (const KeyValue<int, int> &E : const_map) {
+ CHECK(expected[idx] == Pair<int, int>(E.key, E.value));
+ idx++;
+ }
+
+ idx--;
+ for (AHashMap<int, int>::ConstIterator it = const_map.last(); it; --it) {
+ CHECK(expected[idx] == Pair<int, int>(it->key, it->value));
+ idx--;
+ }
+}
+
+TEST_CASE("[AHashMap] Replace key") {
+ AHashMap<int, int> map;
+ map.insert(42, 84);
+ map.insert(0, 12934);
+ CHECK(map.replace_key(0, 1));
+ CHECK(map.has(1));
+ CHECK(map[1] == 12934);
+}
+
+TEST_CASE("[AHashMap] Clear") {
+ AHashMap<int, int> map;
+ map.insert(42, 84);
+ map.insert(123, 12385);
+ map.insert(0, 12934);
+
+ map.clear();
+ CHECK(!map.has(42));
+ CHECK(map.size() == 0);
+ CHECK(map.is_empty());
+}
+
+TEST_CASE("[AHashMap] Get") {
+ AHashMap<int, int> map;
+ map.insert(42, 84);
+ map.insert(123, 12385);
+ map.insert(0, 12934);
+
+ CHECK(map.get(123) == 12385);
+ map.get(123) = 10;
+ CHECK(map.get(123) == 10);
+
+ CHECK(*map.getptr(0) == 12934);
+ *map.getptr(0) = 1;
+ CHECK(*map.getptr(0) == 1);
+
+ CHECK(map.get(42) == 84);
+ CHECK(map.getptr(-10) == nullptr);
+}
+
+TEST_CASE("[AHashMap] Insert, iterate and remove many elements") {
+ const int elem_max = 1234;
+ AHashMap<int, int> map;
+ for (int i = 0; i < elem_max; i++) {
+ map.insert(i, i);
+ }
+
+ //insert order should have been kept
+ int idx = 0;
+ for (auto &K : map) {
+ CHECK(idx == K.key);
+ CHECK(idx == K.value);
+ CHECK(map.has(idx));
+ idx++;
+ }
+
+ Vector<int> elems_still_valid;
+
+ for (int i = 0; i < elem_max; i++) {
+ if ((i % 5) == 0) {
+ map.erase(i);
+ } else {
+ elems_still_valid.push_back(i);
+ }
+ }
+
+ CHECK(elems_still_valid.size() == map.size());
+
+ for (int i = 0; i < elems_still_valid.size(); i++) {
+ CHECK(map.has(elems_still_valid[i]));
+ }
+}
+
+TEST_CASE("[AHashMap] Insert, iterate and remove many strings") {
+ const int elem_max = 432;
+ AHashMap<String, String> map;
+ for (int i = 0; i < elem_max; i++) {
+ map.insert(itos(i), itos(i));
+ }
+
+ //insert order should have been kept
+ int idx = 0;
+ for (auto &K : map) {
+ CHECK(itos(idx) == K.key);
+ CHECK(itos(idx) == K.value);
+ CHECK(map.has(itos(idx)));
+ idx++;
+ }
+
+ Vector<String> elems_still_valid;
+
+ for (int i = 0; i < elem_max; i++) {
+ if ((i % 5) == 0) {
+ map.erase(itos(i));
+ } else {
+ elems_still_valid.push_back(itos(i));
+ }
+ }
+
+ CHECK(elems_still_valid.size() == map.size());
+
+ for (int i = 0; i < elems_still_valid.size(); i++) {
+ CHECK(map.has(elems_still_valid[i]));
+ }
+
+ elems_still_valid.clear();
+}
+
+TEST_CASE("[AHashMap] Copy constructor") {
+ AHashMap<int, int> map0;
+ const uint32_t count = 5;
+ for (uint32_t i = 0; i < count; i++) {
+ map0.insert(i, i);
+ }
+ AHashMap<int, int> map1(map0);
+ CHECK(map0.size() == map1.size());
+ CHECK(map0.get_capacity() == map1.get_capacity());
+ CHECK(*map0.getptr(0) == *map1.getptr(0));
+}
+
+TEST_CASE("[AHashMap] Operator =") {
+ AHashMap<int, int> map0;
+ AHashMap<int, int> map1;
+ const uint32_t count = 5;
+ map1.insert(1234, 1234);
+ for (uint32_t i = 0; i < count; i++) {
+ map0.insert(i, i);
+ }
+ map1 = map0;
+ CHECK(map0.size() == map1.size());
+ CHECK(map0.get_capacity() == map1.get_capacity());
+ CHECK(*map0.getptr(0) == *map1.getptr(0));
+}
+
+TEST_CASE("[AHashMap] Array methods") {
+ AHashMap<int, int> map;
+ for (int i = 0; i < 100; i++) {
+ map.insert(100 - i, i);
+ }
+ for (int i = 0; i < 100; i++) {
+ CHECK(map.get_by_index(i).value == i);
+ }
+ int index = map.get_index(1);
+ CHECK(map.get_by_index(index).value == 99);
+ CHECK(map.erase_by_index(index));
+ CHECK(!map.erase_by_index(index));
+ CHECK(map.get_index(1) == -1);
+}
+
+} // namespace TestAHashMap
+
+#endif // TEST_A_HASH_MAP_H
diff --git a/tests/scene/test_tree.h b/tests/scene/test_tree.h
index 5fda4dea73..dc63cb6eb7 100644
--- a/tests/scene/test_tree.h
+++ b/tests/scene/test_tree.h
@@ -141,18 +141,30 @@ TEST_CASE("[SceneTree][Tree]") {
TreeItem *child1 = tree->create_item();
TreeItem *child2 = tree->create_item();
TreeItem *child3 = tree->create_item();
+ CHECK_EQ(root->get_next(), nullptr);
+ CHECK_EQ(root->get_next_visible(), child1);
+ CHECK_EQ(root->get_next_in_tree(), child1);
CHECK_EQ(child1->get_next(), child2);
+ CHECK_EQ(child1->get_next_visible(), child2);
CHECK_EQ(child1->get_next_in_tree(), child2);
CHECK_EQ(child2->get_next(), child3);
+ CHECK_EQ(child2->get_next_visible(), child3);
CHECK_EQ(child2->get_next_in_tree(), child3);
CHECK_EQ(child3->get_next(), nullptr);
+ CHECK_EQ(child3->get_next_visible(), nullptr);
CHECK_EQ(child3->get_next_in_tree(), nullptr);
+ CHECK_EQ(root->get_prev(), nullptr);
+ CHECK_EQ(root->get_prev_visible(), nullptr);
+ CHECK_EQ(root->get_prev_in_tree(), nullptr);
CHECK_EQ(child1->get_prev(), nullptr);
+ CHECK_EQ(child1->get_prev_visible(), root);
CHECK_EQ(child1->get_prev_in_tree(), root);
CHECK_EQ(child2->get_prev(), child1);
+ CHECK_EQ(child2->get_prev_visible(), child1);
CHECK_EQ(child2->get_prev_in_tree(), child1);
CHECK_EQ(child3->get_prev(), child2);
+ CHECK_EQ(child3->get_prev_visible(), child2);
CHECK_EQ(child3->get_prev_in_tree(), child2);
TreeItem *nested1 = tree->create_item(child2);
@@ -160,13 +172,127 @@ TEST_CASE("[SceneTree][Tree]") {
TreeItem *nested3 = tree->create_item(child2);
CHECK_EQ(child1->get_next(), child2);
+ CHECK_EQ(child1->get_next_visible(), child2);
CHECK_EQ(child1->get_next_in_tree(), child2);
CHECK_EQ(child2->get_next(), child3);
+ CHECK_EQ(child2->get_next_visible(), nested1);
CHECK_EQ(child2->get_next_in_tree(), nested1);
CHECK_EQ(child3->get_prev(), child2);
+ CHECK_EQ(child3->get_prev_visible(), nested3);
CHECK_EQ(child3->get_prev_in_tree(), nested3);
CHECK_EQ(nested1->get_prev_in_tree(), child2);
CHECK_EQ(nested1->get_next_in_tree(), nested2);
+ CHECK_EQ(nested3->get_next_in_tree(), child3);
+
+ memdelete(tree);
+ }
+
+ SUBCASE("[Tree] Previous and Next items with hide root.") {
+ Tree *tree = memnew(Tree);
+ tree->set_hide_root(true);
+ TreeItem *root = tree->create_item();
+
+ TreeItem *child1 = tree->create_item();
+ TreeItem *child2 = tree->create_item();
+ TreeItem *child3 = tree->create_item();
+ CHECK_EQ(root->get_next(), nullptr);
+ CHECK_EQ(root->get_next_visible(), child1);
+ CHECK_EQ(root->get_next_in_tree(), child1);
+ CHECK_EQ(child1->get_next(), child2);
+ CHECK_EQ(child1->get_next_visible(), child2);
+ CHECK_EQ(child1->get_next_in_tree(), child2);
+ CHECK_EQ(child2->get_next(), child3);
+ CHECK_EQ(child2->get_next_visible(), child3);
+ CHECK_EQ(child2->get_next_in_tree(), child3);
+ CHECK_EQ(child3->get_next(), nullptr);
+ CHECK_EQ(child3->get_next_visible(), nullptr);
+ CHECK_EQ(child3->get_next_in_tree(), nullptr);
+
+ CHECK_EQ(root->get_prev(), nullptr);
+ CHECK_EQ(root->get_prev_visible(), nullptr);
+ CHECK_EQ(root->get_prev_in_tree(), nullptr);
+ CHECK_EQ(child1->get_prev(), nullptr);
+ CHECK_EQ(child1->get_prev_visible(), nullptr);
+ CHECK_EQ(child1->get_prev_in_tree(), nullptr);
+ CHECK_EQ(child2->get_prev(), child1);
+ CHECK_EQ(child2->get_prev_visible(), child1);
+ CHECK_EQ(child2->get_prev_in_tree(), child1);
+ CHECK_EQ(child3->get_prev(), child2);
+ CHECK_EQ(child3->get_prev_visible(), child2);
+ CHECK_EQ(child3->get_prev_in_tree(), child2);
+
+ memdelete(tree);
+ }
+
+ SUBCASE("[Tree] Previous and Next items wrapping.") {
+ Tree *tree = memnew(Tree);
+ TreeItem *root = tree->create_item();
+
+ TreeItem *child1 = tree->create_item();
+ TreeItem *child2 = tree->create_item();
+ TreeItem *child3 = tree->create_item();
+ CHECK_EQ(root->get_next_visible(true), child1);
+ CHECK_EQ(root->get_next_in_tree(true), child1);
+ CHECK_EQ(child1->get_next_visible(true), child2);
+ CHECK_EQ(child1->get_next_in_tree(true), child2);
+ CHECK_EQ(child2->get_next_visible(true), child3);
+ CHECK_EQ(child2->get_next_in_tree(true), child3);
+ CHECK_EQ(child3->get_next_visible(true), root);
+ CHECK_EQ(child3->get_next_in_tree(true), root);
+
+ CHECK_EQ(root->get_prev_visible(true), child3);
+ CHECK_EQ(root->get_prev_in_tree(true), child3);
+ CHECK_EQ(child1->get_prev_visible(true), root);
+ CHECK_EQ(child1->get_prev_in_tree(true), root);
+ CHECK_EQ(child2->get_prev_visible(true), child1);
+ CHECK_EQ(child2->get_prev_in_tree(true), child1);
+ CHECK_EQ(child3->get_prev_visible(true), child2);
+ CHECK_EQ(child3->get_prev_in_tree(true), child2);
+
+ TreeItem *nested1 = tree->create_item(child2);
+ TreeItem *nested2 = tree->create_item(child2);
+ TreeItem *nested3 = tree->create_item(child2);
+
+ CHECK_EQ(child1->get_next_visible(true), child2);
+ CHECK_EQ(child1->get_next_in_tree(true), child2);
+ CHECK_EQ(child2->get_next_visible(true), nested1);
+ CHECK_EQ(child2->get_next_in_tree(true), nested1);
+ CHECK_EQ(nested3->get_next_visible(true), child3);
+ CHECK_EQ(nested3->get_next_in_tree(true), child3);
+ CHECK_EQ(child3->get_prev_visible(true), nested3);
+ CHECK_EQ(child3->get_prev_in_tree(true), nested3);
+ CHECK_EQ(nested1->get_prev_in_tree(true), child2);
+ CHECK_EQ(nested1->get_next_in_tree(true), nested2);
+ CHECK_EQ(nested3->get_next_in_tree(true), child3);
+
+ memdelete(tree);
+ }
+
+ SUBCASE("[Tree] Previous and Next items wrapping with hide root.") {
+ Tree *tree = memnew(Tree);
+ tree->set_hide_root(true);
+ TreeItem *root = tree->create_item();
+
+ TreeItem *child1 = tree->create_item();
+ TreeItem *child2 = tree->create_item();
+ TreeItem *child3 = tree->create_item();
+ CHECK_EQ(root->get_next_visible(true), child1);
+ CHECK_EQ(root->get_next_in_tree(true), child1);
+ CHECK_EQ(child1->get_next_visible(true), child2);
+ CHECK_EQ(child1->get_next_in_tree(true), child2);
+ CHECK_EQ(child2->get_next_visible(true), child3);
+ CHECK_EQ(child2->get_next_in_tree(true), child3);
+ CHECK_EQ(child3->get_next_visible(true), root);
+ CHECK_EQ(child3->get_next_in_tree(true), root);
+
+ CHECK_EQ(root->get_prev_visible(true), child3);
+ CHECK_EQ(root->get_prev_in_tree(true), child3);
+ CHECK_EQ(child1->get_prev_visible(true), child3);
+ CHECK_EQ(child1->get_prev_in_tree(true), child3);
+ CHECK_EQ(child2->get_prev_visible(true), child1);
+ CHECK_EQ(child2->get_prev_in_tree(true), child1);
+ CHECK_EQ(child3->get_prev_visible(true), child2);
+ CHECK_EQ(child3->get_prev_in_tree(true), child2);
memdelete(tree);
}
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index be9ed7bd77..e63ac96067 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -89,6 +89,7 @@
#include "tests/core/string/test_string.h"
#include "tests/core/string/test_translation.h"
#include "tests/core/string/test_translation_server.h"
+#include "tests/core/templates/test_a_hash_map.h"
#include "tests/core/templates/test_command_queue.h"
#include "tests/core/templates/test_hash_map.h"
#include "tests/core/templates/test_hash_set.h"