summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/input/input.cpp173
-rw-r--r--core/input/input.h40
-rw-r--r--core/math/basis.cpp23
-rw-r--r--core/math/basis.h1
-rw-r--r--core/object/make_virtuals.py236
-rw-r--r--core/os/os.cpp41
-rw-r--r--core/os/os.h8
-rw-r--r--core/register_core_types.cpp21
-rw-r--r--doc/classes/GPUParticles2D.xml1
-rw-r--r--doc/classes/GPUParticles3D.xml1
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp5
-rw-r--r--editor/editor_fonts.cpp4
-rw-r--r--editor/editor_help.cpp10
-rw-r--r--editor/editor_help.h1
-rw-r--r--editor/editor_node.cpp17
-rw-r--r--editor/editor_node.h3
-rw-r--r--editor/editor_properties.cpp8
-rw-r--r--editor/editor_themes.cpp15
-rw-r--r--editor/export/editor_export_platform.cpp31
-rw-r--r--editor/export/editor_export_platform_pc.cpp1
-rw-r--r--editor/export/project_export.cpp52
-rw-r--r--editor/node_dock.cpp4
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp28
-rw-r--r--editor/register_editor_types.cpp8
-rw-r--r--editor/scene_tree_dock.cpp8
-rw-r--r--main/main.cpp519
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp4
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp72
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp20
-rw-r--r--modules/gdscript/gdscript_compiler.cpp20
-rw-r--r--modules/gdscript/gdscript_function.h55
-rw-r--r--modules/gdscript/gdscript_parser.cpp59
-rw-r--r--modules/gdscript/gdscript_parser.h60
-rw-r--r--modules/gdscript/gdscript_vm.cpp4
-rw-r--r--platform/android/export/export_plugin.cpp20
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt12
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt37
-rw-r--r--platform/android/java_godot_wrapper.cpp14
-rw-r--r--platform/android/java_godot_wrapper.h4
-rw-r--r--platform/android/os_android.cpp8
-rw-r--r--platform/android/os_android.h4
-rw-r--r--platform/ios/display_server_ios.mm6
-rw-r--r--platform/ios/export/export_plugin.cpp134
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp18
-rw-r--r--platform/macos/export/export_plugin.cpp15
-rw-r--r--platform/web/export/export_plugin.cpp17
-rw-r--r--platform/windows/export/export_plugin.cpp11
-rw-r--r--scene/register_scene_types.cpp12
-rw-r--r--scene/resources/primitive_meshes.cpp13
-rw-r--r--servers/register_server_types.cpp12
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp37
-rw-r--r--tests/core/math/test_basis.h94
-rw-r--r--tests/core/string/test_string.h81
-rw-r--r--tests/scene/test_primitives.h2
-rw-r--r--thirdparty/squish/colourblock.cpp17
-rw-r--r--thirdparty/squish/colourblock.h1
-rw-r--r--thirdparty/squish/godot-changes.patch109
-rw-r--r--thirdparty/squish/squish.cpp4
60 files changed, 1453 insertions, 788 deletions
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 257452b3d8..14cb8bcf4e 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -241,8 +241,8 @@ bool Input::is_anything_pressed() const {
return true;
}
- for (const KeyValue<StringName, Input::Action> &E : action_state) {
- if (E.value.pressed) {
+ for (const KeyValue<StringName, Input::ActionState> &E : action_states) {
+ if (E.value.cache.pressed) {
return true;
}
}
@@ -285,12 +285,17 @@ bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const {
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
- return action_state.has(p_action) && action_state[p_action].pressed > 0 && (p_exact ? action_state[p_action].exact : true);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
+ if (!E) {
+ return false;
+ }
+
+ return E->value.cache.pressed && (p_exact ? E->value.exact : true);
}
bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
- HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) {
return false;
}
@@ -300,7 +305,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
}
// Backward compatibility for legacy behavior, only return true if currently pressed.
- bool pressed_requirement = legacy_just_pressed_behavior ? E->value.pressed : true;
+ bool pressed_requirement = legacy_just_pressed_behavior ? E->value.cache.pressed : true;
if (Engine::get_singleton()->is_in_physics_frame()) {
return pressed_requirement && E->value.pressed_physics_frame == Engine::get_singleton()->get_physics_frames();
@@ -311,7 +316,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
- HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) {
return false;
}
@@ -321,7 +326,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
}
// Backward compatibility for legacy behavior, only return true if currently released.
- bool released_requirement = legacy_just_pressed_behavior ? !E->value.pressed : true;
+ bool released_requirement = legacy_just_pressed_behavior ? !E->value.cache.pressed : true;
if (Engine::get_singleton()->is_in_physics_frame()) {
return released_requirement && E->value.released_physics_frame == Engine::get_singleton()->get_physics_frames();
@@ -332,7 +337,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
float Input::get_action_strength(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
- HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) {
return 0.0f;
}
@@ -341,12 +346,12 @@ float Input::get_action_strength(const StringName &p_action, bool p_exact) const
return 0.0f;
}
- return E->value.strength;
+ return E->value.cache.strength;
}
float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
- HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) {
return 0.0f;
}
@@ -355,7 +360,7 @@ float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) c
return 0.0f;
}
- return E->value.raw_strength;
+ return E->value.cache.raw_strength;
}
float Input::get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const {
@@ -440,6 +445,18 @@ static String _hex_str(uint8_t p_byte) {
void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) {
_THREAD_SAFE_METHOD_
+
+ // Clear the pressed status if a Joypad gets disconnected.
+ if (!p_connected) {
+ for (KeyValue<StringName, ActionState> &E : action_states) {
+ HashMap<int, ActionState::DeviceState>::Iterator it = E.value.device_states.find(p_idx);
+ if (it) {
+ E.value.device_states.remove(it);
+ _update_action_cache(E.key, E.value);
+ }
+ }
+ }
+
Joypad js;
js.name = p_connected ? p_name : "";
js.uid = p_connected ? p_guid : "";
@@ -699,30 +716,35 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (event_index == -1) {
continue;
}
+ ERR_FAIL_COND_MSG(event_index >= (int)MAX_EVENT, vformat("Input singleton does not support more than %d events assigned to an action.", MAX_EVENT));
+
+ int device_id = p_event->get_device();
+ bool is_pressed = p_event->is_action_pressed(E.key, true);
+ ActionState &action_state = action_states[E.key];
+
+ // Update the action's per-device state.
+ ActionState::DeviceState &device_state = action_state.device_states[device_id];
+ device_state.pressed[event_index] = is_pressed;
+ device_state.strength[event_index] = p_event->get_action_strength(E.key);
+ device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key);
+
+ // Update the action's global state and cache.
+ if (!is_pressed) {
+ action_state.api_pressed = false; // Always release the event from action_press() method.
+ action_state.api_strength = 0.0;
+ }
+ action_state.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
- Action &action = action_state[E.key];
- if (!p_event->is_echo()) {
- if (p_event->is_action_pressed(E.key)) {
- if (!action.pressed) {
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
- }
- action.pressed |= ((uint64_t)1 << event_index);
- } else {
- action.pressed &= ~((uint64_t)1 << event_index);
- action.pressed &= ~(1 << MAX_EVENT); // Always release the event from action_press() method.
-
- if (!action.pressed) {
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
- }
- _update_action_strength(action, MAX_EVENT, 0.0);
- _update_action_raw_strength(action, MAX_EVENT, 0.0);
- }
- action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
+ bool was_pressed = action_state.cache.pressed;
+ _update_action_cache(E.key, action_state);
+ if (action_state.cache.pressed && !was_pressed) {
+ action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ }
+ if (!action_state.cache.pressed && was_pressed) {
+ action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
}
- _update_action_strength(action, event_index, p_event->get_action_strength(E.key));
- _update_action_raw_strength(action, event_index, p_event->get_action_raw_strength(E.key));
}
if (event_dispatch_function) {
@@ -837,32 +859,30 @@ Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, con
void Input::action_press(const StringName &p_action, float p_strength) {
// Create or retrieve existing action.
- Action &action = action_state[p_action];
+ ActionState &action_state = action_states[p_action];
- if (!action.pressed) {
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ if (!action_state.cache.pressed) {
+ action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
}
- action.pressed |= 1 << MAX_EVENT;
- _update_action_strength(action, MAX_EVENT, p_strength);
- _update_action_raw_strength(action, MAX_EVENT, p_strength);
- action.exact = true;
+ action_state.exact = true;
+ action_state.api_pressed = true;
+ action_state.api_strength = p_strength;
+ _update_action_cache(p_action, action_state);
}
void Input::action_release(const StringName &p_action) {
// Create or retrieve existing action.
- Action &action = action_state[p_action];
-
- action.pressed = 0;
- action.strength = 0.0;
- action.raw_strength = 0.0;
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
- for (uint64_t i = 0; i <= MAX_EVENT; i++) {
- action.strengths[i] = 0.0;
- action.raw_strengths[i] = 0.0;
- }
- action.exact = true;
+ ActionState &action_state = action_states[p_action];
+ action_state.cache.pressed = 0;
+ action_state.cache.strength = 0.0;
+ action_state.cache.raw_strength = 0.0;
+ action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
+ action_state.device_states.clear();
+ action_state.exact = true;
+ action_state.api_pressed = false;
+ action_state.api_strength = 0.0;
}
void Input::set_emulate_touch_from_mouse(bool p_emulate) {
@@ -1020,10 +1040,8 @@ void Input::release_pressed_events() {
joy_buttons_pressed.clear();
_joy_axis.clear();
- for (KeyValue<StringName, Input::Action> &E : action_state) {
- if (E.value.pressed > 0) {
- // Make sure the action is really released.
- E.value.pressed = 1;
+ for (KeyValue<StringName, Input::ActionState> &E : action_states) {
+ if (E.value.cache.pressed) {
action_release(E.key);
}
}
@@ -1190,35 +1208,26 @@ void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) {
parse_input_event(ievent);
}
-void Input::_update_action_strength(Action &p_action, int p_event_index, float p_strength) {
- ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1);
+void Input::_update_action_cache(const StringName &p_action_name, ActionState &r_action_state) {
+ // Update the action cache, computed from the per-device and per-event states.
+ r_action_state.cache.pressed = false;
+ r_action_state.cache.strength = 0.0;
+ r_action_state.cache.raw_strength = 0.0;
- float old_strength = p_action.strengths[p_event_index];
- p_action.strengths[p_event_index] = p_strength;
-
- if (p_strength > p_action.strength) {
- p_action.strength = p_strength;
- } else if (Math::is_equal_approx(old_strength, p_action.strength)) {
- p_action.strength = p_strength;
- for (uint64_t i = 0; i <= MAX_EVENT; i++) {
- p_action.strength = MAX(p_action.strength, p_action.strengths[i]);
+ int max_event = InputMap::get_singleton()->action_get_events(p_action_name)->size();
+ for (const KeyValue<int, ActionState::DeviceState> &kv : r_action_state.device_states) {
+ const ActionState::DeviceState &device_state = kv.value;
+ for (int i = 0; i < max_event; i++) {
+ r_action_state.cache.pressed = r_action_state.cache.pressed || device_state.pressed[i];
+ r_action_state.cache.strength = MAX(r_action_state.cache.strength, device_state.strength[i]);
+ r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, device_state.raw_strength[i]);
}
}
-}
-
-void Input::_update_action_raw_strength(Action &p_action, int p_event_index, float p_strength) {
- ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1);
- float old_strength = p_action.raw_strengths[p_event_index];
- p_action.raw_strengths[p_event_index] = p_strength;
-
- if (p_strength > p_action.raw_strength) {
- p_action.raw_strength = p_strength;
- } else if (Math::is_equal_approx(old_strength, p_action.raw_strength)) {
- p_action.raw_strength = p_strength;
- for (uint64_t i = 0; i <= MAX_EVENT; i++) {
- p_action.raw_strength = MAX(p_action.raw_strength, p_action.raw_strengths[i]);
- }
+ if (r_action_state.api_pressed) {
+ r_action_state.cache.pressed = true;
+ r_action_state.cache.strength = MAX(r_action_state.cache.strength, r_action_state.api_strength);
+ r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, r_action_state.api_strength); // Use the strength as raw_strength for API-pressed states.
}
}
diff --git a/core/input/input.h b/core/input/input.h
index dd613c4877..b98406e884 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -44,7 +44,7 @@ class Input : public Object {
static Input *singleton;
- static constexpr uint64_t MAX_EVENT = 31;
+ static constexpr uint64_t MAX_EVENT = 32;
public:
enum MouseMode {
@@ -100,30 +100,31 @@ private:
int64_t mouse_window = 0;
bool legacy_just_pressed_behavior = false;
- struct Action {
+ struct ActionState {
uint64_t pressed_physics_frame = UINT64_MAX;
uint64_t pressed_process_frame = UINT64_MAX;
uint64_t released_physics_frame = UINT64_MAX;
uint64_t released_process_frame = UINT64_MAX;
- uint64_t pressed = 0;
bool exact = true;
- float strength = 0.0f;
- float raw_strength = 0.0f;
- LocalVector<float> strengths;
- LocalVector<float> raw_strengths;
-
- Action() {
- strengths.resize(MAX_EVENT + 1);
- raw_strengths.resize(MAX_EVENT + 1);
-
- for (uint64_t i = 0; i <= MAX_EVENT; i++) {
- strengths[i] = 0.0;
- raw_strengths[i] = 0.0;
- }
- }
+
+ struct DeviceState {
+ bool pressed[MAX_EVENT] = { false };
+ float strength[MAX_EVENT] = { 0.0 };
+ float raw_strength[MAX_EVENT] = { 0.0 };
+ };
+ bool api_pressed = false;
+ float api_strength = 0.0;
+ HashMap<int, DeviceState> device_states;
+
+ // Cache.
+ struct ActionStateCache {
+ bool pressed = false;
+ float strength = false;
+ float raw_strength = false;
+ } cache;
};
- HashMap<StringName, Action> action_state;
+ HashMap<StringName, ActionState> action_states;
bool emulate_touch_from_mouse = false;
bool emulate_mouse_from_touch = false;
@@ -240,8 +241,7 @@ private:
JoyAxis _get_output_axis(String output);
void _button_event(int p_device, JoyButton p_index, bool p_pressed);
void _axis_event(int p_device, JoyAxis p_axis, float p_value);
- void _update_action_strength(Action &p_action, int p_event_index, float p_strength);
- void _update_action_raw_strength(Action &p_action, int p_event_index, float p_strength);
+ void _update_action_cache(const StringName &p_action_name, ActionState &r_action_state);
void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 9796ac59c2..cd8c87b158 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -89,13 +89,26 @@ Basis Basis::orthogonalized() const {
return c;
}
+// Returns true if the basis vectors are orthogonal (perpendicular), so it has no skew or shear, and can be decomposed into rotation and scale.
+// See https://en.wikipedia.org/wiki/Orthogonal_basis
bool Basis::is_orthogonal() const {
- Basis identity;
- Basis m = (*this) * transposed();
+ const Vector3 x = get_column(0);
+ const Vector3 y = get_column(1);
+ const Vector3 z = get_column(2);
+ return Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z));
+}
- return m.is_equal_approx(identity);
+// Returns true if the basis vectors are orthonormal (orthogonal and normalized), so it has no scale, skew, or shear.
+// See https://en.wikipedia.org/wiki/Orthonormal_basis
+bool Basis::is_orthonormal() const {
+ const Vector3 x = get_column(0);
+ const Vector3 y = get_column(1);
+ const Vector3 z = get_column(2);
+ return Math::is_equal_approx(x.length_squared(), 1) && Math::is_equal_approx(y.length_squared(), 1) && Math::is_equal_approx(z.length_squared(), 1) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z));
}
+// Returns true if the basis is conformal (orthogonal, uniform scale, preserves angles and distance ratios).
+// See https://en.wikipedia.org/wiki/Conformal_linear_transformation
bool Basis::is_conformal() const {
const Vector3 x = get_column(0);
const Vector3 y = get_column(1);
@@ -104,6 +117,7 @@ bool Basis::is_conformal() const {
return Math::is_equal_approx(x_len_sq, y.length_squared()) && Math::is_equal_approx(x_len_sq, z.length_squared()) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z));
}
+// Returns true if the basis only has diagonal elements, so it may only have scale or flip, but no rotation, skew, or shear.
bool Basis::is_diagonal() const {
return (
Math::is_zero_approx(rows[0][1]) && Math::is_zero_approx(rows[0][2]) &&
@@ -111,8 +125,9 @@ bool Basis::is_diagonal() const {
Math::is_zero_approx(rows[2][0]) && Math::is_zero_approx(rows[2][1]));
}
+// Returns true if the basis is a pure rotation matrix, so it has no scale, skew, shear, or flip.
bool Basis::is_rotation() const {
- return Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON) && is_orthogonal();
+ return is_conformal() && Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON);
}
#ifdef MATH_CHECKS
diff --git a/core/math/basis.h b/core/math/basis.h
index adacd1c216..b4d971464e 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -138,6 +138,7 @@ struct _NO_DISCARD_ Basis {
_FORCE_INLINE_ Basis operator*(const real_t p_val) const;
bool is_orthogonal() const;
+ bool is_orthonormal() const;
bool is_conformal() const;
bool is_diagonal() const;
bool is_rotation() const;
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 79a8df6c8a..ae70981f72 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -1,78 +1,74 @@
-proto = """
-#define GDVIRTUAL$VER($RET m_name $ARG) \\
-StringName _gdvirtual_##m_name##_sn = #m_name;\\
-mutable bool _gdvirtual_##m_name##_initialized = false;\\
-mutable void* _gdvirtual_##m_name = nullptr;\\
-template<bool required>\\
-_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
- ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
- if (_script_instance) {\\
- Callable::CallError ce; \\
- $CALLSIARGS\\
- $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\
- if (ce.error == Callable::CallError::CALL_OK) {\\
- $CALLSIRET\\
+proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
+ StringName _gdvirtual_##m_name##_sn = #m_name;\\
+ mutable bool _gdvirtual_##m_name##_initialized = false;\\
+ mutable void *_gdvirtual_##m_name = nullptr;\\
+ template <bool required>\\
+ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\
+ ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\
+ if (_script_instance) {\\
+ Callable::CallError ce;\\
+ $CALLSIARGS\\
+ $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\
+ if (ce.error == Callable::CallError::CALL_OK) {\\
+ $CALLSIRET\\
+ return true;\\
+ }\\
+ }\\
+ if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
+ _gdvirtual_##m_name = nullptr;\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ } else if (_get_extension()->get_virtual) {\\
+ _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ }\\
+ GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\
+ _gdvirtual_##m_name##_initialized = true;\\
+ }\\
+ if (_gdvirtual_##m_name) {\\
+ $CALLPTRARGS\\
+ $CALLPTRRETDEF\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS, $CALLPTRRETPASS);\\
+ $CALLPTRRET\\
+ } else {\\
+ ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(), $CALLPTRARGPASS, $CALLPTRRETPASS);\\
+ $CALLPTRRET\\
+ }\\
return true;\\
- } \\
+ }\\
+ if (required) {\\
+ ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
+ $RVOID\\
+ }\\
+ return false;\\
}\\
- if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
- _gdvirtual_##m_name = nullptr;\\
- if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
- _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
- } else if (_get_extension()->get_virtual) {\\
- _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
- }\\
- GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\
- _gdvirtual_##m_name##_initialized = true;\\
- }\\
- if (_gdvirtual_##m_name) {\\
- $CALLPTRARGS\\
- $CALLPTRRETDEF\\
- if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
- _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS,$CALLPTRRETPASS);\\
- $CALLPTRRET\\
- } else {\\
- ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
- $CALLPTRRET\\
- }\\
- return true;\\
- }\\
- \\
- if (required) {\\
- ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
- $RVOID\\
- }\\
-\\
- return false;\\
-}\\
-_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\
- ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
- if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\
- return true;\\
- }\\
- if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
- _gdvirtual_##m_name = nullptr;\\
- if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
- _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
- } else if (_get_extension()->get_virtual) {\\
- _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
- }\\
- GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\
- _gdvirtual_##m_name##_initialized = true;\\
- }\\
- if (_gdvirtual_##m_name) {\\
- return true;\\
+ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\
+ ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\
+ if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\
+ return true;\\
+ }\\
+ if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
+ _gdvirtual_##m_name = nullptr;\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ } else if (_get_extension()->get_virtual) {\\
+ _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ }\\
+ GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\
+ _gdvirtual_##m_name##_initialized = true;\\
+ }\\
+ if (_gdvirtual_##m_name) {\\
+ return true;\\
+ }\\
+ return false;\\
}\\
- return false;\\
-}\\
-\\
-_FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\
- MethodInfo method_info;\\
- method_info.name = #m_name;\\
- method_info.flags = METHOD_FLAG_VIRTUAL;\\
- $FILL_METHOD_INFO\\
- return method_info;\\
-}
+ _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() {\\
+ MethodInfo method_info;\\
+ method_info.name = #m_name;\\
+ method_info.flags = $METHOD_FLAGS;\\
+ $FILL_METHOD_INFO\\
+ return method_info;\\
+ }
"""
@@ -83,22 +79,23 @@ def generate_version(argcount, const=False, returns=False):
method_info = ""
if returns:
sproto += "R"
- s = s.replace("$RET", "m_ret, ")
+ s = s.replace("$RET", "m_ret,")
s = s.replace("$RVOID", "(void)r_ret;") # If required, may lead to uninitialized errors
s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;")
- method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n"
- method_info += "\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;\\\n"
+ method_info += "method_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n"
+ method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;"
else:
- s = s.replace("$RET", "")
- s = s.replace("$RVOID", "")
- s = s.replace("$CALLPTRRETDEF", "")
+ s = s.replace("$RET ", "")
+ s = s.replace("\t\t\t$RVOID\\\n", "")
+ s = s.replace("\t\t\t$CALLPTRRETDEF\\\n", "")
if const:
sproto += "C"
s = s.replace("$CONST", "const")
- method_info += "\tmethod_info.flags|=METHOD_FLAG_CONST;\\\n"
+ s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST")
else:
- s = s.replace("$CONST", "")
+ s = s.replace("$CONST ", "")
+ s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL")
s = s.replace("$VER", sproto)
argtext = ""
@@ -108,9 +105,9 @@ def generate_version(argcount, const=False, returns=False):
callptrargsptr = ""
if argcount > 0:
argtext += ", "
- callsiargs = "Variant vargs[" + str(argcount) + "]={"
- callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={"
- callptrargsptr = "\t\tGDExtensionConstTypePtr argptrs[" + str(argcount) + "]={"
+ callsiargs = f"Variant vargs[{argcount}] = {{ "
+ callsiargptrs = f"\t\t\tconst Variant *vargptrs[{argcount}] = {{ "
+ callptrargsptr = f"\t\t\tGDExtensionConstTypePtr argptrs[{argcount}] = {{ "
callptrargs = ""
for i in range(argcount):
if i > 0:
@@ -118,52 +115,55 @@ def generate_version(argcount, const=False, returns=False):
callargtext += ", "
callsiargs += ", "
callsiargptrs += ", "
- callptrargs += "\t\t"
+ callptrargs += "\t\t\t"
callptrargsptr += ", "
- argtext += "m_type" + str(i + 1)
- callargtext += "m_type" + str(i + 1) + " arg" + str(i + 1)
- callsiargs += "Variant(arg" + str(i + 1) + ")"
- callsiargptrs += "&vargs[" + str(i) + "]"
+ argtext += f"m_type{i + 1}"
+ callargtext += f"m_type{i + 1} arg{i + 1}"
+ callsiargs += f"Variant(arg{i + 1})"
+ callsiargptrs += f"&vargs[{i}]"
callptrargs += (
- "PtrToArg<m_type" + str(i + 1) + ">::EncodeT argval" + str(i + 1) + " = arg" + str(i + 1) + ";\\\n"
- )
- callptrargsptr += "&argval" + str(i + 1)
- method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n"
- method_info += (
- "\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::METADATA);\\\n"
+ f"PtrToArg<m_type{i + 1}>::EncodeT argval{i + 1} = (PtrToArg<m_type{i + 1}>::EncodeT)arg{i + 1};\\\n"
)
+ callptrargsptr += f"&argval{i + 1}"
+ if method_info:
+ method_info += "\\\n\t\t"
+ method_info += f"method_info.arguments.push_back(GetTypeInfo<m_type{i + 1}>::get_class_info());\\\n"
+ method_info += f"\t\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type{i + 1}>::METADATA);"
if argcount:
- callsiargs += "};\\\n"
- callsiargptrs += "};\\\n"
+ callsiargs += " };\\\n"
+ callsiargptrs += " };"
s = s.replace("$CALLSIARGS", callsiargs + callsiargptrs)
- s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount))
- callptrargsptr += "};\\\n"
+ s = s.replace("$CALLSIARGPASS", f"(const Variant **)vargptrs, {argcount}")
+ callptrargsptr += " };"
s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr)
- s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr*>(argptrs)")
+ s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr *>(argptrs)")
else:
- s = s.replace("$CALLSIARGS", "")
+ s = s.replace("\t\t\t$CALLSIARGS\\\n", "")
s = s.replace("$CALLSIARGPASS", "nullptr, 0")
- s = s.replace("$CALLPTRARGS", "")
+ s = s.replace("\t\t\t$CALLPTRARGS\\\n", "")
s = s.replace("$CALLPTRARGPASS", "nullptr")
if returns:
if argcount > 0:
- callargtext += ","
- callargtext += " m_ret& r_ret"
+ callargtext += ", "
+ callargtext += "m_ret &r_ret"
s = s.replace("$CALLSIBEGIN", "Variant ret = ")
s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);")
s = s.replace("$CALLPTRRETPASS", "&ret")
s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;")
else:
s = s.replace("$CALLSIBEGIN", "")
- s = s.replace("$CALLSIRET", "")
+ s = s.replace("\t\t\t\t$CALLSIRET\\\n", "")
s = s.replace("$CALLPTRRETPASS", "nullptr")
- s = s.replace("$CALLPTRRET", "")
+ s = s.replace("\t\t\t\t$CALLPTRRET\\\n", "")
- s = s.replace("$ARG", argtext)
+ s = s.replace(" $ARG", argtext)
s = s.replace("$CALLARGS", callargtext)
- s = s.replace("$FILL_METHOD_INFO", method_info)
+ if method_info:
+ s = s.replace("$FILL_METHOD_INFO", method_info)
+ else:
+ s = s.replace("\t\t$FILL_METHOD_INFO\\\n", method_info)
return s
@@ -171,21 +171,21 @@ def generate_version(argcount, const=False, returns=False):
def run(target, source, env):
max_versions = 12
- txt = """
+ txt = """/* THIS FILE IS GENERATED DO NOT EDIT */
#ifndef GDVIRTUAL_GEN_H
#define GDVIRTUAL_GEN_H
#include "core/object/script_instance.h"
#ifdef TOOLS_ENABLED
-#define GDVIRTUAL_TRACK(m_virtual, m_initialized) \\
- if (_get_extension()->reloadable) {\\
- VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\
- tracker->method = (void **)&m_virtual;\\
- tracker->initialized = &m_initialized;\\
- tracker->next = virtual_method_list;\\
- virtual_method_list = tracker;\\
- }
+#define GDVIRTUAL_TRACK(m_virtual, m_initialized)\\
+ if (_get_extension()->reloadable) {\\
+ VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\
+ tracker->method = (void **)&m_virtual;\\
+ tracker->initialized = &m_initialized;\\
+ tracker->next = virtual_method_list;\\
+ virtual_method_list = tracker;\\
+ }
#else
#define GDVIRTUAL_TRACK(m_virtual, m_initialized)
#endif
@@ -193,13 +193,13 @@ def run(target, source, env):
"""
for i in range(max_versions + 1):
- txt += "/* " + str(i) + " Arguments */\n\n"
+ txt += f"/* {i} Arguments */\n\n"
txt += generate_version(i, False, False)
txt += generate_version(i, False, True)
txt += generate_version(i, True, False)
txt += generate_version(i, True, True)
- txt += "#endif"
+ txt += "#endif // GDVIRTUAL_GEN_H\n"
with open(target[0], "w") as f:
f.write(txt)
diff --git a/core/os/os.cpp b/core/os/os.cpp
index f5d55ca107..26ae286979 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -626,17 +626,22 @@ String OS::get_benchmark_file() {
return benchmark_file;
}
-void OS::benchmark_begin_measure(const String &p_what) {
+void OS::benchmark_begin_measure(const String &p_context, const String &p_what) {
#ifdef TOOLS_ENABLED
- start_benchmark_from[p_what] = OS::get_singleton()->get_ticks_usec();
+ Pair<String, String> mark_key(p_context, p_what);
+ ERR_FAIL_COND_MSG(benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' already exists.", p_context, p_what));
+
+ benchmark_marks_from[mark_key] = OS::get_singleton()->get_ticks_usec();
#endif
}
-void OS::benchmark_end_measure(const String &p_what) {
+void OS::benchmark_end_measure(const String &p_context, const String &p_what) {
#ifdef TOOLS_ENABLED
- uint64_t total = OS::get_singleton()->get_ticks_usec() - start_benchmark_from[p_what];
- double total_f = double(total) / double(1000000);
+ Pair<String, String> mark_key(p_context, p_what);
+ ERR_FAIL_COND_MSG(!benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' doesn't exist.", p_context, p_what));
- startup_benchmark_json[p_what] = total_f;
+ uint64_t total = OS::get_singleton()->get_ticks_usec() - benchmark_marks_from[mark_key];
+ double total_f = double(total) / double(1000000);
+ benchmark_marks_final[mark_key] = total_f;
#endif
}
@@ -645,19 +650,33 @@ void OS::benchmark_dump() {
if (!use_benchmark) {
return;
}
+
if (!benchmark_file.is_empty()) {
Ref<FileAccess> f = FileAccess::open(benchmark_file, FileAccess::WRITE);
if (f.is_valid()) {
+ Dictionary benchmark_marks;
+ for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) {
+ const String mark_key = vformat("[%s] %s", E.key.first, E.key.second);
+ benchmark_marks[mark_key] = E.value;
+ }
+
Ref<JSON> json;
json.instantiate();
- f->store_string(json->stringify(startup_benchmark_json, "\t", false, true));
+ f->store_string(json->stringify(benchmark_marks, "\t", false, true));
}
} else {
- List<Variant> keys;
- startup_benchmark_json.get_key_list(&keys);
+ HashMap<String, String> results;
+ for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) {
+ if (E.key.first == "Startup" && !results.has(E.key.first)) {
+ results.insert(E.key.first, "", true); // Hack to make sure "Startup" always comes first.
+ }
+
+ results[E.key.first] += vformat("\t\t- %s: %.3f msec.\n", E.key.second, (E.value * 1000));
+ }
+
print_line("BENCHMARK:");
- for (const Variant &K : keys) {
- print_line("\t-", K, ": ", startup_benchmark_json[K], +" sec.");
+ for (const KeyValue<String, String> &E : results) {
+ print_line(vformat("\t[%s]\n%s", E.key, E.value));
}
}
#endif
diff --git a/core/os/os.h b/core/os/os.h
index cc5ebe1bc8..e22514dce3 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -79,8 +79,8 @@ class OS {
// For tracking benchmark data
bool use_benchmark = false;
String benchmark_file;
- HashMap<String, uint64_t> start_benchmark_from;
- Dictionary startup_benchmark_json;
+ HashMap<Pair<String, String>, uint64_t, PairHash<String, String>> benchmark_marks_from;
+ HashMap<Pair<String, String>, double, PairHash<String, String>> benchmark_marks_final;
protected:
void _set_logger(CompositeLogger *p_logger);
@@ -313,8 +313,8 @@ public:
bool is_use_benchmark_set();
void set_benchmark_file(const String &p_benchmark_file);
String get_benchmark_file();
- virtual void benchmark_begin_measure(const String &p_what);
- virtual void benchmark_end_measure(const String &p_what);
+ virtual void benchmark_begin_measure(const String &p_context, const String &p_what);
+ virtual void benchmark_end_measure(const String &p_context, const String &p_what);
virtual void benchmark_dump();
virtual void process_and_drop_events() {}
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 4ad9dd43c4..2785d1daa5 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -120,7 +120,8 @@ static ResourceUID *resource_uid = nullptr;
static bool _is_core_extensions_registered = false;
void register_core_types() {
- OS::get_singleton()->benchmark_begin_measure("register_core_types");
+ OS::get_singleton()->benchmark_begin_measure("Core", "Register Types");
+
//consistency check
static_assert(sizeof(Callable) <= 16);
@@ -296,7 +297,7 @@ void register_core_types() {
worker_thread_pool = memnew(WorkerThreadPool);
- OS::get_singleton()->benchmark_end_measure("register_core_types");
+ OS::get_singleton()->benchmark_end_measure("Core", "Register Types");
}
void register_core_settings() {
@@ -311,6 +312,8 @@ void register_core_settings() {
}
void register_core_singletons() {
+ OS::get_singleton()->benchmark_begin_measure("Core", "Register Singletons");
+
GDREGISTER_CLASS(ProjectSettings);
GDREGISTER_ABSTRACT_CLASS(IP);
GDREGISTER_CLASS(core_bind::Geometry2D);
@@ -346,25 +349,35 @@ void register_core_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("GDExtensionManager", GDExtensionManager::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", worker_thread_pool));
+
+ OS::get_singleton()->benchmark_end_measure("Core", "Register Singletons");
}
void register_core_extensions() {
+ OS::get_singleton()->benchmark_begin_measure("Core", "Register Extensions");
+
// Hardcoded for now.
GDExtension::initialize_gdextensions();
gdextension_manager->load_extensions();
gdextension_manager->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE);
_is_core_extensions_registered = true;
+
+ OS::get_singleton()->benchmark_end_measure("Core", "Register Extensions");
}
void unregister_core_extensions() {
+ OS::get_singleton()->benchmark_begin_measure("Core", "Unregister Extensions");
+
if (_is_core_extensions_registered) {
gdextension_manager->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE);
}
GDExtension::finalize_gdextensions();
+
+ OS::get_singleton()->benchmark_end_measure("Core", "Unregister Extensions");
}
void unregister_core_types() {
- OS::get_singleton()->benchmark_begin_measure("unregister_core_types");
+ OS::get_singleton()->benchmark_begin_measure("Core", "Unregister Types");
// Destroy singletons in reverse order to ensure dependencies are not broken.
@@ -435,5 +448,5 @@ void unregister_core_types() {
CoreStringNames::free();
StringName::cleanup();
- OS::get_singleton()->benchmark_end_measure("unregister_core_types");
+ OS::get_singleton()->benchmark_end_measure("Core", "Unregister Types");
}
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index 2308ec43c5..f4ba305f8b 100644
--- a/doc/classes/GPUParticles2D.xml
+++ b/doc/classes/GPUParticles2D.xml
@@ -37,6 +37,7 @@
<param index="4" name="flags" type="int" />
<description>
Emits a single particle. Whether [param xform], [param velocity], [param color] and [param custom] are applied depends on the value of [param flags]. See [enum EmitFlags].
+ The default ParticleProcessMaterial will overwrite [param color] and use the contents of [param custom] as [code](rotation, age, animation, lifetime)[/code].
</description>
</method>
<method name="restart">
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index b5af63a8f4..d1903b85cd 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -35,6 +35,7 @@
<param index="4" name="flags" type="int" />
<description>
Emits a single particle. Whether [param xform], [param velocity], [param color] and [param custom] are applied depends on the value of [param flags]. See [enum EmitFlags].
+ The default ParticleProcessMaterial will overwrite [param color] and use the contents of [param custom] as [code](rotation, age, animation, lifetime)[/code].
</description>
</method>
<method name="get_draw_pass_mesh" qualifiers="const">
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 2db17e96f7..af292fe80a 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -661,6 +661,11 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.current_tex = RID();
for (uint32_t i = 0; i <= state.current_batch_index; i++) {
+ // Skipping when there is no instances.
+ if (state.canvas_instance_batches[i].instance_count == 0) {
+ continue;
+ }
+
//setup clip
if (current_clip != state.canvas_instance_batches[i].clip) {
current_clip = state.canvas_instance_batches[i].clip;
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index 6c51be8361..1e1dff61b9 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -107,7 +107,7 @@ Ref<FontVariation> make_bold_font(const Ref<Font> &p_font, double p_embolden, Ty
}
void editor_register_fonts(Ref<Theme> p_theme) {
- OS::get_singleton()->benchmark_begin_measure("editor_register_fonts");
+ OS::get_singleton()->benchmark_begin_measure("EditorTheme", "Register Fonts");
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
TextServer::FontAntialiasing font_antialiasing = (TextServer::FontAntialiasing)(int)EDITOR_GET("interface/editor/font_antialiasing");
@@ -445,5 +445,5 @@ void editor_register_fonts(Ref<Theme> p_theme) {
p_theme->set_font_size("status_source_size", EditorStringName(EditorFonts), default_font_size);
p_theme->set_font("status_source", EditorStringName(EditorFonts), mono_other_fc);
- OS::get_singleton()->benchmark_end_measure("editor_register_fonts");
+ OS::get_singleton()->benchmark_end_measure("EditorTheme", "Register Fonts");
}
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 10ab733c2b..2b1ab4ca02 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -2360,6 +2360,7 @@ void EditorHelp::_add_text(const String &p_bbcode) {
_add_text_to_rt(p_bbcode, class_desc, this, edited_class);
}
+int EditorHelp::doc_generation_count = 0;
String EditorHelp::doc_version_hash;
Thread EditorHelp::worker_thread;
@@ -2392,6 +2393,8 @@ void EditorHelp::_load_doc_thread(void *p_udata) {
// We have to go back to the main thread to start from scratch, bypassing any possibly existing cache.
callable_mp_static(&EditorHelp::generate_doc).bind(false).call_deferred();
}
+
+ OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count));
}
void EditorHelp::_gen_doc_thread(void *p_udata) {
@@ -2417,6 +2420,8 @@ void EditorHelp::_gen_doc_thread(void *p_udata) {
if (err) {
ERR_PRINT("Cannot save editor help cache (" + get_cache_full_path() + ").");
}
+
+ OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count));
}
void EditorHelp::_gen_extensions_docs() {
@@ -2424,7 +2429,8 @@ void EditorHelp::_gen_extensions_docs() {
}
void EditorHelp::generate_doc(bool p_use_cache) {
- OS::get_singleton()->benchmark_begin_measure("EditorHelp::generate_doc");
+ doc_generation_count++;
+ OS::get_singleton()->benchmark_begin_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count));
// In case not the first attempt.
_wait_for_thread();
@@ -2444,8 +2450,6 @@ void EditorHelp::generate_doc(bool p_use_cache) {
doc->generate();
worker_thread.start(_gen_doc_thread, nullptr);
}
-
- OS::get_singleton()->benchmark_end_measure("EditorHelp::generate_doc");
}
void EditorHelp::_toggle_scripts_pressed() {
diff --git a/editor/editor_help.h b/editor/editor_help.h
index d2d05a8603..ff440a679a 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -187,6 +187,7 @@ class EditorHelp : public VBoxContainer {
String _fix_constant(const String &p_constant) const;
void _toggle_scripts_pressed();
+ static int doc_generation_count;
static String doc_version_hash;
static Thread worker_thread;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 60bca5cd77..79cc059900 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -627,7 +627,7 @@ void EditorNode::_notification(int p_what) {
if (requested_first_scan) {
requested_first_scan = false;
- OS::get_singleton()->benchmark_begin_measure("editor_scan_and_import");
+ OS::get_singleton()->benchmark_begin_measure("Editor", "First Scan");
if (run_surface_upgrade_tool) {
run_surface_upgrade_tool = false;
@@ -974,6 +974,9 @@ void EditorNode::_fs_changed() {
} else { // Normal project export.
String config_error;
bool missing_templates;
+ if (export_defer.android_build_template) {
+ export_template_manager->install_android_template();
+ }
if (!platform->can_export(export_preset, config_error, missing_templates, export_defer.debug)) {
ERR_PRINT(vformat("Cannot export project with preset \"%s\" due to configuration errors:\n%s", preset_name, config_error));
err = missing_templates ? ERR_FILE_NOT_FOUND : ERR_UNCONFIGURED;
@@ -1041,7 +1044,7 @@ void EditorNode::_sources_changed(bool p_exist) {
if (waiting_for_first_scan) {
waiting_for_first_scan = false;
- OS::get_singleton()->benchmark_end_measure("editor_scan_and_import");
+ OS::get_singleton()->benchmark_end_measure("Editor", "First Scan");
// Reload the global shader variables, but this time
// loading textures, as they are now properly imported.
@@ -1050,11 +1053,12 @@ void EditorNode::_sources_changed(bool p_exist) {
_load_editor_layout();
if (!defer_load_scene.is_empty()) {
- OS::get_singleton()->benchmark_begin_measure("editor_load_scene");
+ OS::get_singleton()->benchmark_begin_measure("Editor", "Load Scene");
+
load_scene(defer_load_scene);
defer_load_scene = "";
- OS::get_singleton()->benchmark_end_measure("editor_load_scene");
+ OS::get_singleton()->benchmark_end_measure("Editor", "Load Scene");
OS::get_singleton()->benchmark_dump();
}
@@ -4665,7 +4669,7 @@ void EditorNode::_begin_first_scan() {
// In headless mode, scan right away.
// This allows users to continue using `godot --headless --editor --quit` to prepare a project.
if (!DisplayServer::get_singleton()->window_can_draw()) {
- OS::get_singleton()->benchmark_begin_measure("editor_scan_and_import");
+ OS::get_singleton()->benchmark_begin_measure("Editor", "First Scan");
EditorFileSystem::get_singleton()->scan();
return;
}
@@ -4676,11 +4680,12 @@ void EditorNode::_begin_first_scan() {
requested_first_scan = true;
}
-Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only) {
+Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template) {
export_defer.preset = p_preset;
export_defer.path = p_path;
export_defer.debug = p_debug;
export_defer.pack_only = p_pack_only;
+ export_defer.android_build_template = p_android_build_template;
cmdline_export_mode = true;
return OK;
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 84fb6beb97..917feed4ee 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -264,6 +264,7 @@ private:
String path;
bool debug = false;
bool pack_only = false;
+ bool android_build_template = false;
} export_defer;
static EditorNode *singleton;
@@ -880,7 +881,7 @@ public:
void _copy_warning(const String &p_str);
- Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only);
+ Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template);
bool is_project_exporting() const;
Control *get_gui_base() { return gui_base; }
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 6d27829e2e..f891bfbff7 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -56,6 +56,7 @@
#include "scene/resources/font.h"
#include "scene/resources/mesh.h"
#include "scene/resources/packed_scene.h"
+#include "scene/resources/visual_shader_nodes.h"
///////////////////// Nil /////////////////////////
@@ -3204,6 +3205,13 @@ void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource)
Ref<ViewportTexture> vpt = p_resource;
if (vpt.is_valid()) {
r = Object::cast_to<Resource>(get_edited_object());
+ if (Object::cast_to<VisualShaderNodeTexture>(r)) {
+ EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture in a Texture2D node because the texture will not be bound to a scene.\nUse a Texture2DParameter node instead and set the texture in the \"Shader Parameters\" tab."));
+ emit_changed(get_edited_property(), Ref<Resource>());
+ update_property();
+ return;
+ }
+
if (r && r->get_path().is_resource_file()) {
EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on resources saved as a file.\nResource needs to belong to a scene."));
emit_changed(get_edited_property(), Ref<Resource>());
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 0dd787f0ea..96d2abf202 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -403,7 +403,9 @@ float get_gizmo_handle_scale(const String &gizmo_handle_name = "") {
}
void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, float p_icon_saturation, int p_thumb_size, bool p_only_thumbs = false) {
- OS::get_singleton()->benchmark_begin_measure("editor_register_and_generate_icons_" + String((p_only_thumbs ? "with_only_thumbs" : "all")));
+ const String benchmark_key = vformat("Generate Icons (%s)", (p_only_thumbs ? "Only Thumbs" : "All"));
+ OS::get_singleton()->benchmark_begin_measure("EditorTheme", benchmark_key);
+
// Before we register the icons, we adjust their colors and saturation.
// Most icons follow the standard rules for color conversion to follow the editor
// theme's polarity (dark/light). We also adjust the saturation for most icons,
@@ -531,11 +533,11 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, f
p_theme->set_icon(editor_icons_names[index], EditorStringName(EditorIcons), icon);
}
}
- OS::get_singleton()->benchmark_end_measure("editor_register_and_generate_icons_" + String((p_only_thumbs ? "with_only_thumbs" : "all")));
+ OS::get_singleton()->benchmark_end_measure("EditorTheme", benchmark_key);
}
Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
- OS::get_singleton()->benchmark_begin_measure("create_editor_theme");
+ OS::get_singleton()->benchmark_begin_measure("EditorTheme", "Create Editor Theme");
Ref<EditorTheme> theme = memnew(EditorTheme);
// Controls may rely on the scale for their internal drawing logic.
@@ -2360,15 +2362,16 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("search_result_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/search_result_color"));
theme->set_color("search_result_border_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/search_result_border_color"));
- OS::get_singleton()->benchmark_end_measure("create_editor_theme");
+ OS::get_singleton()->benchmark_end_measure("EditorTheme", "Create Editor Theme");
return theme;
}
Ref<Theme> create_custom_theme(const Ref<Theme> p_theme) {
- OS::get_singleton()->benchmark_begin_measure("create_custom_theme");
Ref<Theme> theme = create_editor_theme(p_theme);
+ OS::get_singleton()->benchmark_begin_measure("EditorTheme", "Create Custom Theme");
+
const String custom_theme_path = EDITOR_GET("interface/theme/custom_theme");
if (!custom_theme_path.is_empty()) {
Ref<Theme> custom_theme = ResourceLoader::load(custom_theme_path);
@@ -2377,7 +2380,7 @@ Ref<Theme> create_custom_theme(const Ref<Theme> p_theme) {
}
}
- OS::get_singleton()->benchmark_end_measure("create_custom_theme");
+ OS::get_singleton()->benchmark_end_measure("EditorTheme", "Create Custom Theme");
return theme;
}
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 733003db1a..ab2ada9ae4 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -93,7 +93,7 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
}
p_log->add_newline();
- if (msg_count) {
+ if (msg_count > 0) {
p_log->push_table(2);
p_log->set_table_column_expand(0, false);
p_log->set_table_column_expand(1, true);
@@ -133,8 +133,37 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
}
p_log->pop();
p_log->add_newline();
+ } else if (p_err != OK) {
+ // We failed but don't show any user-facing messages. This is bad and should not
+ // be allowed, but just in case this happens, let's give the user something at least.
+ p_log->push_table(2);
+ p_log->set_table_column_expand(0, false);
+ p_log->set_table_column_expand(1, true);
+
+ {
+ Color color = p_log->get_theme_color(SNAME("error_color"), EditorStringName(Editor));
+ Ref<Texture> icon = p_log->get_editor_theme_icon(SNAME("Error"));
+
+ p_log->push_cell();
+ p_log->add_text("\t");
+ if (icon.is_valid()) {
+ p_log->add_image(icon);
+ }
+ p_log->pop();
+
+ p_log->push_cell();
+ p_log->push_color(color);
+ p_log->add_text(vformat("[%s]: %s", TTR("Unknown Error"), vformat(TTR("Export failed with error code %d."), p_err)));
+ p_log->pop();
+ p_log->pop();
+ }
+
+ p_log->pop();
+ p_log->add_newline();
}
+
p_log->add_newline();
+
return has_messages;
}
diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp
index ec34ffd1df..42df0d93f6 100644
--- a/editor/export/editor_export_platform_pc.cpp
+++ b/editor/export/editor_export_platform_pc.cpp
@@ -161,6 +161,7 @@ Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_
}
if (err != OK) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("Failed to copy export template."));
+ return err;
}
return err;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index cacdf108cf..f2c5eeb2ed 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -1109,37 +1109,40 @@ void ProjectExportDialog::_export_all_dialog_action(const String &p_str) {
}
void ProjectExportDialog::_export_all(bool p_debug) {
- String export_target = p_debug ? TTR("Debug") : TTR("Release");
- EditorProgress ep("exportall", TTR("Exporting All") + " " + export_target, EditorExport::get_singleton()->get_export_preset_count(), true);
-
exporting = true;
-
bool show_dialog = false;
- result_dialog_log->clear();
- for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
- Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
- if (preset.is_null()) {
- exporting = false;
- ERR_FAIL_MSG("Failed to start the export: one of the presets is invalid.");
- }
- Ref<EditorExportPlatform> platform = preset->get_platform();
- if (platform.is_null()) {
- exporting = false;
- ERR_FAIL_MSG("Failed to start the export: one of the presets has no valid platform.");
- }
+ { // Scope for the editor progress, we must free it before showing the dialog at the end.
+ String export_target = p_debug ? TTR("Debug") : TTR("Release");
+ EditorProgress ep("exportall", TTR("Exporting All") + " " + export_target, EditorExport::get_singleton()->get_export_preset_count(), true);
- ep.step(preset->get_name(), i);
+ result_dialog_log->clear();
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
+ if (preset.is_null()) {
+ exporting = false;
+ ERR_FAIL_MSG("Failed to start the export: one of the presets is invalid.");
+ }
- platform->clear_messages();
- Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
- if (err == ERR_SKIP) {
- exporting = false;
- return;
+ Ref<EditorExportPlatform> platform = preset->get_platform();
+ if (platform.is_null()) {
+ exporting = false;
+ ERR_FAIL_MSG("Failed to start the export: one of the presets has no valid platform.");
+ }
+
+ ep.step(preset->get_name(), i);
+
+ platform->clear_messages();
+ Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
+ if (err == ERR_SKIP) {
+ exporting = false;
+ return;
+ }
+ bool has_messages = platform->fill_log_messages(result_dialog_log, err);
+ show_dialog = show_dialog || has_messages;
}
- bool has_messages = platform->fill_log_messages(result_dialog_log, err);
- show_dialog = show_dialog || has_messages;
}
+
if (show_dialog) {
result_dialog->popup_centered_ratio(0.5);
}
@@ -1148,7 +1151,6 @@ void ProjectExportDialog::_export_all(bool p_debug) {
}
void ProjectExportDialog::_bind_methods() {
- ClassDB::bind_method("_export_all", &ProjectExportDialog::_export_all);
ClassDB::bind_method("set_export_path", &ProjectExportDialog::set_export_path);
ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset);
diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp
index 6521730473..680e77fe42 100644
--- a/editor/node_dock.cpp
+++ b/editor/node_dock.cpp
@@ -104,7 +104,7 @@ NodeDock::NodeDock() {
mode_hb->hide();
connections_button = memnew(Button);
- connections_button->set_flat(true);
+ connections_button->set_theme_type_variation("FlatButton");
connections_button->set_text(TTR("Signals"));
connections_button->set_toggle_mode(true);
connections_button->set_pressed(true);
@@ -114,7 +114,7 @@ NodeDock::NodeDock() {
connections_button->connect("pressed", callable_mp(this, &NodeDock::show_connections));
groups_button = memnew(Button);
- groups_button->set_flat(true);
+ groups_button->set_theme_type_variation("FlatButton");
groups_button->set_text(TTR("Groups"));
groups_button->set_toggle_mode(true);
groups_button->set_pressed(false);
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 42c080fdf2..6fe1949382 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -303,9 +303,19 @@ void Path3DGizmo::redraw() {
}
const Transform3D *r = frames.ptr();
+
Vector<Vector3> _collision_segments;
+ _collision_segments.resize((sample_count - 1) * 2);
+ Vector3 *_collisions_ptr = _collision_segments.ptrw();
+
Vector<Vector3> bones;
+ bones.resize(sample_count * 4);
+ Vector3 *bones_ptr = bones.ptrw();
+
Vector<Vector3> ribbon;
+ ribbon.resize(sample_count);
+ Vector3 *ribbon_ptr = ribbon.ptrw();
+
for (int i = 0; i < sample_count; i++) {
const Vector3 p1 = r[i].origin;
const Vector3 side = r[i].basis.get_column(0);
@@ -313,23 +323,25 @@ void Path3DGizmo::redraw() {
const Vector3 forward = r[i].basis.get_column(2);
// Collision segments.
- if (i != sample_count) {
+ if (i != sample_count - 1) {
const Vector3 p2 = r[i + 1].origin;
- _collision_segments.push_back(p1);
- _collision_segments.push_back(p2);
+ _collisions_ptr[(i * 2)] = p1;
+ _collisions_ptr[(i * 2) + 1] = p2;
}
// Path3D as a ribbon.
- ribbon.push_back(p1);
+ ribbon_ptr[i] = p1;
// Fish Bone.
const Vector3 p_left = p1 + (side + forward - up * 0.3) * 0.06;
const Vector3 p_right = p1 + (-side + forward - up * 0.3) * 0.06;
- bones.push_back(p1);
- bones.push_back(p_left);
- bones.push_back(p1);
- bones.push_back(p_right);
+ const int bone_idx = i * 4;
+
+ bones_ptr[bone_idx] = p1;
+ bones_ptr[bone_idx + 1] = p_left;
+ bones_ptr[bone_idx + 2] = p1;
+ bones_ptr[bone_idx + 3] = p_right;
}
add_collision_segments(_collision_segments);
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
index 9a667d5775..075c856c1c 100644
--- a/editor/register_editor_types.cpp
+++ b/editor/register_editor_types.cpp
@@ -129,7 +129,7 @@
#include "editor/register_exporters.h"
void register_editor_types() {
- OS::get_singleton()->benchmark_begin_measure("register_editor_types");
+ OS::get_singleton()->benchmark_begin_measure("Editor", "Register Types");
ResourceLoader::set_timestamp_on_load(true);
ResourceSaver::set_timestamp_on_save(true);
@@ -282,11 +282,11 @@ void register_editor_types() {
ei_singleton.editor_only = true;
Engine::get_singleton()->add_singleton(ei_singleton);
- OS::get_singleton()->benchmark_end_measure("register_editor_types");
+ OS::get_singleton()->benchmark_end_measure("Editor", "Register Types");
}
void unregister_editor_types() {
- OS::get_singleton()->benchmark_begin_measure("unregister_editor_types");
+ OS::get_singleton()->benchmark_begin_measure("Editor", "Unregister Types");
EditorNode::cleanup();
EditorInterface::free();
@@ -296,5 +296,5 @@ void unregister_editor_types() {
}
EditorStringNames::free();
- OS::get_singleton()->benchmark_end_measure("unregister_editor_types");
+ OS::get_singleton()->benchmark_end_measure("Editor", "Unregister Types");
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 65e328d287..4e62ed4667 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -4039,21 +4039,21 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
vbc->add_child(button_hb);
edit_remote = memnew(Button);
- edit_remote->set_flat(true);
- button_hb->add_child(edit_remote);
+ edit_remote->set_theme_type_variation("FlatButton");
edit_remote->set_h_size_flags(SIZE_EXPAND_FILL);
edit_remote->set_text(TTR("Remote"));
edit_remote->set_toggle_mode(true);
edit_remote->set_tooltip_text(TTR("If selected, the Remote scene tree dock will cause the project to stutter every time it updates.\nSwitch back to the Local scene tree dock to improve performance."));
+ button_hb->add_child(edit_remote);
edit_remote->connect("pressed", callable_mp(this, &SceneTreeDock::_remote_tree_selected));
edit_local = memnew(Button);
- edit_local->set_flat(true);
- button_hb->add_child(edit_local);
+ edit_local->set_theme_type_variation("FlatButton");
edit_local->set_h_size_flags(SIZE_EXPAND_FILL);
edit_local->set_text(TTR("Local"));
edit_local->set_toggle_mode(true);
edit_local->set_pressed(true);
+ button_hb->add_child(edit_local);
edit_local->connect("pressed", callable_mp(this, &SceneTreeDock::_local_tree_selected));
remote_tree = nullptr;
diff --git a/main/main.cpp b/main/main.cpp
index 95d414a9f7..ef78f45496 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -510,6 +510,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" The target directory must exist.\n");
OS::get_singleton()->print(" --export-debug <preset> <path> Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n");
OS::get_singleton()->print(" --export-pack <preset> <path> Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n");
+ OS::get_singleton()->print(" --install-android-build-template Install the android build template. Used in conjunction with --export-release or --export-debug.\n");
#ifndef DISABLE_DEPRECATED
OS::get_singleton()->print(" --convert-3to4 [<max_file_kb>] [<max_line_size>]\n");
OS::get_singleton()->print(" Converts project from Godot 3.x to Godot 4.x.\n");
@@ -765,12 +766,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// Benchmark tracking must be done after `OS::get_singleton()->initialize()` as on some
// platforms, it's used to set up the time utilities.
- OS::get_singleton()->benchmark_begin_measure("startup_begin");
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Total");
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Setup");
engine = memnew(Engine);
MAIN_PRINT("Main: Initialize CORE");
- OS::get_singleton()->benchmark_begin_measure("core");
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Core");
register_core_types();
register_core_driver_types();
@@ -2179,11 +2181,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
Thread::release_main_thread(); // If setup2() is called from another thread, that one will become main thread, so preventively release this one.
set_current_thread_safe_for_nodes(false);
+ OS::get_singleton()->benchmark_end_measure("Startup", "Core");
+
if (p_second_phase) {
return setup2();
}
- OS::get_singleton()->benchmark_end_measure("core");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Setup");
return OK;
error:
@@ -2237,7 +2241,8 @@ error:
memdelete(message_queue);
}
- OS::get_singleton()->benchmark_end_measure("core");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Core");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Setup");
OS::get_singleton()->finalize_core();
locale = String();
@@ -2271,25 +2276,10 @@ Error Main::setup2() {
// Print engine name and version
print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
- OS::get_singleton()->benchmark_begin_measure("servers");
-
- tsman = memnew(TextServerManager);
-
- if (tsman) {
- Ref<TextServerDummy> ts;
- ts.instantiate();
- tsman->add_interface(ts);
- }
-
- physics_server_3d_manager = memnew(PhysicsServer3DManager);
- physics_server_2d_manager = memnew(PhysicsServer2DManager);
-
- register_server_types();
- initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
- GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
-
#ifdef TOOLS_ENABLED
if (editor || project_manager || cmdline_tool) {
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Initialize Early Settings");
+
EditorPaths::create();
// Editor setting class is not available, load config directly.
@@ -2351,16 +2341,49 @@ Error Main::setup2() {
return FAILED;
}
}
+
+ OS::get_singleton()->benchmark_end_measure("Startup", "Initialize Early Settings");
}
#endif
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Servers");
+
+ tsman = memnew(TextServerManager);
+ if (tsman) {
+ Ref<TextServerDummy> ts;
+ ts.instantiate();
+ tsman->add_interface(ts);
+ }
+
+ physics_server_3d_manager = memnew(PhysicsServer3DManager);
+ physics_server_2d_manager = memnew(PhysicsServer2DManager);
+
+ register_server_types();
+ {
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Modules and Extensions");
+
+ initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
+ GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Modules and Extensions");
+ }
+
/* Initialize Input */
- input = memnew(Input);
+ {
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Input");
+
+ input = memnew(Input);
+ OS::get_singleton()->initialize_joypads();
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Input");
+ }
/* Initialize Display Server */
{
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Display");
+
String display_driver = DisplayServer::get_create_function_name(display_driver_idx);
Vector2i *window_position = nullptr;
@@ -2394,10 +2417,12 @@ Error Main::setup2() {
ERR_PRINT("Unable to create DisplayServer, all display drivers failed.");
return err;
}
- }
- if (display_server->has_feature(DisplayServer::FEATURE_ORIENTATION)) {
- display_server->screen_set_orientation(window_orientation);
+ if (display_server->has_feature(DisplayServer::FEATURE_ORIENTATION)) {
+ display_server->screen_set_orientation(window_orientation);
+ }
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Display");
}
if (GLOBAL_GET("debug/settings/stdout/print_fps") || print_fps) {
@@ -2425,48 +2450,58 @@ Error Main::setup2() {
/* Initialize Pen Tablet Driver */
{
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Tablet Driver");
+
GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", "");
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "wintab,winink"), "");
- }
- if (tablet_driver.is_empty()) { // specified in project.godot
- tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver");
- if (tablet_driver.is_empty()) {
- tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(0);
+ if (tablet_driver.is_empty()) { // specified in project.godot
+ tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver");
+ if (tablet_driver.is_empty()) {
+ tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(0);
+ }
}
- }
- for (int i = 0; i < DisplayServer::get_singleton()->tablet_get_driver_count(); i++) {
- if (tablet_driver == DisplayServer::get_singleton()->tablet_get_driver_name(i)) {
- DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(i));
- break;
+ for (int i = 0; i < DisplayServer::get_singleton()->tablet_get_driver_count(); i++) {
+ if (tablet_driver == DisplayServer::get_singleton()->tablet_get_driver_name(i)) {
+ DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(i));
+ break;
+ }
}
- }
- if (DisplayServer::get_singleton()->tablet_get_current_driver().is_empty()) {
- DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(0));
- }
+ if (DisplayServer::get_singleton()->tablet_get_current_driver().is_empty()) {
+ DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(0));
+ }
- print_verbose("Using \"" + tablet_driver + "\" pen tablet driver...");
+ print_verbose("Using \"" + tablet_driver + "\" pen tablet driver...");
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Tablet Driver");
+ }
/* Initialize Rendering Server */
- rendering_server = memnew(RenderingServerDefault(OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD));
+ {
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Rendering");
+
+ rendering_server = memnew(RenderingServerDefault(OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD));
- rendering_server->init();
- //rendering_server->call_set_use_vsync(OS::get_singleton()->_use_vsync);
- rendering_server->set_render_loop_enabled(!disable_render_loop);
+ rendering_server->init();
+ //rendering_server->call_set_use_vsync(OS::get_singleton()->_use_vsync);
+ rendering_server->set_render_loop_enabled(!disable_render_loop);
- if (profile_gpu || (!editor && bool(GLOBAL_GET("debug/settings/stdout/print_gpu_profile")))) {
- rendering_server->set_print_gpu_profile(true);
- }
+ if (profile_gpu || (!editor && bool(GLOBAL_GET("debug/settings/stdout/print_gpu_profile")))) {
+ rendering_server->set_print_gpu_profile(true);
+ }
- if (Engine::get_singleton()->get_write_movie_path() != String()) {
- movie_writer = MovieWriter::find_writer_for_file(Engine::get_singleton()->get_write_movie_path());
- if (movie_writer == nullptr) {
- ERR_PRINT("Can't find movie writer for file type, aborting: " + Engine::get_singleton()->get_write_movie_path());
- Engine::get_singleton()->set_write_movie_path(String());
+ if (Engine::get_singleton()->get_write_movie_path() != String()) {
+ movie_writer = MovieWriter::find_writer_for_file(Engine::get_singleton()->get_write_movie_path());
+ if (movie_writer == nullptr) {
+ ERR_PRINT("Can't find movie writer for file type, aborting: " + Engine::get_singleton()->get_write_movie_path());
+ Engine::get_singleton()->set_write_movie_path(String());
+ }
}
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Rendering");
}
#ifdef UNIX_ENABLED
@@ -2476,210 +2511,246 @@ Error Main::setup2() {
}
#endif
- OS::get_singleton()->initialize_joypads();
-
/* Initialize Audio Driver */
- AudioDriverManager::initialize(audio_driver_idx);
+ {
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Audio");
+
+ AudioDriverManager::initialize(audio_driver_idx);
+
+ print_line(" "); // Add a blank line for readability.
- print_line(" "); //add a blank line for readability
+ // Right moment to create and initialize the audio server.
+ audio_server = memnew(AudioServer);
+ audio_server->init();
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Audio");
+ }
+
+ /* Initialize XR Server */
+
+ {
+ OS::get_singleton()->benchmark_begin_measure("Servers", "XR");
- // right moment to create and initialize the audio server
+ xr_server = memnew(XRServer);
- audio_server = memnew(AudioServer);
- audio_server->init();
+ OS::get_singleton()->benchmark_end_measure("Servers", "XR");
+ }
- // also init our xr_server from here
- xr_server = memnew(XRServer);
+ OS::get_singleton()->benchmark_end_measure("Startup", "Servers");
register_core_singletons();
- MAIN_PRINT("Main: Setup Logo");
+ /* Initialize the main window and boot screen */
+
+ {
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Setup Window and Boot");
+
+ MAIN_PRINT("Main: Setup Logo");
#if !defined(TOOLS_ENABLED) && (defined(WEB_ENABLED) || defined(ANDROID_ENABLED))
- bool show_logo = false;
+ bool show_logo = false;
#else
- bool show_logo = true;
+ bool show_logo = true;
#endif
- if (init_windowed) {
- //do none..
- } else if (init_maximized) {
- DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_MAXIMIZED);
- } else if (init_fullscreen) {
- DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_FULLSCREEN);
- }
- if (init_always_on_top) {
- DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, true);
- }
+ if (init_windowed) {
+ //do none..
+ } else if (init_maximized) {
+ DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_MAXIMIZED);
+ } else if (init_fullscreen) {
+ DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_FULLSCREEN);
+ }
+ if (init_always_on_top) {
+ DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, true);
+ }
- MAIN_PRINT("Main: Load Boot Image");
+ MAIN_PRINT("Main: Load Boot Image");
- Color clear = GLOBAL_DEF_BASIC("rendering/environment/defaults/default_clear_color", Color(0.3, 0.3, 0.3));
- RenderingServer::get_singleton()->set_default_clear_color(clear);
+ Color clear = GLOBAL_DEF_BASIC("rendering/environment/defaults/default_clear_color", Color(0.3, 0.3, 0.3));
+ RenderingServer::get_singleton()->set_default_clear_color(clear);
- if (show_logo) { //boot logo!
- const bool boot_logo_image = GLOBAL_DEF_BASIC("application/boot_splash/show_image", true);
- const String boot_logo_path = String(GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"), String())).strip_edges();
- const bool boot_logo_scale = GLOBAL_DEF_BASIC("application/boot_splash/fullsize", true);
- const bool boot_logo_filter = GLOBAL_DEF_BASIC("application/boot_splash/use_filter", true);
+ if (show_logo) { //boot logo!
+ const bool boot_logo_image = GLOBAL_DEF_BASIC("application/boot_splash/show_image", true);
+ const String boot_logo_path = String(GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"), String())).strip_edges();
+ const bool boot_logo_scale = GLOBAL_DEF_BASIC("application/boot_splash/fullsize", true);
+ const bool boot_logo_filter = GLOBAL_DEF_BASIC("application/boot_splash/use_filter", true);
- Ref<Image> boot_logo;
+ Ref<Image> boot_logo;
- if (boot_logo_image) {
- if (!boot_logo_path.is_empty()) {
- boot_logo.instantiate();
- Error load_err = ImageLoader::load_image(boot_logo_path, boot_logo);
- if (load_err) {
- ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + "'. Loading default splash.");
+ if (boot_logo_image) {
+ if (!boot_logo_path.is_empty()) {
+ boot_logo.instantiate();
+ Error load_err = ImageLoader::load_image(boot_logo_path, boot_logo);
+ if (load_err) {
+ ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + "'. Loading default splash.");
+ }
}
+ } else {
+ // Create a 1×1 transparent image. This will effectively hide the splash image.
+ boot_logo.instantiate();
+ boot_logo->initialize_data(1, 1, false, Image::FORMAT_RGBA8);
+ boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0));
}
- } else {
- // Create a 1×1 transparent image. This will effectively hide the splash image.
- boot_logo.instantiate();
- boot_logo->initialize_data(1, 1, false, Image::FORMAT_RGBA8);
- boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0));
- }
- Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color");
+ Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color");
#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
- boot_bg_color =
- GLOBAL_DEF_BASIC("application/boot_splash/bg_color",
- (editor || project_manager) ? boot_splash_editor_bg_color : boot_splash_bg_color);
+ boot_bg_color =
+ GLOBAL_DEF_BASIC("application/boot_splash/bg_color",
+ (editor || project_manager) ? boot_splash_editor_bg_color : boot_splash_bg_color);
#endif
- if (boot_logo.is_valid()) {
- RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale,
- boot_logo_filter);
+ if (boot_logo.is_valid()) {
+ RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale,
+ boot_logo_filter);
- } else {
+ } else {
#ifndef NO_DEFAULT_BOOT_LOGO
- MAIN_PRINT("Main: Create bootsplash");
+ MAIN_PRINT("Main: Create bootsplash");
#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
- Ref<Image> splash = (editor || project_manager) ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png));
+ Ref<Image> splash = (editor || project_manager) ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png));
#else
- Ref<Image> splash = memnew(Image(boot_splash_png));
+ Ref<Image> splash = memnew(Image(boot_splash_png));
#endif
- MAIN_PRINT("Main: ClearColor");
- RenderingServer::get_singleton()->set_default_clear_color(boot_bg_color);
- MAIN_PRINT("Main: Image");
- RenderingServer::get_singleton()->set_boot_image(splash, boot_bg_color, false);
+ MAIN_PRINT("Main: ClearColor");
+ RenderingServer::get_singleton()->set_default_clear_color(boot_bg_color);
+ MAIN_PRINT("Main: Image");
+ RenderingServer::get_singleton()->set_boot_image(splash, boot_bg_color, false);
#endif
- }
+ }
#if defined(TOOLS_ENABLED) && defined(MACOS_ENABLED)
- if (OS::get_singleton()->get_bundle_icon_path().is_empty()) {
- Ref<Image> icon = memnew(Image(app_icon_png));
- DisplayServer::get_singleton()->set_icon(icon);
- }
+ if (OS::get_singleton()->get_bundle_icon_path().is_empty()) {
+ Ref<Image> icon = memnew(Image(app_icon_png));
+ DisplayServer::get_singleton()->set_icon(icon);
+ }
#endif
- }
+ }
+
+ MAIN_PRINT("Main: Clear Color");
- DisplayServer::set_early_window_clear_color_override(false);
+ DisplayServer::set_early_window_clear_color_override(false);
+ RenderingServer::get_singleton()->set_default_clear_color(
+ GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
- MAIN_PRINT("Main: DCC");
- RenderingServer::get_singleton()->set_default_clear_color(
- GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), String());
+ GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"), String());
+ GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/windows_native_icon", PROPERTY_HINT_FILE, "*.ico"), String());
- GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), String());
- GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"), String());
- GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/windows_native_icon", PROPERTY_HINT_FILE, "*.ico"), String());
+ MAIN_PRINT("Main: Touch Input");
- Input *id = Input::get_singleton();
- if (id) {
- agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false);
+ Input *id = Input::get_singleton();
+ if (id) {
+ agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false);
- if (bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_touch_from_mouse", false)) &&
- !(editor || project_manager)) {
- if (!DisplayServer::get_singleton()->is_touchscreen_available()) {
- //only if no touchscreen ui hint, set emulation
- id->set_emulate_touch_from_mouse(true);
+ if (bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_touch_from_mouse", false)) &&
+ !(editor || project_manager)) {
+ if (!DisplayServer::get_singleton()->is_touchscreen_available()) {
+ //only if no touchscreen ui hint, set emulation
+ id->set_emulate_touch_from_mouse(true);
+ }
}
+
+ id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
}
- id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
- }
+ GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
+ GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
- GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
- GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
+ OS::get_singleton()->benchmark_end_measure("Startup", "Setup Window and Boot");
+ }
MAIN_PRINT("Main: Load Translations and Remaps");
- translation_server->setup(); //register translations, load them, etc.
- if (!locale.is_empty()) {
- translation_server->set_locale(locale);
- }
- translation_server->load_translations();
- ResourceLoader::load_translation_remaps(); //load remaps for resources
+ /* Setup translations and remaps */
- ResourceLoader::load_path_remaps();
-
- MAIN_PRINT("Main: Load TextServer");
+ {
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Translations and Remaps");
- /* Enum text drivers */
- GLOBAL_DEF_RST("internationalization/rendering/text_driver", "");
- String text_driver_options;
- for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
- const String driver_name = TextServerManager::get_singleton()->get_interface(i)->get_name();
- if (driver_name == "Dummy") {
- // Dummy text driver cannot draw any text, making the editor unusable if selected.
- continue;
+ translation_server->setup(); //register translations, load them, etc.
+ if (!locale.is_empty()) {
+ translation_server->set_locale(locale);
}
- if (!text_driver_options.is_empty() && text_driver_options.find(",") == -1) {
- // Not the first option; add a comma before it as a separator for the property hint.
- text_driver_options += ",";
- }
- text_driver_options += driver_name;
- }
- ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/rendering/text_driver", PROPERTY_HINT_ENUM, text_driver_options));
+ translation_server->load_translations();
+ ResourceLoader::load_translation_remaps(); //load remaps for resources
- /* Determine text driver */
- if (text_driver.is_empty()) {
- text_driver = GLOBAL_GET("internationalization/rendering/text_driver");
+ ResourceLoader::load_path_remaps();
+
+ OS::get_singleton()->benchmark_end_measure("Startup", "Translations and Remaps");
}
- if (!text_driver.is_empty()) {
- /* Load user selected text server. */
+ MAIN_PRINT("Main: Load TextServer");
+
+ /* Setup Text Server */
+
+ {
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Text Server");
+
+ /* Enum text drivers */
+ GLOBAL_DEF_RST("internationalization/rendering/text_driver", "");
+ String text_driver_options;
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
- if (TextServerManager::get_singleton()->get_interface(i)->get_name() == text_driver) {
- text_driver_idx = i;
- break;
+ const String driver_name = TextServerManager::get_singleton()->get_interface(i)->get_name();
+ if (driver_name == "Dummy") {
+ // Dummy text driver cannot draw any text, making the editor unusable if selected.
+ continue;
+ }
+ if (!text_driver_options.is_empty() && text_driver_options.find(",") == -1) {
+ // Not the first option; add a comma before it as a separator for the property hint.
+ text_driver_options += ",";
}
+ text_driver_options += driver_name;
}
- }
+ ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/rendering/text_driver", PROPERTY_HINT_ENUM, text_driver_options));
- if (text_driver_idx < 0) {
- /* If not selected, use one with the most features available. */
- int max_features = 0;
- for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
- uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features();
- int feature_number = 0;
- while (features) {
- feature_number += features & 1;
- features >>= 1;
+ /* Determine text driver */
+ if (text_driver.is_empty()) {
+ text_driver = GLOBAL_GET("internationalization/rendering/text_driver");
+ }
+
+ if (!text_driver.is_empty()) {
+ /* Load user selected text server. */
+ for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
+ if (TextServerManager::get_singleton()->get_interface(i)->get_name() == text_driver) {
+ text_driver_idx = i;
+ break;
+ }
}
- if (feature_number >= max_features) {
- max_features = feature_number;
- text_driver_idx = i;
+ }
+
+ if (text_driver_idx < 0) {
+ /* If not selected, use one with the most features available. */
+ int max_features = 0;
+ for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
+ uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features();
+ int feature_number = 0;
+ while (features) {
+ feature_number += features & 1;
+ features >>= 1;
+ }
+ if (feature_number >= max_features) {
+ max_features = feature_number;
+ text_driver_idx = i;
+ }
}
}
- }
- if (text_driver_idx >= 0) {
- Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(text_driver_idx);
- TextServerManager::get_singleton()->set_primary_interface(ts);
- if (ts->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
- ts->load_support_data("res://" + ts->get_support_data_filename());
+ if (text_driver_idx >= 0) {
+ Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(text_driver_idx);
+ TextServerManager::get_singleton()->set_primary_interface(ts);
+ if (ts->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
+ ts->load_support_data("res://" + ts->get_support_data_filename());
+ }
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface.");
}
- } else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface.");
- }
- OS::get_singleton()->benchmark_end_measure("servers");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Text Server");
+ }
MAIN_PRINT("Main: Load Scene Types");
- OS::get_singleton()->benchmark_begin_measure("scene");
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Scene");
// Initialize ThemeDB early so that scene types can register their theme items.
// Default theme will be initialized later, after modules and ScriptServer are ready.
@@ -2690,23 +2761,42 @@ Error Main::setup2() {
register_scene_singletons();
- initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
- GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
+ {
+ OS::get_singleton()->benchmark_begin_measure("Scene", "Modules and Extensions");
+
+ initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
+ GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
+
+ OS::get_singleton()->benchmark_end_measure("Scene", "Modules and Extensions");
+ }
+
+ OS::get_singleton()->benchmark_end_measure("Startup", "Scene");
#ifdef TOOLS_ENABLED
ClassDB::set_current_api(ClassDB::API_EDITOR);
register_editor_types();
- initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
- GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
+
+ {
+ OS::get_singleton()->benchmark_begin_measure("Editor", "Modules and Extensions");
+
+ initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
+ GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
+
+ OS::get_singleton()->benchmark_end_measure("Editor", "Modules and Extensions");
+ }
ClassDB::set_current_api(ClassDB::API_CORE);
#endif
- MAIN_PRINT("Main: Load Modules");
+ MAIN_PRINT("Main: Load Platforms");
+
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Platforms");
register_platform_apis();
+ OS::get_singleton()->benchmark_end_measure("Startup", "Platforms");
+
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/mouse_cursor/custom_image", PROPERTY_HINT_FILE, "*.png,*.webp"), String());
GLOBAL_DEF_BASIC("display/mouse_cursor/custom_image_hotspot", Vector2());
GLOBAL_DEF_BASIC("display/mouse_cursor/tooltip_position_offset", Point2(10, 10));
@@ -2720,12 +2810,18 @@ Error Main::setup2() {
}
}
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Finalize Setup");
+
camera_server = CameraServer::create();
MAIN_PRINT("Main: Load Physics");
initialize_physics();
+
+ MAIN_PRINT("Main: Load Navigation");
+
initialize_navigation_server();
+
register_server_singletons();
// This loads global classes, so it must happen before custom loaders and savers are registered
@@ -2757,6 +2853,8 @@ Error Main::setup2() {
rendering_server->global_shader_parameters_load_settings(!editor);
}
+ OS::get_singleton()->benchmark_end_measure("Startup", "Finalize Setup");
+
_start_success = true;
ClassDB::set_current_api(ClassDB::API_NONE); //no more APIs are registered at this point
@@ -2765,7 +2863,7 @@ Error Main::setup2() {
print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR)));
MAIN_PRINT("Main: Done");
- OS::get_singleton()->benchmark_end_measure("scene");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Setup");
return OK;
}
@@ -2793,6 +2891,7 @@ bool Main::start() {
String _export_preset;
bool export_debug = false;
bool export_pack_only = false;
+ bool install_android_build_template = false;
#ifdef MODULE_GDSCRIPT_ENABLED
String gdscript_docs_path;
#endif
@@ -2825,6 +2924,8 @@ bool Main::start() {
editor = true;
} else if (args[i] == "-p" || args[i] == "--project-manager") {
project_manager = true;
+ } else if (args[i] == "--install-android-build-template") {
+ install_android_build_template = true;
#endif // TOOLS_ENABLED
} else if (args[i].length() && args[i][0] != '-' && positional_arg.is_empty()) {
positional_arg = args[i];
@@ -3169,7 +3270,7 @@ bool Main::start() {
if (!project_manager && !editor) { // game
if (!game_path.is_empty() || !script.is_empty()) {
//autoload
- OS::get_singleton()->benchmark_begin_measure("load_autoloads");
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Load Autoloads");
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
//first pass, add the constants so they exist before any script is loaded
@@ -3234,7 +3335,7 @@ bool Main::start() {
for (Node *E : to_add) {
sml->get_root()->add_child(E);
}
- OS::get_singleton()->benchmark_end_measure("load_autoloads");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Load Autoloads");
}
}
@@ -3273,16 +3374,16 @@ bool Main::start() {
EditorNode *editor_node = nullptr;
if (editor) {
- OS::get_singleton()->benchmark_begin_measure("editor");
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Editor");
editor_node = memnew(EditorNode);
sml->get_root()->add_child(editor_node);
if (!_export_preset.is_empty()) {
- editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only);
+ editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only, install_android_build_template);
game_path = ""; // Do not load anything.
}
- OS::get_singleton()->benchmark_end_measure("editor");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Editor");
}
#endif
sml->set_auto_accept_quit(GLOBAL_GET("application/config/auto_accept_quit"));
@@ -3420,7 +3521,7 @@ bool Main::start() {
if (!project_manager && !editor) { // game
- OS::get_singleton()->benchmark_begin_measure("game_load");
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Load Game");
// Load SSL Certificates from Project Settings (or builtin).
Crypto::load_default_certificates(GLOBAL_GET("network/tls/certificate_bundle_override"));
@@ -3462,19 +3563,19 @@ bool Main::start() {
}
}
- OS::get_singleton()->benchmark_end_measure("game_load");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Load Game");
}
#ifdef TOOLS_ENABLED
if (project_manager) {
- OS::get_singleton()->benchmark_begin_measure("project_manager");
+ OS::get_singleton()->benchmark_begin_measure("Startup", "Project Manager");
Engine::get_singleton()->set_editor_hint(true);
ProjectManager *pmanager = memnew(ProjectManager);
ProgressDialog *progress_dialog = memnew(ProgressDialog);
pmanager->add_child(progress_dialog);
sml->get_root()->add_child(pmanager);
DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_PROJECTMAN);
- OS::get_singleton()->benchmark_end_measure("project_manager");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Project Manager");
}
if (project_manager || editor) {
@@ -3502,7 +3603,7 @@ bool Main::start() {
}
}
- OS::get_singleton()->benchmark_end_measure("startup_begin");
+ OS::get_singleton()->benchmark_end_measure("Startup", "Total");
OS::get_singleton()->benchmark_dump();
return true;
@@ -3751,7 +3852,7 @@ void Main::force_redraw() {
* The order matters as some of those steps are linked with each other.
*/
void Main::cleanup(bool p_force) {
- OS::get_singleton()->benchmark_begin_measure("Main::cleanup");
+ OS::get_singleton()->benchmark_begin_measure("Shutdown", "Total");
if (!p_force) {
ERR_FAIL_COND(!_start_success);
}
@@ -3892,7 +3993,7 @@ void Main::cleanup(bool p_force) {
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
unregister_core_types();
- OS::get_singleton()->benchmark_end_measure("Main::cleanup");
+ OS::get_singleton()->benchmark_end_measure("Shutdown", "Total");
OS::get_singleton()->benchmark_dump();
OS::get_singleton()->finalize_core();
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
index c3979dd290..00179109a3 100644
--- a/modules/gdscript/editor/gdscript_docgen.cpp
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -64,8 +64,8 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type
r_type = p_is_return ? "void" : "null";
return;
}
- if (p_gdtype.builtin_type == Variant::ARRAY && p_gdtype.has_container_element_type()) {
- _doctype_from_gdtype(p_gdtype.get_container_element_type(), r_type, r_enum);
+ if (p_gdtype.builtin_type == Variant::ARRAY && p_gdtype.has_container_element_type(0)) {
+ _doctype_from_gdtype(p_gdtype.get_container_element_type(0), r_type, r_enum);
if (!r_enum.is_empty()) {
r_type = "int[]";
r_enum += "[]";
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 8dbd262b22..3df07f9794 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -494,7 +494,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_function_arg_dicts = 0;
}
- if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[' && str[j] != '.') {
+ if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[' && str[j] != ',' && str[j] != '.') {
expect_type = false;
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 983a19470a..55bd0f97d5 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -685,10 +685,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result.builtin_type = GDScriptParser::get_builtin_type(first);
if (result.builtin_type == Variant::ARRAY) {
- GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type));
+ GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0)));
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
container_type.is_constant = false;
- result.set_container_element_type(container_type);
+ result.set_container_element_type(0, container_type);
}
}
} else if (class_exists(first)) {
@@ -829,8 +829,16 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
}
- if (result.builtin_type != Variant::ARRAY && p_type->container_type != nullptr) {
- push_error("Only arrays can specify the collection element type.", p_type);
+ if (!p_type->container_types.is_empty()) {
+ if (result.builtin_type == Variant::ARRAY) {
+ if (p_type->container_types.size() != 1) {
+ push_error("Arrays require exactly one collection element type.", p_type);
+ return bad_type;
+ }
+ } else {
+ push_error("Only arrays can specify collection element types.", p_type);
+ return bad_type;
+ }
}
p_type->set_datatype(result);
@@ -1891,8 +1899,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer);
- if (has_specified_type && specified_type.has_container_element_type()) {
- update_array_literal_element_type(array, specified_type.get_container_element_type());
+ if (has_specified_type && specified_type.has_container_element_type(0)) {
+ update_array_literal_element_type(array, specified_type.get_container_element_type(0));
}
}
@@ -1955,7 +1963,7 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
} else {
push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
}
- } else if (specified_type.has_container_element_type() && !initializer_type.has_container_element_type()) {
+ } else if (specified_type.has_container_element_type(0) && !initializer_type.has_container_element_type(0)) {
mark_node_unsafe(p_assignable->initializer);
#ifdef DEBUG_ENABLED
} else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) {
@@ -2127,8 +2135,8 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (list_type.is_variant()) {
variable_type.kind = GDScriptParser::DataType::VARIANT;
mark_node_unsafe(p_for->list);
- } else if (list_type.has_container_element_type()) {
- variable_type = list_type.get_container_element_type();
+ } else if (list_type.has_container_element_type(0)) {
+ variable_type = list_type.get_container_element_type(0);
variable_type.type_source = list_type.type_source;
} else if (list_type.is_typed_container_type()) {
variable_type = list_type.get_typed_container_type();
@@ -2377,8 +2385,8 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
result.builtin_type = Variant::NIL;
result.is_constant = true;
} else {
- if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) {
- update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type());
+ if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type(0)) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type(0));
}
if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) {
update_const_expression_builtin_type(p_return->return_value, expected_type, "return");
@@ -2598,7 +2606,7 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr
// This function determines which type is that (if any).
void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) {
GDScriptParser::DataType expected_type = p_element_type;
- expected_type.unset_container_element_type(); // Nested types (like `Array[Array[int]]`) are not currently supported.
+ expected_type.container_element_types.clear(); // Nested types (like `Array[Array[int]]`) are not currently supported.
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element_node = p_array->elements[i];
@@ -2621,7 +2629,7 @@ void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNo
}
GDScriptParser::DataType array_type = p_array->get_datatype();
- array_type.set_container_element_type(expected_type);
+ array_type.set_container_element_type(0, expected_type);
p_array->set_datatype(array_type);
}
@@ -2668,8 +2676,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
- if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type()) {
- update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type());
+ if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type(0)) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type(0));
}
if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) {
@@ -2747,7 +2755,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
// weak non-variant assignee and incompatible result
downgrades_assignee = true;
}
- } else if (assignee_type.has_container_element_type() && !op_type.has_container_element_type()) {
+ } else if (assignee_type.has_container_element_type(0) && !op_type.has_container_element_type(0)) {
// typed array assignee and untyped array result
mark_node_unsafe(p_assignment);
}
@@ -3311,8 +3319,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
// If the function requires typed arrays we must make literals be typed.
for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
int index = E.key;
- if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type()) {
- update_array_literal_element_type(E.value, par_types[index].get_container_element_type());
+ if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type(0)) {
+ update_array_literal_element_type(E.value, par_types[index].get_container_element_type(0));
}
}
validate_call_arg(par_types, default_arg_count, method_flags.has_flag(METHOD_FLAG_VARARG), p_call);
@@ -3444,8 +3452,8 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
}
}
- if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type()) {
- update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type());
+ if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type(0)) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type(0));
}
if (!cast_type.is_variant()) {
@@ -4432,8 +4440,8 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
break;
// Can have an element type.
case Variant::ARRAY:
- if (base_type.has_container_element_type()) {
- result_type = base_type.get_container_element_type();
+ if (base_type.has_container_element_type(0)) {
+ result_type = base_type.get_container_element_type(0);
result_type.type_source = base_type.type_source;
} else {
result_type.kind = GDScriptParser::DataType::VARIANT;
@@ -4597,7 +4605,7 @@ Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::Expressi
}
Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) {
- Array array = p_array->get_datatype().has_container_element_type() ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type()) : Array();
+ Array array = p_array->get_datatype().has_container_element_type(0) ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type(0)) : Array();
array.resize(p_array->elements.size());
for (int i = 0; i < p_array->elements.size(); i++) {
@@ -4719,8 +4727,8 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
GDScriptParser::DataType datatype = p_variable->get_datatype();
if (datatype.is_hard_type()) {
if (datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
- if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) {
- result = make_array_from_element_datatype(datatype.get_container_element_type());
+ if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type(0)) {
+ result = make_array_from_element_datatype(datatype.get_container_element_type(0));
} else {
VariantInternal::initialize(&result, datatype.builtin_type);
}
@@ -4747,11 +4755,11 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
if (p_value.get_type() == Variant::ARRAY) {
const Array &array = p_value;
if (array.get_typed_script()) {
- result.set_container_element_type(type_from_metatype(make_script_meta_type(array.get_typed_script())));
+ result.set_container_element_type(0, type_from_metatype(make_script_meta_type(array.get_typed_script())));
} else if (array.get_typed_class_name()) {
- result.set_container_element_type(type_from_metatype(make_native_meta_type(array.get_typed_class_name())));
+ result.set_container_element_type(0, type_from_metatype(make_native_meta_type(array.get_typed_class_name())));
} else if (array.get_typed_builtin() != Variant::NIL) {
- result.set_container_element_type(type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin())));
+ result.set_container_element_type(0, type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin())));
}
} else if (p_value.get_type() == Variant::OBJECT) {
// Object is treated as a native type, not a builtin type.
@@ -4873,7 +4881,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed array.");
}
elem_type.is_constant = false;
- result.set_container_element_type(elem_type);
+ result.set_container_element_type(0, elem_type);
} else if (p_property.type == Variant::INT) {
// Check if it's enum.
if ((p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && p_property.class_name != StringName()) {
@@ -5225,7 +5233,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
if (p_operation == Variant::OP_ADD && a_type == Variant::ARRAY && b_type == Variant::ARRAY) {
- if (p_a.has_container_element_type() && p_b.has_container_element_type() && p_a.get_container_element_type() == p_b.get_container_element_type()) {
+ if (p_a.has_container_element_type(0) && p_b.has_container_element_type(0) && p_a.get_container_element_type(0) == p_b.get_container_element_type(0)) {
r_valid = true;
result = p_a;
result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
@@ -5276,8 +5284,8 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
}
if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) {
// Check the element type.
- if (p_target.has_container_element_type() && p_source.has_container_element_type()) {
- valid = p_target.get_container_element_type() == p_source.get_container_element_type();
+ if (p_target.has_container_element_type(0) && p_source.has_container_element_type(0)) {
+ valid = p_target.get_container_element_type(0) == p_source.get_container_element_type(0);
}
}
return valid;
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 25e20c0e76..27766115d5 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -623,8 +623,8 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
switch (p_type.kind) {
case GDScriptDataType::BUILTIN: {
- if (p_type.builtin_type == Variant::ARRAY && p_type.has_container_element_type()) {
- const GDScriptDataType &element_type = p_type.get_container_element_type();
+ if (p_type.builtin_type == Variant::ARRAY && p_type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = p_type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_ARRAY);
append(p_target);
append(p_source);
@@ -878,8 +878,8 @@ void GDScriptByteCodeGenerator::write_get_static_variable(const Address &p_targe
void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) {
switch (p_target.type.kind) {
case GDScriptDataType::BUILTIN: {
- if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- const GDScriptDataType &element_type = p_target.type.get_container_element_type();
+ if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = p_target.type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY);
append(p_target);
append(p_source);
@@ -924,8 +924,8 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
}
void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
- if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- const GDScriptDataType &element_type = p_target.type.get_container_element_type();
+ if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = p_target.type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY);
append(p_target);
append(p_source);
@@ -1666,9 +1666,9 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
// If this is a typed function, then we need to check for potential conversions.
if (function->return_type.has_type) {
- if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) {
+ if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) {
// Typed array.
- const GDScriptDataType &element_type = function->return_type.get_container_element_type();
+ const GDScriptDataType &element_type = function->return_type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
@@ -1691,8 +1691,8 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
} else {
switch (function->return_type.kind) {
case GDScriptDataType::BUILTIN: {
- if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) {
- const GDScriptDataType &element_type = function->return_type.get_container_element_type();
+ if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = function->return_type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 7980f020b8..9560f670e6 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -196,8 +196,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
}
- if (p_datatype.has_container_element_type()) {
- result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner, false));
+ for (int i = 0; i < p_datatype.container_element_types.size(); i++) {
+ result.set_container_element_type(i, _gdtype_from_datatype(p_datatype.get_container_element_type_or_variant(i), p_owner, false));
}
return result;
@@ -507,8 +507,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
values.push_back(val);
}
- if (array_type.has_container_element_type()) {
- gen->write_construct_typed_array(result, array_type.get_container_element_type(), values);
+ if (array_type.has_container_element_type(0)) {
+ gen->write_construct_typed_array(result, array_type.get_container_element_type(0), values);
} else {
gen->write_construct_array(result, values);
}
@@ -2133,8 +2133,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
initialized = true;
} else if (local_type.has_type) {
// Initialize with default for type.
- if (local_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ if (local_type.has_container_element_type(0)) {
+ codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
initialized = true;
} else if (local_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
@@ -2276,8 +2276,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type);
- if (field_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ if (field_type.has_container_element_type(0)) {
+ codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
} else if (field_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
}
@@ -2466,9 +2466,9 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS
if (field_type.has_type) {
codegen.generator->write_newline(field->start_line);
- if (field_type.has_container_element_type()) {
+ if (field_type.has_container_element_type(0)) {
GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);
- codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index);
codegen.generator->pop_temporary();
} else if (field_type.kind == GDScriptDataType::BUILTIN) {
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index c9b543fbb9..e63fa17cfe 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -45,10 +45,9 @@ class GDScriptInstance;
class GDScript;
class GDScriptDataType {
-private:
- GDScriptDataType *container_element_type = nullptr;
-
public:
+ Vector<GDScriptDataType> container_element_types;
+
enum Kind {
UNINITIALIZED,
BUILTIN,
@@ -76,19 +75,20 @@ public:
case BUILTIN: {
Variant::Type var_type = p_variant.get_type();
bool valid = builtin_type == var_type;
- if (valid && builtin_type == Variant::ARRAY && has_container_element_type()) {
+ if (valid && builtin_type == Variant::ARRAY && has_container_element_type(0)) {
Array array = p_variant;
if (array.is_typed()) {
+ GDScriptDataType array_container_type = get_container_element_type(0);
Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin();
StringName array_native_type = array.get_typed_class_name();
Ref<Script> array_script_type_ref = array.get_typed_script();
if (array_script_type_ref.is_valid()) {
- valid = (container_element_type->kind == SCRIPT || container_element_type->kind == GDSCRIPT) && container_element_type->script_type == array_script_type_ref.ptr();
+ valid = (array_container_type.kind == SCRIPT || array_container_type.kind == GDSCRIPT) && array_container_type.script_type == array_script_type_ref.ptr();
} else if (array_native_type != StringName()) {
- valid = container_element_type->kind == NATIVE && container_element_type->native_type == array_native_type;
+ valid = array_container_type.kind == NATIVE && array_container_type.native_type == array_native_type;
} else {
- valid = container_element_type->kind == BUILTIN && container_element_type->builtin_type == array_builtin_type;
+ valid = array_container_type.kind == BUILTIN && array_container_type.builtin_type == array_builtin_type;
}
} else {
valid = false;
@@ -147,24 +147,32 @@ public:
return false;
}
- void set_container_element_type(const GDScriptDataType &p_element_type) {
- container_element_type = memnew(GDScriptDataType(p_element_type));
+ void set_container_element_type(int p_index, const GDScriptDataType &p_element_type) {
+ ERR_FAIL_COND(p_index < 0);
+ while (p_index >= container_element_types.size()) {
+ container_element_types.push_back(GDScriptDataType());
+ }
+ container_element_types.write[p_index] = GDScriptDataType(p_element_type);
+ }
+
+ GDScriptDataType get_container_element_type(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, container_element_types.size(), GDScriptDataType());
+ return container_element_types[p_index];
}
- GDScriptDataType get_container_element_type() const {
- ERR_FAIL_NULL_V(container_element_type, GDScriptDataType());
- return *container_element_type;
+ GDScriptDataType get_container_element_type_or_variant(int p_index) const {
+ if (p_index < 0 || p_index >= container_element_types.size()) {
+ return GDScriptDataType();
+ }
+ return container_element_types[p_index];
}
- bool has_container_element_type() const {
- return container_element_type != nullptr;
+ bool has_container_element_type(int p_index) const {
+ return p_index >= 0 && p_index < container_element_types.size();
}
- void unset_container_element_type() {
- if (container_element_type) {
- memdelete(container_element_type);
- }
- container_element_type = nullptr;
+ bool has_container_element_types() const {
+ return !container_element_types.is_empty();
}
GDScriptDataType() = default;
@@ -176,19 +184,14 @@ public:
native_type = p_other.native_type;
script_type = p_other.script_type;
script_type_ref = p_other.script_type_ref;
- unset_container_element_type();
- if (p_other.has_container_element_type()) {
- set_container_element_type(p_other.get_container_element_type());
- }
+ container_element_types = p_other.container_element_types;
}
GDScriptDataType(const GDScriptDataType &p_other) {
*this = p_other;
}
- ~GDScriptDataType() {
- unset_container_element_type();
- }
+ ~GDScriptDataType() {}
};
class GDScriptFunction {
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index db7b3e7ace..ea7abc9ded 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3337,14 +3337,21 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
if (match(GDScriptTokenizer::Token::BRACKET_OPEN)) {
// Typed collection (like Array[int]).
- type->container_type = parse_type(false); // Don't allow void for array element type.
- if (type->container_type == nullptr) {
- push_error(R"(Expected type for collection after "[".)");
- complete_extents(type);
- type = nullptr;
- } else if (type->container_type->container_type != nullptr) {
- push_error("Nested typed collections are not supported.");
- }
+ bool first_pass = true;
+ do {
+ TypeNode *container_type = parse_type(false); // Don't allow void for element type.
+ if (container_type == nullptr) {
+ push_error(vformat(R"(Expected type for collection after "%s".)", first_pass ? "[" : ","));
+ complete_extents(type);
+ type = nullptr;
+ break;
+ } else if (container_type->container_types.size() > 0) {
+ push_error("Nested typed collections are not supported.");
+ } else {
+ type->container_types.append(container_type);
+ }
+ first_pass = false;
+ } while (match(GDScriptTokenizer::Token::COMMA));
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
if (type != nullptr) {
complete_extents(type);
@@ -3996,8 +4003,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.type = Variant::INT;
}
} else if (p_annotation->name == SNAME("@export_multiline")) {
- if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
- DataType inner_type = export_type.get_container_element_type();
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) {
+ DataType inner_type = export_type.get_container_element_type(0);
if (inner_type.builtin_type != Variant::STRING) {
push_error(vformat(R"("%s" annotation on arrays requires a string type but type "%s" was given instead.)", p_annotation->name.operator String(), inner_type.to_string()), variable);
return false;
@@ -4033,8 +4040,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
bool is_array = false;
- if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
- export_type = export_type.get_container_element_type(); // Use inner type for.
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) {
+ export_type = export_type.get_container_element_type(0); // Use inner type for.
is_array = true;
}
@@ -4344,8 +4351,8 @@ String GDScriptParser::DataType::to_string() const {
if (builtin_type == Variant::NIL) {
return "null";
}
- if (builtin_type == Variant::ARRAY && has_container_element_type()) {
- return vformat("Array[%s]", container_element_type->to_string());
+ if (builtin_type == Variant::ARRAY && has_container_element_type(0)) {
+ return vformat("Array[%s]", container_element_types[0].to_string());
}
return Variant::get_type_name(builtin_type);
case NATIVE:
@@ -4398,36 +4405,36 @@ PropertyInfo GDScriptParser::DataType::to_property_info(const String &p_name) co
switch (kind) {
case BUILTIN:
result.type = builtin_type;
- if (builtin_type == Variant::ARRAY && has_container_element_type()) {
- const DataType *elem_type = container_element_type;
- switch (elem_type->kind) {
+ if (builtin_type == Variant::ARRAY && has_container_element_type(0)) {
+ const DataType elem_type = get_container_element_type(0);
+ switch (elem_type.kind) {
case BUILTIN:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- result.hint_string = Variant::get_type_name(elem_type->builtin_type);
+ result.hint_string = Variant::get_type_name(elem_type.builtin_type);
break;
case NATIVE:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- result.hint_string = elem_type->native_type;
+ result.hint_string = elem_type.native_type;
break;
case SCRIPT:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- if (elem_type->script_type.is_valid() && elem_type->script_type->get_global_name() != StringName()) {
- result.hint_string = elem_type->script_type->get_global_name();
+ if (elem_type.script_type.is_valid() && elem_type.script_type->get_global_name() != StringName()) {
+ result.hint_string = elem_type.script_type->get_global_name();
} else {
- result.hint_string = elem_type->native_type;
+ result.hint_string = elem_type.native_type;
}
break;
case CLASS:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- if (elem_type->class_type != nullptr && elem_type->class_type->get_global_name() != StringName()) {
- result.hint_string = elem_type->class_type->get_global_name();
+ if (elem_type.class_type != nullptr && elem_type.class_type->get_global_name() != StringName()) {
+ result.hint_string = elem_type.class_type->get_global_name();
} else {
- result.hint_string = elem_type->native_type;
+ result.hint_string = elem_type.native_type;
}
break;
case ENUM:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- result.hint_string = String(elem_type->native_type).replace("::", ".");
+ result.hint_string = String(elem_type.native_type).replace("::", ".");
break;
case VARIANT:
case RESOLVING:
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 4b46b98baa..f48ad48de0 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -101,11 +101,9 @@ public:
struct WhileNode;
class DataType {
- private:
- // Private access so we can control memory management.
- DataType *container_element_type = nullptr;
-
public:
+ Vector<DataType> container_element_types;
+
enum Kind {
BUILTIN,
NATIVE,
@@ -152,24 +150,39 @@ public:
_FORCE_INLINE_ String to_string_strict() const { return is_hard_type() ? to_string() : "Variant"; }
PropertyInfo to_property_info(const String &p_name) const;
- _FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
- container_element_type = memnew(DataType(p_type));
+ _FORCE_INLINE_ static DataType get_variant_type() { // Default DataType for container elements.
+ DataType datatype;
+ datatype.kind = VARIANT;
+ datatype.type_source = INFERRED;
+ return datatype;
}
- _FORCE_INLINE_ DataType get_container_element_type() const {
- ERR_FAIL_NULL_V(container_element_type, DataType());
- return *container_element_type;
+ _FORCE_INLINE_ void set_container_element_type(int p_index, const DataType &p_type) {
+ ERR_FAIL_COND(p_index < 0);
+ while (p_index >= container_element_types.size()) {
+ container_element_types.push_back(get_variant_type());
+ }
+ container_element_types.write[p_index] = DataType(p_type);
}
- _FORCE_INLINE_ bool has_container_element_type() const {
- return container_element_type != nullptr;
+ _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];
}
- _FORCE_INLINE_ void unset_container_element_type() {
- if (container_element_type) {
- memdelete(container_element_type);
- };
- container_element_type = nullptr;
+ _FORCE_INLINE_ DataType get_container_element_type_or_variant(int p_index) const {
+ if (p_index < 0 || p_index >= container_element_types.size()) {
+ return get_variant_type();
+ }
+ return container_element_types[p_index];
+ }
+
+ _FORCE_INLINE_ bool has_container_element_type(int p_index) const {
+ return p_index >= 0 && p_index < container_element_types.size();
+ }
+
+ _FORCE_INLINE_ bool has_container_element_types() const {
+ return !container_element_types.is_empty();
}
bool is_typed_container_type() const;
@@ -229,10 +242,7 @@ public:
class_type = p_other.class_type;
method_info = p_other.method_info;
enum_values = p_other.enum_values;
- unset_container_element_type();
- if (p_other.has_container_element_type()) {
- set_container_element_type(p_other.get_container_element_type());
- }
+ container_element_types = p_other.container_element_types;
}
DataType() = default;
@@ -241,9 +251,7 @@ public:
*this = p_other;
}
- ~DataType() {
- unset_container_element_type();
- }
+ ~DataType() {}
};
struct ParserError {
@@ -1183,7 +1191,11 @@ public:
struct TypeNode : public Node {
Vector<IdentifierNode *> type_chain;
- TypeNode *container_type = nullptr;
+ Vector<TypeNode *> container_types;
+
+ TypeNode *get_container_type_or_null(int p_index) const {
+ return p_index >= 0 && p_index < container_types.size() ? container_types[p_index] : nullptr;
+ }
TypeNode() {
type = TYPE;
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index d31411b26b..2c0b8df9ac 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -91,8 +91,8 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT
if (p_data_type.builtin_type == Variant::ARRAY) {
Array array;
// Typed array.
- if (p_data_type.has_container_element_type()) {
- const GDScriptDataType &element_type = p_data_type.get_container_element_type();
+ if (p_data_type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = p_data_type.get_container_element_type(0);
array.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type);
}
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index aee79d93ed..10d54e8d97 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -2782,6 +2782,12 @@ Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset>
Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+ const String base_dir = p_path.get_base_dir();
+ if (!DirAccess::exists(base_dir)) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir));
+ return ERR_FILE_BAD_PATH;
+ }
+
String src_apk;
Error err;
@@ -2856,7 +2862,10 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
}
const String assets_directory = get_assets_directory(p_preset, export_format);
String sdk_path = EDITOR_GET("export/android/android_sdk_path");
- ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.");
+ if (sdk_path.is_empty()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."));
+ return ERR_UNCONFIGURED;
+ }
print_verbose("Android sdk path: " + sdk_path);
// TODO: should we use "package/name" or "application/config/name"?
@@ -3106,10 +3115,6 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
}
}
- if (!DirAccess::exists(p_path.get_base_dir())) {
- return ERR_FILE_BAD_PATH;
- }
-
Ref<FileAccess> io_fa;
zlib_filefunc_def io = zipio_create_io(&io_fa);
@@ -3302,10 +3307,6 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
zipClose(unaligned_apk, nullptr);
unzClose(pkg);
- if (err != OK) {
- CLEANUP_AND_RETURN(err);
- }
-
// Let's zip-align (must be done before signing)
static const int ZIP_ALIGNMENT = 4;
@@ -3392,6 +3393,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
// file will invalidate the signature.
err = sign_apk(p_preset, p_debug, p_path, ep);
if (err != OK) {
+ // Message is supplied by the subroutine method.
CLEANUP_AND_RETURN(err);
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
index 2278b46f6c..217e7a2b60 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -184,7 +184,7 @@ class Godot(private val context: Context) : SensorEventListener {
return
}
- beginBenchmarkMeasure("Godot::onCreate")
+ beginBenchmarkMeasure("Startup", "Godot::onCreate")
try {
this.primaryHost = primaryHost
val activity = requireActivity()
@@ -286,7 +286,7 @@ class Godot(private val context: Context) : SensorEventListener {
initializationStarted = false
throw e
} finally {
- endBenchmarkMeasure("Godot::onCreate");
+ endBenchmarkMeasure("Startup", "Godot::onCreate");
}
}
@@ -1016,13 +1016,13 @@ class Godot(private val context: Context) : SensorEventListener {
}
@Keep
- private fun nativeBeginBenchmarkMeasure(label: String) {
- beginBenchmarkMeasure(label)
+ private fun nativeBeginBenchmarkMeasure(scope: String, label: String) {
+ beginBenchmarkMeasure(scope, label)
}
@Keep
- private fun nativeEndBenchmarkMeasure(label: String) {
- endBenchmarkMeasure(label)
+ private fun nativeEndBenchmarkMeasure(scope: String, label: String) {
+ endBenchmarkMeasure(scope, label)
}
@Keep
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
index 120e1722e5..f1c029e7a1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
@@ -172,7 +172,7 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
@Override
public void onCreate(Bundle icicle) {
- BenchmarkUtils.beginBenchmarkMeasure("GodotFragment::onCreate");
+ BenchmarkUtils.beginBenchmarkMeasure("Startup", "GodotFragment::onCreate");
super.onCreate(icicle);
final Activity activity = getActivity();
@@ -180,7 +180,7 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
godot = new Godot(requireContext());
performEngineInitialization();
- BenchmarkUtils.endBenchmarkMeasure("GodotFragment::onCreate");
+ BenchmarkUtils.endBenchmarkMeasure("Startup", "GodotFragment::onCreate");
}
private void performEngineInitialization() {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt
index 1552c8f082..69748c0a8d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt
@@ -41,7 +41,7 @@ import org.godotengine.godot.io.file.FileAccessFlags
import org.godotengine.godot.io.file.FileAccessHandler
import org.json.JSONObject
import java.nio.ByteBuffer
-import java.util.concurrent.ConcurrentSkipListMap
+import java.util.Collections
/**
* Contains benchmark related utilities methods
@@ -51,44 +51,47 @@ private const val TAG = "GodotBenchmark"
var useBenchmark = false
var benchmarkFile = ""
-private val startBenchmarkFrom = ConcurrentSkipListMap<String, Long>()
-private val benchmarkTracker = ConcurrentSkipListMap<String, Double>()
+private val startBenchmarkFrom = Collections.synchronizedMap(LinkedHashMap<Pair<String, String>, Long>())
+private val benchmarkTracker = Collections.synchronizedMap(LinkedHashMap<Pair<String, String>, Double>())
/**
- * Start measuring and tracing the execution of a given section of code using the given label.
+ * Start measuring and tracing the execution of a given section of code using the given label
+ * within the given scope.
*
* Must be followed by a call to [endBenchmarkMeasure].
*
* Note: Only enabled on 'editorDev' build variant.
*/
-fun beginBenchmarkMeasure(label: String) {
+fun beginBenchmarkMeasure(scope: String, label: String) {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
return
}
- startBenchmarkFrom[label] = SystemClock.elapsedRealtime()
+ val key = Pair(scope, label)
+ startBenchmarkFrom[key] = SystemClock.elapsedRealtime()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- Trace.beginAsyncSection(label, 0)
+ Trace.beginAsyncSection("[$scope] $label", 0)
}
}
/**
- * End measuring and tracing of the section of code with the given label.
+ * End measuring and tracing of the section of code with the given label within the given scope.
*
* Must be preceded by a call [beginBenchmarkMeasure]
*
* * Note: Only enabled on 'editorDev' build variant.
*/
-fun endBenchmarkMeasure(label: String) {
+fun endBenchmarkMeasure(scope: String, label: String) {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
return
}
- val startTime = startBenchmarkFrom[label] ?: return
+ val key = Pair(scope, label)
+ val startTime = startBenchmarkFrom[key] ?: return
val total = SystemClock.elapsedRealtime() - startTime
- benchmarkTracker[label] = total / 1000.0
+ benchmarkTracker[key] = total / 1_000.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- Trace.endAsyncSection(label, 0)
+ Trace.endAsyncSection("[$scope] $label", 0)
}
}
@@ -107,8 +110,16 @@ fun dumpBenchmark(fileAccessHandler: FileAccessHandler?, filepath: String? = ben
return
}
+ val results = LinkedHashMap<String, String>()
+ for (entry in benchmarkTracker) {
+ if (!results.containsKey(entry.key.first)) {
+ results[entry.key.first] = ""
+ }
+ results[entry.key.first] += "\t\t- ${entry.key.second}: ${entry.value * 1_000.0} msec.\n"
+ }
+
val printOut =
- benchmarkTracker.map { "\t- ${it.key} : ${it.value} sec." }.joinToString("\n")
+ results.map { "\t- [${it.key}]\n ${it.value}" }.joinToString("\n")
Log.i(TAG, "BENCHMARK:\n$printOut")
if (fileAccessHandler != null && !filepath.isNullOrBlank()) {
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index cb6ebf14a8..4e401e633e 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -78,8 +78,8 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
_create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I");
_get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
- _begin_benchmark_measure = p_env->GetMethodID(godot_class, "nativeBeginBenchmarkMeasure", "(Ljava/lang/String;)V");
- _end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;)V");
+ _begin_benchmark_measure = p_env->GetMethodID(godot_class, "nativeBeginBenchmarkMeasure", "(Ljava/lang/String;Ljava/lang/String;)V");
+ _end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;Ljava/lang/String;)V");
_dump_benchmark = p_env->GetMethodID(godot_class, "nativeDumpBenchmark", "(Ljava/lang/String;)V");
_get_gdextension_list_config_file = p_env->GetMethodID(godot_class, "getGDExtensionConfigFiles", "()[Ljava/lang/String;");
_has_feature = p_env->GetMethodID(godot_class, "hasFeature", "(Ljava/lang/String;)Z");
@@ -348,21 +348,23 @@ int GodotJavaWrapper::create_new_godot_instance(List<String> args) {
}
}
-void GodotJavaWrapper::begin_benchmark_measure(const String &p_label) {
+void GodotJavaWrapper::begin_benchmark_measure(const String &p_context, const String &p_label) {
if (_begin_benchmark_measure) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL(env);
+ jstring j_context = env->NewStringUTF(p_context.utf8().get_data());
jstring j_label = env->NewStringUTF(p_label.utf8().get_data());
- env->CallVoidMethod(godot_instance, _begin_benchmark_measure, j_label);
+ env->CallVoidMethod(godot_instance, _begin_benchmark_measure, j_context, j_label);
}
}
-void GodotJavaWrapper::end_benchmark_measure(const String &p_label) {
+void GodotJavaWrapper::end_benchmark_measure(const String &p_context, const String &p_label) {
if (_end_benchmark_measure) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL(env);
+ jstring j_context = env->NewStringUTF(p_context.utf8().get_data());
jstring j_label = env->NewStringUTF(p_label.utf8().get_data());
- env->CallVoidMethod(godot_instance, _end_benchmark_measure, j_label);
+ env->CallVoidMethod(godot_instance, _end_benchmark_measure, j_context, j_label);
}
}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 920df958c2..52043c6027 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -105,8 +105,8 @@ public:
void vibrate(int p_duration_ms);
String get_input_fallback_mapping();
int create_new_godot_instance(List<String> args);
- void begin_benchmark_measure(const String &p_label);
- void end_benchmark_measure(const String &p_label);
+ void begin_benchmark_measure(const String &p_context, const String &p_label);
+ void end_benchmark_measure(const String &p_context, const String &p_label);
void dump_benchmark(const String &benchmark_file);
// Return the list of gdextensions config file.
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 92dc5f909f..0d82bec75d 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -708,15 +708,15 @@ String OS_Android::get_config_path() const {
return get_user_data_dir().path_join("config");
}
-void OS_Android::benchmark_begin_measure(const String &p_what) {
+void OS_Android::benchmark_begin_measure(const String &p_context, const String &p_what) {
#ifdef TOOLS_ENABLED
- godot_java->begin_benchmark_measure(p_what);
+ godot_java->begin_benchmark_measure(p_context, p_what);
#endif
}
-void OS_Android::benchmark_end_measure(const String &p_what) {
+void OS_Android::benchmark_end_measure(const String &p_context, const String &p_what) {
#ifdef TOOLS_ENABLED
- godot_java->end_benchmark_measure(p_what);
+ godot_java->end_benchmark_measure(p_context, p_what);
#endif
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index f88f3e0518..837ef8bad5 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -165,8 +165,8 @@ public:
virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override;
- virtual void benchmark_begin_measure(const String &p_what) override;
- virtual void benchmark_end_measure(const String &p_what) override;
+ virtual void benchmark_begin_measure(const String &p_context, const String &p_what) override;
+ virtual void benchmark_end_measure(const String &p_context, const String &p_what) override;
virtual void benchmark_dump() override;
virtual void load_platform_gdextensions() const override;
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index 2561c1c095..60da16ae8c 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -443,7 +443,11 @@ int DisplayServerIOS::screen_get_dpi(int p_screen) const {
}
float DisplayServerIOS::screen_get_refresh_rate(int p_screen) const {
- return [UIScreen mainScreen].maximumFramesPerSecond;
+ float fps = [UIScreen mainScreen].maximumFramesPerSecond;
+ if ([NSProcessInfo processInfo].lowPowerModeEnabled) {
+ fps = 60;
+ }
+ return fps;
}
float DisplayServerIOS::screen_get_scale(int p_screen) const {
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 6bc3241425..e479e258fe 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -620,7 +620,10 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
String sizes;
Ref<DirAccess> da = DirAccess::open(p_iconset_dir);
- ERR_FAIL_COND_V_MSG(da.is_null(), ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'.");
+ if (da.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not open a directory at path \"%s\"."), p_iconset_dir));
+ return ERR_CANT_OPEN;
+ }
Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color");
@@ -692,12 +695,20 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
json_description += "]}";
Ref<FileAccess> json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE);
- ERR_FAIL_COND_V(json_file.is_null(), ERR_CANT_CREATE);
+ if (json_file.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not write to a file at path \"%s\"."), p_iconset_dir + "Contents.json"));
+ return ERR_CANT_CREATE;
+ }
+
CharString json_utf8 = json_description.utf8();
json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length());
Ref<FileAccess> sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE);
- ERR_FAIL_COND_V(sizes_file.is_null(), ERR_CANT_CREATE);
+ if (sizes_file.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not write to a file at path \"%s\"."), p_iconset_dir + "sizes"));
+ return ERR_CANT_CREATE;
+ }
+
CharString sizes_utf8 = sizes.utf8();
sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length());
@@ -1219,10 +1230,10 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
String asset = p_assets[f_idx];
if (asset.begins_with("res://")) {
Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
- ERR_FAIL_COND_V(err, err);
+ ERR_FAIL_COND_V(err != OK, err);
} else if (ProjectSettings::get_singleton()->localize_path(asset).begins_with("res://")) {
Error err = _copy_asset(p_out_dir, ProjectSettings::get_singleton()->localize_path(asset), nullptr, p_is_framework, p_should_embed, r_exported_assets);
- ERR_FAIL_COND_V(err, err);
+ ERR_FAIL_COND_V(err != OK, err);
} else {
// either SDK-builtin or already a part of the export template
IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed };
@@ -1306,8 +1317,7 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
// We shouldn't embed .xcframework that contains static libraries.
// Static libraries are not embedded anyway.
err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
-
- ERR_FAIL_COND_V(err, err);
+ ERR_FAIL_COND_V(err != OK, err);
// Adding dependencies.
// Use separate container for names to check for duplicates.
@@ -1432,15 +1442,15 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
{
// Export linked plugin dependency
err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
- ERR_FAIL_COND_V(err, err);
+ ERR_FAIL_COND_V(err != OK, err);
// Export embedded plugin dependency
err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
- ERR_FAIL_COND_V(err, err);
+ ERR_FAIL_COND_V(err != OK, err);
// Export plugin files
err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets);
- ERR_FAIL_COND_V(err, err);
+ ERR_FAIL_COND_V(err != OK, err);
}
// Update CPP
@@ -1496,9 +1506,14 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_skip_ipa) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
- String src_pkg_name;
- String dest_dir = p_path.get_base_dir() + "/";
- String binary_name = p_path.get_file().get_basename();
+ const String dest_dir = p_path.get_base_dir() + "/";
+ const String binary_name = p_path.get_file().get_basename();
+ const String binary_dir = dest_dir + binary_name;
+
+ if (!DirAccess::exists(dest_dir)) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), dest_dir));
+ return ERR_FILE_BAD_PATH;
+ }
bool export_project_only = p_preset->get("application/export_project_only");
@@ -1507,6 +1522,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
String team_id = p_preset->get("application/app_store_team_id");
ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project.");
+ String src_pkg_name;
if (p_debug) {
src_pkg_name = p_preset->get("custom_template/debug");
} else {
@@ -1522,10 +1538,6 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
}
- if (!DirAccess::exists(dest_dir)) {
- return ERR_FILE_BAD_PATH;
- }
-
{
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (da.is_valid()) {
@@ -1533,18 +1545,19 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
// remove leftovers from last export so they don't interfere
// in case some files are no longer needed
- if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) {
+ if (da->change_dir(binary_dir + ".xcodeproj") == OK) {
da->erase_contents_recursive();
}
- if (da->change_dir(dest_dir + binary_name) == OK) {
+ if (da->change_dir(binary_dir) == OK) {
da->erase_contents_recursive();
}
da->change_dir(current_dir);
- if (!da->dir_exists(dest_dir + binary_name)) {
- Error err = da->make_dir(dest_dir + binary_name);
- if (err) {
+ if (!da->dir_exists(binary_dir)) {
+ Error err = da->make_dir(binary_dir);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create the directory: \"%s\""), binary_dir));
return err;
}
}
@@ -1554,10 +1567,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
if (ep.step("Making .pck", 0)) {
return ERR_SKIP;
}
- String pack_path = dest_dir + binary_name + ".pck";
+ String pack_path = binary_dir + ".pck";
Vector<SharedObject> libraries;
Error err = save_pack(p_preset, p_debug, pack_path, &libraries);
if (err) {
+ // Message is supplied by the subroutine method.
return err;
}
@@ -1606,7 +1620,10 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
Vector<IOSExportAsset> assets;
Ref<DirAccess> tmp_app_path = DirAccess::create_for_path(dest_dir);
- ERR_FAIL_COND_V(tmp_app_path.is_null(), ERR_CANT_CREATE);
+ if (tmp_app_path.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), dest_dir));
+ return ERR_CANT_CREATE;
+ }
print_line("Unzipping...");
Ref<FileAccess> io_fa;
@@ -1617,8 +1634,14 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
return ERR_CANT_OPEN;
}
- err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug);
- ERR_FAIL_COND_V(err, err);
+ err = _export_ios_plugins(p_preset, config_data, binary_dir, assets, p_debug);
+ if (err != OK) {
+ // TODO: Improve error reporting by using `add_message` throughout all methods called via `_export_ios_plugins`.
+ // For now a generic top level message would be fine, but we're ought to use proper reporting here instead of
+ // just fail macros and non-descriptive error return values.
+ add_message(EXPORT_MESSAGE_ERROR, TTR("iOS Plugins"), vformat(TTR("Failed to export iOS plugins with code %d. Please check the output log."), err));
+ return err;
+ }
//export rest of the files
int ret = unzGoToFirstFile(src_pkg_zip);
@@ -1683,8 +1706,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
print_line("Creating " + dir_name);
Error dir_err = tmp_app_path->make_dir_recursive(dir_name);
if (dir_err) {
- ERR_PRINT("Can't create '" + dir_name + "'.");
unzClose(src_pkg_zip);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create a directory at path \"%s\"."), dir_name));
return ERR_CANT_CREATE;
}
}
@@ -1693,8 +1716,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
{
Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE);
if (f.is_null()) {
- ERR_PRINT("Can't write '" + file + "'.");
unzClose(src_pkg_zip);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not write to a file at path \"%s\"."), file));
return ERR_CANT_CREATE;
};
f->store_buffer(data.ptr(), data.size());
@@ -1715,7 +1738,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
unzClose(src_pkg_zip);
if (!found_library) {
- ERR_PRINT("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive.");
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Requested template library '%s' not found. It might be missing from your template archive."), library_to_use));
return ERR_FILE_NOT_FOUND;
}
@@ -1727,7 +1750,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
Vector<String> translations = GLOBAL_GET("internationalization/locale/translations");
if (translations.size() > 0) {
{
- String fname = dest_dir + binary_name + "/en.lproj";
+ String fname = binary_dir + "/en.lproj";
tmp_app_path->make_dir_recursive(fname);
Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
f->store_line("/* Localized versions of Info.plist keys */");
@@ -1747,7 +1770,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
for (const String &lang : languages) {
- String fname = dest_dir + binary_name + "/" + lang + ".lproj";
+ String fname = binary_dir + "/" + lang + ".lproj";
tmp_app_path->make_dir_recursive(fname);
Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
f->store_line("/* Localized versions of Info.plist keys */");
@@ -1776,34 +1799,37 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
String dest_lib_file_path = dest_dir + static_lib_path.get_file();
Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path);
if (lib_copy_err != OK) {
- ERR_PRINT("Can't copy '" + static_lib_path + "'.");
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not copy a file at path \"%s\" to \"%s\"."), static_lib_path, dest_lib_file_path));
return lib_copy_err;
}
}
}
- String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/";
+ String iconset_dir = binary_dir + "/Images.xcassets/AppIcon.appiconset/";
err = OK;
if (!tmp_app_path->dir_exists(iconset_dir)) {
err = tmp_app_path->make_dir_recursive(iconset_dir);
}
- if (err) {
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create a directory at path \"%s\"."), iconset_dir));
return err;
}
err = _export_icons(p_preset, iconset_dir);
- if (err) {
+ if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
{
bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard");
- String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/";
- String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/";
+ String launch_image_path = binary_dir + "/Images.xcassets/LaunchImage.launchimage/";
+ String splash_image_path = binary_dir + "/Images.xcassets/SplashImage.imageset/";
Ref<DirAccess> launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (launch_screen_da.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not access the filesystem."));
return ERR_CANT_CREATE;
}
@@ -1816,10 +1842,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
err = _export_loading_screen_file(p_preset, splash_image_path);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create a file at path \"%s\" with code %d."), splash_image_path, err));
} else {
print_line("Using Launch Images");
- const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard";
+ const String launch_screen_path = binary_dir + "/Launch Screen.storyboard";
launch_screen_da->remove(launch_screen_path);
@@ -1829,21 +1856,22 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
err = _export_loading_screen_images(p_preset, launch_image_path);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create a file at path \"%s\" with code %d."), launch_image_path, err));
}
}
- if (err) {
+ if (err != OK) {
return err;
}
print_line("Exporting additional assets");
- _export_additional_assets(dest_dir + binary_name, libraries, assets);
+ _export_additional_assets(binary_dir, libraries, assets);
_add_assets_to_project(p_preset, project_file_data, assets);
- String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj";
+ String project_file_name = binary_dir + ".xcodeproj/project.pbxproj";
{
Ref<FileAccess> f = FileAccess::open(project_file_name, FileAccess::WRITE);
if (f.is_null()) {
- ERR_PRINT("Can't write '" + project_file_name + "'.");
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not write to a file at path \"%s\"."), project_file_name));
return ERR_CANT_CREATE;
};
f->store_buffer(project_file_data.ptr(), project_file_data.size());
@@ -1854,7 +1882,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
if (ep.step("Code-signing dylibs", 2)) {
return ERR_SKIP;
}
- Ref<DirAccess> dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs");
+ Ref<DirAccess> dylibs_dir = DirAccess::open(binary_dir + "/dylibs");
ERR_FAIL_COND_V(dylibs_dir.is_null(), ERR_CANT_OPEN);
CodesignData codesign_data(p_preset, p_debug);
err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
@@ -1871,10 +1899,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
if (ep.step("Making .xcarchive", 3)) {
return ERR_SKIP;
}
+
String archive_path = p_path.get_basename() + ".xcarchive";
List<String> archive_args;
archive_args.push_back("-project");
- archive_args.push_back(dest_dir + binary_name + ".xcodeproj");
+ archive_args.push_back(binary_dir + ".xcodeproj");
archive_args.push_back("-scheme");
archive_args.push_back(binary_name);
archive_args.push_back("-sdk");
@@ -1895,9 +1924,14 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
archive_args.push_back("-allowProvisioningUpdates");
archive_args.push_back("-archivePath");
archive_args.push_back(archive_path);
+
String archive_str;
err = OS::get_singleton()->execute("xcodebuild", archive_args, &archive_str, nullptr, true);
- ERR_FAIL_COND_V(err, err);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), vformat(TTR("Failed to run xcodebuild with code %d"), err));
+ return err;
+ }
+
print_line("xcodebuild (.xcarchive):\n" + archive_str);
if (!archive_str.contains("** ARCHIVE SUCCEEDED **")) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR("Xcode project build failed, see editor log for details."));
@@ -1908,18 +1942,24 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
if (ep.step("Making .ipa", 4)) {
return ERR_SKIP;
}
+
List<String> export_args;
export_args.push_back("-exportArchive");
export_args.push_back("-archivePath");
export_args.push_back(archive_path);
export_args.push_back("-exportOptionsPlist");
- export_args.push_back(dest_dir + binary_name + "/export_options.plist");
+ export_args.push_back(binary_dir + "/export_options.plist");
export_args.push_back("-allowProvisioningUpdates");
export_args.push_back("-exportPath");
export_args.push_back(dest_dir);
+
String export_str;
err = OS::get_singleton()->execute("xcodebuild", export_args, &export_str, nullptr, true);
- ERR_FAIL_COND_V(err, err);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), vformat(TTR("Failed to run xcodebuild with code %d"), err));
+ return err;
+ }
+
print_line("xcodebuild (.ipa):\n" + export_str);
if (!export_str.contains("** EXPORT SUCCEEDED **")) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR(".ipa export failed, see editor log for details."));
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 9d1e058b76..64efcffae3 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -79,6 +79,7 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset>
Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
if (export_as_zip) {
if (tmp_app_dir.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), tmp_dir_path));
return ERR_CANT_CREATE;
}
if (DirAccess::exists(tmp_dir_path)) {
@@ -93,19 +94,18 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset>
// Export project.
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, path, p_flags);
if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
// Save console wrapper.
- if (err == OK) {
- int con_scr = p_preset->get("debug/export_console_wrapper");
- if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
- String scr_path = path.get_basename() + ".sh";
- err = _export_debug_script(p_preset, pkg_name, path.get_file(), scr_path);
- FileAccess::set_unix_permissions(scr_path, 0755);
- if (err != OK) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Console Export"), TTR("Could not create console wrapper."));
- }
+ int con_scr = p_preset->get("debug/export_console_wrapper");
+ if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
+ String scr_path = path.get_basename() + ".sh";
+ err = _export_debug_script(p_preset, pkg_name, path.get_file(), scr_path);
+ FileAccess::set_unix_permissions(scr_path, 0755);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Console Export"), TTR("Could not create console wrapper."));
}
}
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 8761fe22e3..6f9db1427b 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -1266,10 +1266,16 @@ Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPres
Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
- String src_pkg_name;
+ const String base_dir = p_path.get_base_dir();
+
+ if (!DirAccess::exists(base_dir)) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir));
+ return ERR_FILE_BAD_PATH;
+ }
EditorProgress ep("export", TTR("Exporting for macOS"), 3, true);
+ String src_pkg_name;
if (p_debug) {
src_pkg_name = p_preset->get("custom_template/debug");
} else {
@@ -1280,16 +1286,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
String err;
src_pkg_name = find_export_template("macos.zip", &err);
if (src_pkg_name.is_empty()) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Export template not found."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Export template not found.") + "\n" + err);
return ERR_FILE_NOT_FOUND;
}
}
- if (!DirAccess::exists(p_path.get_base_dir())) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("The given export path doesn't exist."));
- return ERR_FILE_BAD_PATH;
- }
-
Ref<FileAccess> io_fa;
zlib_filefunc_def io = zipio_create_io(&io_fa);
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index a62ccdc2aa..a70812cf5b 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -259,6 +259,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
_replace_strings(replaces, sw);
Error err = _write_or_error(sw.ptr(), sw.size(), dir.path_join(name + ".service.worker.js"));
if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
@@ -291,16 +292,19 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
const String icon144_path = p_preset->get("progressive_web_app/icon_144x144");
err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr);
if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
const String icon180_path = p_preset->get("progressive_web_app/icon_180x180");
err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr);
if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
const String icon512_path = p_preset->get("progressive_web_app/icon_512x512");
err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr);
if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
manifest["icons"] = icons_arr;
@@ -308,6 +312,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
CharString cs = Variant(manifest).to_json_string().utf8();
err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.path_join(name + ".manifest.json"));
if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
@@ -439,6 +444,11 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
const String base_path = p_path.get_basename();
const String base_name = p_path.get_file().get_basename();
+ if (!DirAccess::exists(base_dir)) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir));
+ return ERR_FILE_BAD_PATH;
+ }
+
// Find the correct template
String template_path = p_debug ? custom_debug : custom_release;
template_path = template_path.strip_edges();
@@ -447,10 +457,6 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
template_path = find_export_template(_get_template_name(extensions, p_debug));
}
- if (!DirAccess::exists(base_dir)) {
- return ERR_FILE_BAD_PATH;
- }
-
if (!template_path.is_empty() && !FileAccess::exists(template_path)) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Template file not found: \"%s\"."), template_path));
return ERR_FILE_NOT_FOUND;
@@ -480,6 +486,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
// Extract templates.
error = _extract_template(template_path, base_dir, base_name, pwa);
if (error) {
+ // Message is supplied by the subroutine method.
return error;
}
@@ -510,6 +517,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
_fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes);
Error err = _write_or_error(html.ptr(), html.size(), p_path);
if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
html.resize(0);
@@ -543,6 +551,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p
if (pwa) {
err = _build_pwa(p_preset, p_path, shared_objects);
if (err != OK) {
+ // Message is supplied by the subroutine method.
return err;
}
}
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 4185c36d77..418f38c127 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -226,6 +226,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
if (export_as_zip) {
if (tmp_app_dir.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), tmp_dir_path));
return ERR_CANT_CREATE;
}
if (DirAccess::exists(tmp_dir_path)) {
@@ -242,8 +243,14 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
if (embedded) {
pck_path = pck_path.get_basename() + ".tmp";
}
+
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags);
- if (p_preset->get("codesign/enable") && err == OK) {
+ if (err != OK) {
+ // Message is supplied by the subroutine method.
+ return err;
+ }
+
+ if (p_preset->get("codesign/enable")) {
_code_sign(p_preset, pck_path);
String wrapper_path = p_path.get_basename() + ".console.exe";
if (FileAccess::exists(wrapper_path)) {
@@ -251,7 +258,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
}
}
- if (embedded && err == OK) {
+ if (embedded) {
Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
err = tmp_dir->rename(pck_path, p_path);
if (err != OK) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 3727c788fd..2e6759063b 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -293,6 +293,8 @@ static Ref<ResourceFormatSaverShaderInclude> resource_saver_shader_include;
static Ref<ResourceFormatLoaderShaderInclude> resource_loader_shader_include;
void register_scene_types() {
+ OS::get_singleton()->benchmark_begin_measure("Scene", "Register Types");
+
SceneStringNames::create();
OS::get_singleton()->yield(); // may take time to init
@@ -1182,9 +1184,13 @@ void register_scene_types() {
}
SceneDebugger::initialize();
+
+ OS::get_singleton()->benchmark_end_measure("Scene", "Register Types");
}
void unregister_scene_types() {
+ OS::get_singleton()->benchmark_begin_measure("Scene", "Unregister Types");
+
SceneDebugger::deinitialize();
ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered);
@@ -1227,10 +1233,16 @@ void unregister_scene_types() {
CanvasItemMaterial::finish_shaders();
ColorPicker::finish_shaders();
SceneStringNames::free();
+
+ OS::get_singleton()->benchmark_end_measure("Scene", "Unregister Types");
}
void register_scene_singletons() {
+ OS::get_singleton()->benchmark_begin_measure("Scene", "Register Singletons");
+
GDREGISTER_CLASS(ThemeDB);
Engine::get_singleton()->add_singleton(Engine::Singleton("ThemeDB", ThemeDB::get_singleton()));
+
+ OS::get_singleton()->benchmark_end_measure("Scene", "Register Singletons");
}
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 13791d8c2b..8ad9eec25f 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -552,7 +552,7 @@ void CapsuleMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings");
ADD_LINKED_PROPERTY("radius", "height");
ADD_LINKED_PROPERTY("height", "radius");
@@ -594,7 +594,8 @@ int CapsuleMesh::get_radial_segments() const {
}
void CapsuleMesh::set_rings(const int p_rings) {
- rings = p_rings > 1 ? p_rings : 1;
+ ERR_FAIL_COND(p_rings < 0);
+ rings = p_rings;
_request_update();
}
@@ -1161,7 +1162,7 @@ void CylinderMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_bottom_radius", "get_bottom_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom");
}
@@ -1206,7 +1207,8 @@ int CylinderMesh::get_radial_segments() const {
}
void CylinderMesh::set_rings(const int p_rings) {
- rings = p_rings > 0 ? p_rings : 0;
+ ERR_FAIL_COND(p_rings < 0);
+ rings = p_rings;
_request_update();
}
@@ -1919,7 +1921,8 @@ int SphereMesh::get_radial_segments() const {
}
void SphereMesh::set_rings(const int p_rings) {
- rings = p_rings > 1 ? p_rings : 1;
+ ERR_FAIL_COND(p_rings < 1);
+ rings = p_rings;
_request_update();
}
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 50e14a1f37..2beab44f91 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -117,6 +117,8 @@ static MovieWriterMJPEG *writer_mjpeg = nullptr;
static MovieWriterPNGWAV *writer_pngwav = nullptr;
void register_server_types() {
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Register Extensions");
+
shader_types = memnew(ShaderTypes);
GDREGISTER_CLASS(TextServerManager);
@@ -293,16 +295,24 @@ void register_server_types() {
writer_pngwav = memnew(MovieWriterPNGWAV);
MovieWriter::add_writer(writer_pngwav);
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Register Extensions");
}
void unregister_server_types() {
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Unregister Extensions");
+
ServersDebugger::deinitialize();
memdelete(shader_types);
memdelete(writer_mjpeg);
memdelete(writer_pngwav);
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Unregister Extensions");
}
void register_server_singletons() {
+ OS::get_singleton()->benchmark_begin_measure("Servers", "Register Singletons");
+
Engine::get_singleton()->add_singleton(Engine::Singleton("DisplayServer", DisplayServer::get_singleton(), "DisplayServer"));
Engine::get_singleton()->add_singleton(Engine::Singleton("RenderingServer", RenderingServer::get_singleton(), "RenderingServer"));
Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton(), "AudioServer"));
@@ -312,4 +322,6 @@ void register_server_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton(), "NavigationServer3D"));
Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton(), "XRServer"));
Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton(), "CameraServer"));
+
+ OS::get_singleton()->benchmark_end_measure("Servers", "Register Singletons");
}
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 6ec9f0768a..6cb8a71019 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1981,8 +1981,7 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve
if (oc->vertex_array.is_null()) {
//create from scratch
//vertices
- // TODO: geometry is always of length lc * 6 * sizeof(float), so in doubles builds this will receive half the data it needs
- oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(real_t), geometry);
+ oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(float), geometry);
Vector<RID> buffer;
buffer.push_back(oc->vertex_buffer);
@@ -2043,7 +2042,18 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve
if (oc->sdf_vertex_array.is_null()) {
//create from scratch
//vertices
- oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(real_t), p_points.to_byte_array());
+#ifdef REAL_T_IS_DOUBLE
+ PackedFloat32Array float_points;
+ float_points.resize(p_points.size() * 2);
+ float *float_points_ptr = (float *)float_points.ptrw();
+ for (int i = 0; i < p_points.size(); i++) {
+ float_points_ptr[i * 2] = p_points[i].x;
+ float_points_ptr[i * 2 + 1] = p_points[i].y;
+ }
+ oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), float_points.to_byte_array());
+#else
+ oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), p_points.to_byte_array());
+#endif
oc->sdf_index_buffer = RD::get_singleton()->index_buffer_create(sdf_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, sdf_indices.to_byte_array());
oc->sdf_index_array = RD::get_singleton()->index_array_create(oc->sdf_index_buffer, 0, sdf_indices.size());
@@ -2054,7 +2064,18 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve
} else {
//update existing
- RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(real_t) * 2 * p_points.size(), p_points.ptr());
+#ifdef REAL_T_IS_DOUBLE
+ PackedFloat32Array float_points;
+ float_points.resize(p_points.size() * 2);
+ float *float_points_ptr = (float *)float_points.ptrw();
+ for (int i = 0; i < p_points.size(); i++) {
+ float_points_ptr[i * 2] = p_points[i].x;
+ float_points_ptr[i * 2 + 1] = p_points[i].y;
+ }
+ RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), float_points.ptr());
+#else
+ RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), p_points.ptr());
+#endif
RD::get_singleton()->buffer_update(oc->sdf_index_buffer, 0, sdf_indices.size() * sizeof(int32_t), sdf_indices.ptr());
}
}
@@ -2578,15 +2599,15 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
//pipelines
Vector<RD::VertexAttribute> vf;
RD::VertexAttribute vd;
- vd.format = sizeof(real_t) == sizeof(float) ? RD::DATA_FORMAT_R32G32B32_SFLOAT : RD::DATA_FORMAT_R64G64B64_SFLOAT;
+ vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
+ vd.stride = sizeof(float) * 3;
vd.location = 0;
vd.offset = 0;
- vd.stride = sizeof(real_t) * 3;
vf.push_back(vd);
shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf);
- vd.format = sizeof(real_t) == sizeof(float) ? RD::DATA_FORMAT_R32G32_SFLOAT : RD::DATA_FORMAT_R64G64_SFLOAT;
- vd.stride = sizeof(real_t) * 2;
+ vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
+ vd.stride = sizeof(float) * 2;
vf.write[0] = vd;
shadow_render.sdf_vertex_format = RD::get_singleton()->vertex_format_create(vf);
diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h
index fcac9a6231..a9bc2e9b99 100644
--- a/tests/core/math/test_basis.h
+++ b/tests/core/math/test_basis.h
@@ -324,6 +324,100 @@ TEST_CASE("[Basis] Is conformal checks") {
CHECK_FALSE_MESSAGE(
Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_conformal(),
"Basis with the X axis skewed 45 degrees should not be conformal.");
+
+ CHECK_MESSAGE(
+ Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_conformal(),
+ "Edge case: Basis with all zeroes should return true for is_conformal (because a 0 scale is uniform).");
+}
+
+TEST_CASE("[Basis] Is orthogonal checks") {
+ CHECK_MESSAGE(
+ Basis().is_orthogonal(),
+ "Identity Basis should be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_orthogonal(),
+ "Basis with only rotation should be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis::from_scale(Vector3(-1, -1, -1)).is_orthogonal(),
+ "Basis with only a flip should be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_orthogonal(),
+ "Basis with only scale should be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis(Vector3(3, 4, 0), Vector3(4, -3, 0), Vector3(0, 0, 5)).is_orthogonal(),
+ "Basis with a flip, rotation, and uniform scale should be orthogonal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_orthogonal(),
+ "Basis with the X axis skewed 45 degrees should not be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_orthogonal(),
+ "Edge case: Basis with all zeroes should return true for is_orthogonal, since zero vectors are orthogonal to all vectors.");
+}
+
+TEST_CASE("[Basis] Is orthonormal checks") {
+ CHECK_MESSAGE(
+ Basis().is_orthonormal(),
+ "Identity Basis should be orthonormal.");
+
+ CHECK_MESSAGE(
+ Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_orthonormal(),
+ "Basis with only rotation should be orthonormal.");
+
+ CHECK_MESSAGE(
+ Basis::from_scale(Vector3(-1, -1, -1)).is_orthonormal(),
+ "Basis with only a flip should be orthonormal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_orthonormal(),
+ "Basis with only scale should not be orthonormal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(3, 4, 0), Vector3(4, -3, 0), Vector3(0, 0, 5)).is_orthonormal(),
+ "Basis with a flip, rotation, and uniform scale should not be orthonormal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_orthonormal(),
+ "Basis with the X axis skewed 45 degrees should not be orthonormal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_orthonormal(),
+ "Edge case: Basis with all zeroes should return false for is_orthonormal, since the vectors do not have a length of 1.");
+}
+
+TEST_CASE("[Basis] Is rotation checks") {
+ CHECK_MESSAGE(
+ Basis().is_rotation(),
+ "Identity Basis should be a rotation (a rotation of zero).");
+
+ CHECK_MESSAGE(
+ Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_rotation(),
+ "Basis with only rotation should be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis::from_scale(Vector3(-1, -1, -1)).is_rotation(),
+ "Basis with only a flip should not be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_rotation(),
+ "Basis with only scale should not be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(2, 0, 0), Vector3(0, 0.5, 0), Vector3(0, 0, 1)).is_rotation(),
+ "Basis with a squeeze should not be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_rotation(),
+ "Basis with the X axis skewed 45 degrees should not be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_rotation(),
+ "Edge case: Basis with all zeroes should return false for is_rotation, because it is not just a rotation (has a scale of 0).");
}
} // namespace TestBasis
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 02349aedc0..8a11491bb2 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -456,30 +456,93 @@ TEST_CASE("[String] Number to string") {
}
TEST_CASE("[String] String to integer") {
- static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" };
- static const int num[4] = { 1237461283, -22, 0, -1123412 };
+ static const char *nums[14] = { "1237461283", "- 22", "0", " - 1123412", "", "10_000_000", "-1_2_3_4", "10__000", " 1 2 34 ", "-0", "007", "--45", "---46", "-7-2" };
+ static const int num[14] = { 1237461283, -22, 0, -1123412, 0, 10000000, -1234, 10000, 1234, 0, 7, 45, -46, -72 };
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 14; i++) {
CHECK(String(nums[i]).to_int() == num[i]);
}
+ CHECK(String("0b1011").to_int() == 1011); // Looks like a binary number, but to_int() handles this as a base-10 number, "b" is just ignored.
+ CHECK(String("0x1012").to_int() == 1012); // Looks like a hexadecimal number, but to_int() handles this as a base-10 number, "x" is just ignored.
+
+ ERR_PRINT_OFF
+ CHECK(String("999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MAX); // Too large, largest possible is returned.
+ CHECK(String("-999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MIN); // Too small, smallest possible is returned.
+ ERR_PRINT_ON
}
TEST_CASE("[String] Hex to integer") {
- static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" };
- static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD };
+ static const char *nums[12] = { "0xFFAE", "22", "0", "AADDAD", "0x7FFFFFFFFFFFFFFF", "-0xf", "", "000", "000f", "0xaA", "-ff", "-" };
+ static const int64_t num[12] = { 0xFFAE, 0x22, 0, 0xAADDAD, 0x7FFFFFFFFFFFFFFF, -0xf, 0, 0, 0xf, 0xaa, -0xff, 0x0 };
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 12; i++) {
CHECK(String(nums[i]).hex_to_int() == num[i]);
}
+
+ // Invalid hex strings should return 0.
+ static const char *invalid_nums[15] = { "qwerty", "QWERTY", "0xqwerty", "0x00qwerty", "qwerty00", "0x", "0x__", "__", "x12", "+", " ff", "ff ", "f f", "+ff", "--0x78" };
+
+ ERR_PRINT_OFF
+ for (int i = 0; i < 15; i++) {
+ CHECK(String(invalid_nums[i]).hex_to_int() == 0);
+ }
+
+ CHECK(String("0xFFFFFFFFFFFFFFFFFFFFFFF").hex_to_int() == INT64_MAX); // Too large, largest possible is returned.
+ CHECK(String("-0xFFFFFFFFFFFFFFFFFFFFFFF").hex_to_int() == INT64_MIN); // Too small, smallest possible is returned.
+ ERR_PRINT_ON
+}
+
+TEST_CASE("[String] Bin to integer") {
+ static const char *nums[10] = { "", "0", "0b0", "0b1", "0b", "1", "0b1010", "-0b11", "-1010", "0b0111111111111111111111111111111111111111111111111111111111111111" };
+ static const int64_t num[10] = { 0, 0, 0, 1, 0, 1, 10, -3, -10, 0x7FFFFFFFFFFFFFFF };
+
+ for (int i = 0; i < 10; i++) {
+ CHECK(String(nums[i]).bin_to_int() == num[i]);
+ }
+
+ // Invalid bin strings should return 0. The long "0x11...11" is just too long for a 64 bit int.
+ static const char *invalid_nums[16] = { "qwerty", "QWERTY", "0bqwerty", "0b00qwerty", "qwerty00", "0x__", "0b__", "__", "b12", "+", "-", "0x12ab", " 11", "11 ", "1 1", "--0b11" };
+
+ for (int i = 0; i < 16; i++) {
+ CHECK(String(invalid_nums[i]).bin_to_int() == 0);
+ }
+
+ ERR_PRINT_OFF
+ CHECK(String("0b111111111111111111111111111111111111111111111111111111111111111111111111111111111").bin_to_int() == INT64_MAX); // Too large, largest possible is returned.
+ CHECK(String("-0b111111111111111111111111111111111111111111111111111111111111111111111111111111111").bin_to_int() == INT64_MIN); // Too small, smallest possible is returned.
+ ERR_PRINT_ON
}
TEST_CASE("[String] String to float") {
- static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" };
- static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 };
+ static const char *nums[12] = { "-12348298412.2", "0.05", "2.0002", " -0.0001", "0", "000", "123", "0.0", "000.000", "000.007", "234__", "3..14" };
+ static const double num[12] = { -12348298412.2, 0.05, 2.0002, -0.0001, 0.0, 0.0, 123.0, 0.0, 0.0, 0.007, 234.0, 3.0 };
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 12; i++) {
CHECK(!(ABS(String(nums[i]).to_float() - num[i]) > 0.00001));
}
+
+ // Invalid float strings should return 0.
+ static const char *invalid_nums[6] = { "qwerty", "qwerty123", "0xffff", "0b1010", "--3.13", "__345" };
+
+ for (int i = 0; i < 6; i++) {
+ CHECK(String(invalid_nums[i]).to_float() == 0);
+ }
+
+ // Very large exponents.
+ CHECK(String("1e308").to_float() == 1e308);
+ CHECK(String("-1e308").to_float() == -1e308);
+
+ // Exponent is so high that value is INFINITY/-INFINITY.
+ CHECK(String("1e309").to_float() == INFINITY);
+ CHECK(String("1e511").to_float() == INFINITY);
+ CHECK(String("-1e309").to_float() == -INFINITY);
+ CHECK(String("-1e511").to_float() == -INFINITY);
+
+ // Exponent is so high that a warning message is printed. Value is INFINITY/-INFINITY.
+ ERR_PRINT_OFF
+ CHECK(String("1e512").to_float() == INFINITY);
+ CHECK(String("-1e512").to_float() == -INFINITY);
+ ERR_PRINT_ON
}
TEST_CASE("[String] Slicing") {
diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h
index 9232a3020d..552f722d24 100644
--- a/tests/scene/test_primitives.h
+++ b/tests/scene/test_primitives.h
@@ -232,7 +232,7 @@ TEST_CASE("[SceneTree][Primitive][Cylinder] Cylinder Primitive") {
CHECK(cylinder->get_bottom_radius() > 0);
CHECK(cylinder->get_height() > 0);
CHECK(cylinder->get_radial_segments() > 0);
- CHECK(cylinder->get_rings() > 0);
+ CHECK(cylinder->get_rings() >= 0);
}
SUBCASE("[SceneTree][Primitive][Cylinder] Set properties and get them") {
diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp
index 3d87adaa77..f14c9362bd 100644
--- a/thirdparty/squish/colourblock.cpp
+++ b/thirdparty/squish/colourblock.cpp
@@ -24,9 +24,9 @@
-------------------------------------------------------------------------- */
#include "colourblock.h"
-// -- Godot start --
+// -- GODOT start --
#include "alpha.h"
-// -- Godot end --
+// -- GODOT end --
namespace squish {
@@ -214,7 +214,18 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 )
}
}
-// -- Godot start --
+// -- GODOT start --
+void DecompressColourBc4( u8* rgba, void const* block)
+{
+ DecompressAlphaDxt5(rgba,block);
+ for ( int i = 0; i < 16; ++i ) {
+ rgba[i*4] = rgba[i*4 + 3];
+ rgba[i*4 + 1] = 0;
+ rgba[i*4 + 2] = 0;
+ rgba[i*4 + 3] = 255;
+ }
+}
+
void DecompressColourBc5( u8* rgba, void const* block)
{
void const* rblock = block;
diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h
index 3cb9b7e3b9..e1eb9e4917 100644
--- a/thirdparty/squish/colourblock.h
+++ b/thirdparty/squish/colourblock.h
@@ -36,6 +36,7 @@ void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void*
void DecompressColour( u8* rgba, void const* block, bool isDxt1 );
// -- GODOT start --
+void DecompressColourBc4( u8* rgba, void const* block );
void DecompressColourBc5( u8* rgba, void const* block );
// -- GODOT end --
diff --git a/thirdparty/squish/godot-changes.patch b/thirdparty/squish/godot-changes.patch
index ef7bafb4b4..555fbc51d0 100644
--- a/thirdparty/squish/godot-changes.patch
+++ b/thirdparty/squish/godot-changes.patch
@@ -100,3 +100,112 @@ index 1d22a64ad..fd11a147d 100644
// decompress alpha separately if necessary
if( ( flags & kDxt3 ) != 0 )
+
+diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp
+index 49401358bc..f14c9362bd 100644
+--- a/thirdparty/squish/colourblock.cpp
++++ b/thirdparty/squish/colourblock.cpp
+@@ -24,9 +24,9 @@
+ -------------------------------------------------------------------------- */
+
+ #include "colourblock.h"
+-// -- Godot start --
++// -- GODOT start --
+ #include "alpha.h"
+-// -- Godot end --
++// -- GODOT end --
+
+ namespace squish {
+
+diff --git a/thirdparty/squish/godot-changes.patch b/thirdparty/squish/godot-changes.patch
+index ef7bafb4b4..655a8cffc2 100644
+--- a/thirdparty/squish/godot-changes.patch
++++ b/thirdparty/squish/godot-changes.patch
+@@ -1,22 +1,33 @@
+ diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp
+-index af8b98036..3d87adaa7 100644
++index af8b980365..f14c9362bd 100644
+ --- a/thirdparty/squish/colourblock.cpp
+ +++ b/thirdparty/squish/colourblock.cpp
+ @@ -24,6 +24,9 @@
+ -------------------------------------------------------------------------- */
+
+ #include "colourblock.h"
+-+// -- Godot start --
+++// -- GODOT start --
+ +#include "alpha.h"
+-+// -- Godot end --
+++// -- GODOT end --
+
+ namespace squish {
+
+-@@ -211,4 +214,23 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 )
++@@ -211,4 +214,34 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 )
+ }
+ }
+
+-+// -- Godot start --
+++// -- GODOT start --
+++void DecompressColourBc4( u8* rgba, void const* block)
+++{
+++ DecompressAlphaDxt5(rgba,block);
+++ for ( int i = 0; i < 16; ++i ) {
+++ rgba[i*4] = rgba[i*4 + 3];
+++ rgba[i*4 + 1] = 0;
+++ rgba[i*4 + 2] = 0;
+++ rgba[i*4 + 3] = 255;
+++ }
+++}
+++
+ +void DecompressColourBc5( u8* rgba, void const* block)
+ +{
+ + void const* rblock = block;
+@@ -37,21 +48,22 @@ index af8b98036..3d87adaa7 100644
+ +
+ } // namespace squish
+ diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h
+-index fee2cd7c5..3cb9b7e3b 100644
++index fee2cd7c5d..e1eb9e4917 100644
+ --- a/thirdparty/squish/colourblock.h
+ +++ b/thirdparty/squish/colourblock.h
+-@@ -35,6 +35,9 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void*
++@@ -35,6 +35,10 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void*
+ void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block );
+
+ void DecompressColour( u8* rgba, void const* block, bool isDxt1 );
+ +// -- GODOT start --
+++void DecompressColourBc4( u8* rgba, void const* block );
+ +void DecompressColourBc5( u8* rgba, void const* block );
+ +// -- GODOT end --
+
+ } // namespace squish
+
+ diff --git a/thirdparty/squish/config.h b/thirdparty/squish/config.h
+-index 92edefe96..05f8d7259 100644
++index 92edefe966..05f8d72598 100644
+ --- a/thirdparty/squish/config.h
+ +++ b/thirdparty/squish/config.h
+ @@ -32,6 +32,26 @@
+@@ -82,17 +94,19 @@ index 92edefe96..05f8d7259 100644
+ #define SQUISH_USE_SSE 0
+ #endif
+ diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp
+-index 1d22a64ad..fd11a147d 100644
++index 1d22a64ad6..086ba11cd0 100644
+ --- a/thirdparty/squish/squish.cpp
+ +++ b/thirdparty/squish/squish.cpp
+-@@ -135,7 +135,13 @@ void Decompress( u8* rgba, void const* block, int flags )
++@@ -135,7 +135,15 @@ void Decompress( u8* rgba, void const* block, int flags )
+ colourBlock = reinterpret_cast< u8 const* >( block ) + 8;
+
+ // decompress colour
+ - DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
+ + // -- GODOT start --
+ + //DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
+-+ if(( flags & ( kBc5 ) ) != 0)
+++ if(( flags & ( kBc4 ) ) != 0)
+++ DecompressColourBc4( rgba, colourBlock);
+++ else if(( flags & ( kBc5 ) ) != 0)
+ + DecompressColourBc5( rgba, colourBlock);
+ + else
+ + DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); \ No newline at end of file
diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp
index fd11a147de..1de1da3e52 100644
--- a/thirdparty/squish/squish.cpp
+++ b/thirdparty/squish/squish.cpp
@@ -137,7 +137,9 @@ void Decompress( u8* rgba, void const* block, int flags )
// decompress colour
// -- GODOT start --
//DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
- if(( flags & ( kBc5 ) ) != 0)
+ if(( flags & ( kBc4 ) ) != 0)
+ DecompressColourBc4( rgba, colourBlock);
+ else if(( flags & ( kBc5 ) ) != 0)
DecompressColourBc5( rgba, colourBlock);
else
DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );