summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/config/engine.cpp4
-rw-r--r--core/config/engine.h2
-rw-r--r--core/core_bind.cpp12
-rw-r--r--core/core_bind.h1
-rw-r--r--core/extension/gdextension.cpp13
-rw-r--r--core/io/remote_filesystem_client.h4
-rw-r--r--doc/classes/Geometry3D.xml7
-rw-r--r--doc/classes/Node.xml8
-rw-r--r--doc/classes/ProjectSettings.xml2
-rw-r--r--doc/classes/int.xml2
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp58
-rw-r--r--drivers/vulkan/rendering_device_vulkan.h1
-rw-r--r--drivers/vulkan/vulkan_context.cpp40
-rw-r--r--editor/icons/CurveTexture.svg2
-rw-r--r--editor/icons/CurveXYZTexture.svg1
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp6
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp7
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h2
-rw-r--r--main/main.cpp3
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd21
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out5
-rw-r--r--modules/glslang/register_types.cpp30
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml4
-rw-r--r--platform/android/export/export_plugin.cpp1
-rw-r--r--platform/android/export/gradle_export_util.cpp8
-rw-r--r--platform/android/java/app/AndroidManifest.xml1
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml1
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.h8
-rw-r--r--scene/2d/canvas_modulate.cpp67
-rw-r--r--scene/2d/canvas_modulate.h8
-rw-r--r--scene/main/viewport.cpp4
-rw-r--r--scene/main/viewport.h5
-rw-r--r--scene/main/window.cpp38
-rw-r--r--scene/main/window.h4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp14
-rw-r--r--servers/rendering/rendering_device.h1
-rw-r--r--tests/scene/test_window.h96
-rw-r--r--tests/servers/test_navigation_server_3d.h2
-rw-r--r--tests/test_main.cpp1
40 files changed, 414 insertions, 83 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 7fdea7d1aa..6727c58fd1 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -239,6 +239,10 @@ bool Engine::is_validation_layers_enabled() const {
return use_validation_layers;
}
+bool Engine::is_generate_spirv_debug_info_enabled() const {
+ return generate_spirv_debug_info;
+}
+
void Engine::set_print_error_messages(bool p_enabled) {
CoreGlobals::print_error_enabled = p_enabled;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 5ea653ba6c..73d40d50ae 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -67,6 +67,7 @@ private:
double _physics_interpolation_fraction = 0.0f;
bool abort_on_gpu_errors = false;
bool use_validation_layers = false;
+ bool generate_spirv_debug_info = false;
int32_t gpu_idx = -1;
uint64_t _process_frames = 0;
@@ -156,6 +157,7 @@ public:
bool is_abort_on_gpu_errors_enabled() const;
bool is_validation_layers_enabled() const;
+ bool is_generate_spirv_debug_info_enabled() const;
int32_t get_gpu_index() const;
Engine();
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 4e220d0839..05fe393a2f 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -929,6 +929,17 @@ Geometry3D *Geometry3D::get_singleton() {
return singleton;
}
+Vector<Vector3> Geometry3D::compute_convex_mesh_points(const TypedArray<Plane> &p_planes) {
+ Vector<Plane> planes_vec;
+ int size = p_planes.size();
+ planes_vec.resize(size);
+ for (int i = 0; i < size; ++i) {
+ planes_vec.set(i, p_planes[i]);
+ }
+ Variant ret = ::Geometry3D::compute_convex_mesh_points(planes_vec.ptr(), size);
+ return ret;
+}
+
TypedArray<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) {
Variant ret = ::Geometry3D::build_box_planes(p_extents);
return ret;
@@ -1029,6 +1040,7 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const
}
void Geometry3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points);
ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes);
ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z));
ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z));
diff --git a/core/core_bind.h b/core/core_bind.h
index 1cbbcdd251..5f51b64eb7 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -320,6 +320,7 @@ protected:
public:
static Geometry3D *get_singleton();
+ Vector<Vector3> compute_convex_mesh_points(const TypedArray<Plane> &p_planes);
TypedArray<Plane> build_box_planes(const Vector3 &p_extents);
TypedArray<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z);
TypedArray<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z);
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 67b55db3db..9a735f5aa6 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -607,12 +607,13 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
}
bool compatible = true;
- if (VERSION_MAJOR < compatibility_minimum[0]) {
- compatible = false;
- } else if (VERSION_MINOR < compatibility_minimum[1]) {
- compatible = false;
- } else if (VERSION_PATCH < compatibility_minimum[2]) {
- compatible = false;
+ // Check version lexicographically.
+ if (VERSION_MAJOR != compatibility_minimum[0]) {
+ compatible = VERSION_MAJOR > compatibility_minimum[0];
+ } else if (VERSION_MINOR != compatibility_minimum[1]) {
+ compatible = VERSION_MINOR > compatibility_minimum[1];
+ } else {
+ compatible = VERSION_PATCH >= compatibility_minimum[2];
}
if (!compatible) {
if (r_error) {
diff --git a/core/io/remote_filesystem_client.h b/core/io/remote_filesystem_client.h
index 42eba98eb1..fcb5c1cfc3 100644
--- a/core/io/remote_filesystem_client.h
+++ b/core/io/remote_filesystem_client.h
@@ -44,8 +44,8 @@ protected:
String _get_cache_path() { return cache_path; }
struct FileCache {
String path; // Local path (as in "folder/to/file.png")
- uint64_t server_modified_time; // MD5 checksum.
- uint64_t modified_time;
+ uint64_t server_modified_time = 0; // MD5 checksum.
+ uint64_t modified_time = 0;
};
virtual bool _is_configured() { return !cache_path.is_empty(); }
// Can be re-implemented per platform. If so, feel free to ignore get_cache_path()
diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml
index 85b8965faf..a85d17d925 100644
--- a/doc/classes/Geometry3D.xml
+++ b/doc/classes/Geometry3D.xml
@@ -45,6 +45,13 @@
Clips the polygon defined by the points in [param points] against the [param plane] and returns the points of the clipped polygon.
</description>
</method>
+ <method name="compute_convex_mesh_points">
+ <return type="PackedVector3Array" />
+ <param index="0" name="planes" type="Plane[]" />
+ <description>
+ Calculates and returns all the vertex points of a convex shape defined by an array of [param planes].
+ </description>
+ </method>
<method name="get_closest_point_to_segment">
<return type="Vector3" />
<param index="0" name="point" type="Vector3" />
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 49ab3918bb..7510d6bb64 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -1022,12 +1022,12 @@
Notification received right after the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects.
</constant>
<constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002">
- Notification received from the OS when the mouse enters the game window.
- Implemented on desktop and web platforms.
+ Notification received when the mouse enters the window.
+ Implemented for embedded windows and on desktop and web platforms.
</constant>
<constant name="NOTIFICATION_WM_MOUSE_EXIT" value="1003">
- Notification received from the OS when the mouse leaves the game window.
- Implemented on desktop and web platforms.
+ Notification received when the mouse leaves the window.
+ Implemented for embedded windows and on desktop and web platforms.
</constant>
<constant name="NOTIFICATION_WM_WINDOW_FOCUS_IN" value="1004">
Notification received when the node's parent [Window] is focused. This may be a change of focus between two windows of the same engine instance, or from the OS desktop or a third-party application to a window of the game (in which case [constant NOTIFICATION_APPLICATION_FOCUS_IN] is also emitted).
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 3eb1959a44..1be69052e4 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -298,7 +298,7 @@
[b]Note:[/b] Changing this value can help on platforms or with third-party tools where hidden directory patterns are disallowed. Only modify this setting if you know that your environment requires it, as changing the default can impact compatibility with some external tools or plugins which expect the default [code].godot[/code] folder.
</member>
<member name="application/config/version" type="String" setter="" getter="" default="&quot;&quot;">
- The project's human-readable version identifier. This should always be set to a non-empty string, as some exporters rely on this value being defined.
+ The project's human-readable version identifier. This is used by exporters if the version identifier isn't overridden there. If [member application/config/version] is an empty string and the version identifier isn't overridden in an exporter, the exporter will use [code]1.0.0[/code] as a version identifier.
</member>
<member name="application/config/windows_native_icon" type="String" setter="" getter="" default="&quot;&quot;">
Icon set in [code].ico[/code] format used on Windows to set the game's icon. This is done automatically on start by calling [method DisplayServer.set_native_icon].
diff --git a/doc/classes/int.xml b/doc/classes/int.xml
index bc0da03e98..914cf75929 100644
--- a/doc/classes/int.xml
+++ b/doc/classes/int.xml
@@ -390,7 +390,7 @@
<operator name="operator ~">
<return type="int" />
<description>
- Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement/]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code].
+ Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code].
[codeblock]
print(~4) # Prints -5
print(~(-7)) # Prints 6
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 1ed6839dd7..a55abe6a82 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -5905,6 +5905,64 @@ void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_
us->invalidated_callback_userdata = p_userdata;
}
+Error RenderingDeviceVulkan::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER,
+ "Copying buffers is forbidden during creation of a draw list");
+ ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER,
+ "Copying buffers is forbidden during creation of a compute list");
+
+ // This method assumes the barriers have been pushed prior to being called, therefore no barriers are pushed
+ // for the source or destination buffers before performing the copy. These masks are effectively ignored.
+ VkPipelineShaderStageCreateFlags src_stage_mask = 0;
+ VkAccessFlags src_access_mask = 0;
+ Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer, src_stage_mask, src_access_mask, BARRIER_MASK_NO_BARRIER);
+ if (!src_buffer) {
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type.");
+ }
+
+ VkPipelineStageFlags dst_stage_mask = 0;
+ VkAccessFlags dst_access = 0;
+ if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
+ // If the post barrier mask defines it, we indicate the destination buffer will require a barrier with these flags set
+ // after the copy command is queued.
+ dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dst_access = VK_ACCESS_TRANSFER_WRITE_BIT;
+ }
+
+ Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer, dst_stage_mask, dst_access, p_post_barrier);
+ if (!dst_buffer) {
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type.");
+ }
+
+ // Validate the copy's dimensions for both buffers.
+ ERR_FAIL_COND_V_MSG((p_size + p_src_offset) > src_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the source buffer.");
+ ERR_FAIL_COND_V_MSG((p_size + p_dst_offset) > dst_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the destination buffer.");
+
+ // Perform the copy.
+ VkBufferCopy region;
+ region.srcOffset = p_src_offset;
+ region.dstOffset = p_dst_offset;
+ region.size = p_size;
+ vkCmdCopyBuffer(frames[frame].draw_command_buffer, src_buffer->buffer, dst_buffer->buffer, 1, &region);
+
+#ifdef FORCE_FULL_BARRIER
+ _full_barrier(true);
+#else
+ if (dst_stage_mask == 0) {
+ dst_stage_mask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ }
+
+ // As indicated by the post barrier mask, push a new barrier.
+ if (p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) {
+ _buffer_memory_barrier(dst_buffer->buffer, p_dst_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, true);
+ }
+#endif
+
+ return OK;
+}
+
Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier) {
_THREAD_SAFE_METHOD_
diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h
index fd832312ac..edff19a70c 100644
--- a/drivers/vulkan/rendering_device_vulkan.h
+++ b/drivers/vulkan/rendering_device_vulkan.h
@@ -1152,6 +1152,7 @@ public:
virtual bool uniform_set_is_valid(RID p_uniform_set);
virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata);
+ virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS);
virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); // Works for any buffer.
virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS);
virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0);
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index c167caeb7c..c1632907eb 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -504,6 +504,10 @@ Error VulkanContext::_initialize_device_extensions() {
register_requested_device_extension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false);
register_requested_device_extension(VK_KHR_MAINTENANCE_2_EXTENSION_NAME, false);
+ if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) {
+ register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true);
+ }
+
// TODO consider the following extensions:
// - VK_KHR_spirv_1_4
// - VK_KHR_swapchain_mutable_format
@@ -1700,17 +1704,6 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, Display
Error err = _update_swap_chain(&window);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
- VkSemaphoreCreateInfo semaphoreCreateInfo = {
- /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
- /*pNext*/ nullptr,
- /*flags*/ 0,
- };
-
- for (uint32_t i = 0; i < FRAME_LAG; i++) {
- VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window.image_acquired_semaphores[i]);
- ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE);
- }
-
windows[p_window_id] = window;
return OK;
}
@@ -1760,9 +1753,6 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi
void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) {
ERR_FAIL_COND(!windows.has(p_window_id));
_clean_up_swap_chain(&windows[p_window_id]);
- for (uint32_t i = 0; i < FRAME_LAG; i++) {
- vkDestroySemaphore(device, windows[p_window_id].image_acquired_semaphores[i], nullptr);
- }
vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr);
windows.erase(p_window_id);
@@ -1792,6 +1782,17 @@ Error VulkanContext::_clean_up_swap_chain(Window *window) {
if (separate_present_queue) {
vkDestroyCommandPool(device, window->present_cmd_pool, nullptr);
}
+
+ for (uint32_t i = 0; i < FRAME_LAG; i++) {
+ // Destroy the semaphores now (we'll re-create it later if we have to).
+ // We must do this because the semaphore cannot be reused if it's in a signaled state
+ // (which happens if vkAcquireNextImageKHR returned VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR)
+ // The only way to reset it would be to present the swapchain... the one we just destroyed.
+ // And the API has no way to "unsignal" the semaphore.
+ vkDestroySemaphore(device, window->image_acquired_semaphores[i], nullptr);
+ window->image_acquired_semaphores[i] = 0;
+ }
+
return OK;
}
@@ -2175,6 +2176,17 @@ Error VulkanContext::_update_swap_chain(Window *window) {
// Reset current buffer.
window->current_buffer = 0;
+ VkSemaphoreCreateInfo semaphoreCreateInfo = {
+ /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ /*pNext*/ nullptr,
+ /*flags*/ 0,
+ };
+
+ for (uint32_t i = 0; i < FRAME_LAG; i++) {
+ VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window->image_acquired_semaphores[i]);
+ ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE);
+ }
+
return OK;
}
diff --git a/editor/icons/CurveTexture.svg b/editor/icons/CurveTexture.svg
index 50232fdc4a..a5524f145c 100644
--- a/editor/icons/CurveTexture.svg
+++ b/editor/icons/CurveTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11c.331 0 .666-.008 1-.014V3h10v1.135a3 3 0 0 1 2-.006V2a1 1 0 0 0-1-1H2zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.967.988a1 1 0 0 0-.928.739c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-.994-1.287z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11h1V3h10v1.135a3 3 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.039 1.727c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-1.922-.548z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/CurveXYZTexture.svg b/editor/icons/CurveXYZTexture.svg
new file mode 100644
index 0000000000..e376dd434b
--- /dev/null
+++ b/editor/icons/CurveXYZTexture.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v10a2 2 0 0 1 .75-.1Q3 11.9 3 10.62V3h10v6.5a2 2 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v.5a2 2 0 0 1 2.7.5h1.2a2 2 0 0 1 3.2 0h.9V8h-1V6h-1V5z" fill="#e0e0e0"/><g stroke-width="1.6" stroke-linecap="round" fill="none"><path d="M2 14.2q2.5 0 3-3" stroke="#ff5f5f"/><path d="M6.5 14.2q2.5 0 3-3" stroke="#5fff97"/><path d="M11 14.2q2.5 0 3-3" stroke="#5fb2ff"/></g></svg>
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index c98d9086d1..b1111be006 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2655,6 +2655,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {
memdelete(tile_proxy_object);
memdelete(atlas_source_proxy_object);
+
+ // Remove listener for old objects, so the TileSet doesn't
+ // try to call the destroyed TileSetAtlasSourceEditor.
+ if (tile_set.is_valid()) {
+ tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
+ }
}
////// EditorPropertyTilePolygon //////
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 121b70a74f..e432704702 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -325,6 +325,7 @@ void TileMapEditorPlugin::_tile_map_changed() {
}
void TileMapEditorPlugin::_update_tile_map() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
Ref<TileSet> tile_set = tile_map->get_tileset();
if (tile_set.is_valid() && edited_tileset != tile_set->get_instance_id()) {
@@ -347,11 +348,17 @@ void TileMapEditorPlugin::_notification(int p_notification) {
}
void TileMapEditorPlugin::edit(Object *p_object) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
tile_map->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed));
}
tile_map = Object::cast_to<TileMap>(p_object);
+ if (tile_map) {
+ tile_map_id = tile_map->get_instance_id();
+ } else {
+ tile_map_id = ObjectID();
+ }
editor->edit(tile_map);
if (tile_map) {
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index f8e944af81..81cb48eb00 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -115,7 +115,7 @@ class TileMapEditorPlugin : public EditorPlugin {
TileMapEditor *editor = nullptr;
Button *button = nullptr;
- TileMap *tile_map = nullptr;
+ ObjectID tile_map_id;
bool tile_map_changed_needs_update = false;
ObjectID edited_tileset;
diff --git a/main/main.cpp b/main/main.cpp
index 220afda5de..f79a71474d 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -458,6 +458,7 @@ void Main::print_help(const char *p_binary) {
#if DEBUG_ENABLED
OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n");
#endif
+ OS::get_singleton()->print(" --generate-spirv-debug-info Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
OS::get_singleton()->print(" --single-threaded-scene Scene tree runs in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
#if defined(DEBUG_ENABLED)
@@ -1019,6 +1020,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "--gpu-abort") {
Engine::singleton->abort_on_gpu_errors = true;
#endif
+ } else if (I->get() == "--generate-spirv-debug-info") {
+ Engine::singleton->generate_spirv_debug_info = true;
} else if (I->get() == "--tablet-driver") {
if (I->next()) {
tablet_driver = I->next()->get();
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index cb04913620..ad4528747b 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3469,6 +3469,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
for (GDScriptParser::ClassNode *script_class : script_classes) {
if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) {
reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype());
+ if (script_class->outer != nullptr) {
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
+ }
return;
}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd
new file mode 100644
index 0000000000..51c589b8e0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd
@@ -0,0 +1,21 @@
+# GH-80508
+
+class A:
+ func a():
+ return A.new()
+ func b():
+ return B.new()
+
+class B:
+ func a():
+ return A.new()
+ func b():
+ return B.new()
+
+func test():
+ var a := A.new()
+ var b := B.new()
+ print(a.a() is A)
+ print(a.b() is B)
+ print(b.a() is A)
+ print(b.b() is B)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out
new file mode 100644
index 0000000000..f9783e4362
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+true
+true
+true
+true
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index 622910761d..2b070c24b8 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -32,6 +32,7 @@
#include "glslang_resource_limits.h"
+#include "core/config/engine.h"
#include "servers/rendering/rendering_device.h"
#include <glslang/Include/Types.h>
@@ -56,7 +57,6 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2;
glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5;
- glslang::TShader::ForbidIncluder includer;
if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) {
if (capabilities->version_major == 1 && capabilities->version_minor == 0) {
@@ -127,23 +127,10 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
}
EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
- const int DefaultVersion = 100;
- std::string pre_processed_code;
-
- //preprocess
- if (!shader.preprocess(&DefaultTBuiltInResource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) {
- if (r_error) {
- (*r_error) = "Failed pre-process:\n";
- (*r_error) += shader.getInfoLog();
- (*r_error) += "\n";
- (*r_error) += shader.getInfoDebugLog();
- }
-
- return ret;
+ if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) {
+ messages = (EShMessages)(messages | EShMsgDebugInfo);
}
- //set back..
- cs_strings = pre_processed_code.c_str();
- shader.setStrings(&cs_strings, 1);
+ const int DefaultVersion = 100;
//parse
if (!shader.parse(&DefaultTBuiltInResource, DefaultVersion, false, messages)) {
@@ -174,6 +161,13 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
std::vector<uint32_t> SpirV;
spv::SpvBuildLogger logger;
glslang::SpvOptions spvOptions;
+
+ if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) {
+ spvOptions.generateDebugInfo = true;
+ spvOptions.emitNonSemanticShaderDebugInfo = true;
+ spvOptions.emitNonSemanticShaderDebugSource = true;
+ }
+
glslang::GlslangToSpv(*program.getIntermediate(stages[p_stage]), SpirV, &logger, &spvOptions);
ret.resize(SpirV.size() * sizeof(uint32_t));
@@ -188,7 +182,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
static String _get_cache_key_function_glsl(const RenderingDevice *p_render_device) {
const RD::Capabilities *capabilities = p_render_device->get_device_capabilities();
String version;
- version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS));
+ version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)) + " , debug=" + itos(Engine::get_singleton()->is_generate_spirv_debug_info_enabled());
return version;
}
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index c0c57cafbe..d270980d72 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -110,6 +110,10 @@
<member name="package/show_in_android_tv" type="bool" setter="" getter="">
If [code]true[/code], this app will show in Android TV launcher UI.
</member>
+ <member name="package/show_in_app_library" type="bool" setter="" getter="">
+ If [code]true[/code], this app will show in the device's app library.
+ [b]Note:[/b] This is [code]true[/code] by default.
+ </member>
<member name="package/signed" type="bool" setter="" getter="">
If [code]true[/code], package signing is enabled.
</member>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 6f0341d75c..20aaed1e3e 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1838,6 +1838,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_android_tv"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_app_library"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_as_launcher_app"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index d0d0c34bb4..0915009235 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -269,7 +269,12 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con
manifest_activity_text += " <intent-filter>\n"
" <action android:name=\"android.intent.action.MAIN\" />\n"
- " <category android:name=\"android.intent.category.LAUNCHER\" />\n";
+ " <category android:name=\"android.intent.category.DEFAULT\" />\n";
+
+ bool show_in_app_library = p_preset->get("package/show_in_app_library");
+ if (show_in_app_library) {
+ manifest_activity_text += " <category android:name=\"android.intent.category.LAUNCHER\" />\n";
+ }
bool uses_leanback_category = p_preset->get("package/show_in_android_tv");
if (uses_leanback_category) {
@@ -279,7 +284,6 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con
bool uses_home_category = p_preset->get("package/show_as_launcher_app");
if (uses_home_category) {
manifest_activity_text += " <category android:name=\"android.intent.category.HOME\" />\n";
- manifest_activity_text += " <category android:name=\"android.intent.category.DEFAULT\" />\n";
}
manifest_activity_text += " </intent-filter>\n";
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 542ab51660..56d403a263 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -45,6 +45,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index c7b2c8ad67..cb89d6e1b0 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -45,6 +45,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h
index 59e20fec45..d3a25506a8 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.h
+++ b/platform/linuxbsd/x11/gl_manager_x11.h
@@ -74,17 +74,17 @@ private:
};
struct GLDisplay {
- GLDisplay() { context = nullptr; }
+ GLDisplay() {}
~GLDisplay();
GLManager_X11_Private *context = nullptr;
- ::Display *x11_display;
- XVisualInfo x_vi;
+ ::Display *x11_display = nullptr;
+ XVisualInfo x_vi = {};
};
// just for convenience, window and display struct
struct XWinDisp {
::Window x11_window;
- ::Display *x11_display;
+ ::Display *x11_display = nullptr;
} _x_windisp;
LocalVector<GLWindow> _windows;
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index 7dd2e75f09..2c5c6a1a16 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -30,32 +30,67 @@
#include "canvas_modulate.h"
+void CanvasModulate::_on_in_canvas_visibility_changed(bool p_new_visibility) {
+ RID canvas = get_canvas();
+ StringName group_name = "_canvas_modulate_" + itos(canvas.get_id());
+
+ ERR_FAIL_COND_MSG(p_new_visibility == is_in_group(group_name), vformat("CanvasModulate becoming %s in the canvas already %s in the modulate group. Buggy logic, please report.", p_new_visibility ? "visible" : "invisible", p_new_visibility ? "was" : "was not"));
+
+ if (p_new_visibility) {
+ bool has_active_canvas_modulate = get_tree()->has_group(group_name); // Group would be removed if empty; otherwise one CanvasModulate within must be active.
+ add_to_group(group_name);
+ if (!has_active_canvas_modulate) {
+ is_active = true;
+ RS::get_singleton()->canvas_set_modulate(canvas, color);
+ }
+ } else {
+ remove_from_group(group_name);
+ if (is_active) {
+ is_active = false;
+ CanvasModulate *new_active = Object::cast_to<CanvasModulate>(get_tree()->get_first_node_in_group(group_name));
+ if (new_active) {
+ new_active->is_active = true;
+ RS::get_singleton()->canvas_set_modulate(canvas, new_active->color);
+ } else {
+ RS::get_singleton()->canvas_set_modulate(canvas, Color(1, 1, 1, 1));
+ }
+ }
+ }
+
+ update_configuration_warnings();
+}
+
void CanvasModulate::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_CANVAS: {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
- add_to_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ is_in_canvas = true;
+ bool visible_in_tree = is_visible_in_tree();
+ if (visible_in_tree) {
+ _on_in_canvas_visibility_changed(true);
}
+ was_visible_in_tree = visible_in_tree;
} break;
case NOTIFICATION_EXIT_CANVAS: {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1));
- remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ is_in_canvas = false;
+ if (was_visible_in_tree) {
+ _on_in_canvas_visibility_changed(false);
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
- add_to_group("_canvas_modulate_" + itos(get_canvas().get_id()));
- } else {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1));
- remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ if (!is_in_canvas) {
+ return;
}
- update_configuration_warnings();
+ bool visible_in_tree = is_visible_in_tree();
+ if (visible_in_tree == was_visible_in_tree) {
+ return;
+ }
+
+ _on_in_canvas_visibility_changed(visible_in_tree);
+
+ was_visible_in_tree = visible_in_tree;
} break;
}
}
@@ -69,7 +104,7 @@ void CanvasModulate::_bind_methods() {
void CanvasModulate::set_color(const Color &p_color) {
color = p_color;
- if (is_visible_in_tree()) {
+ if (is_active) {
RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
}
}
@@ -81,12 +116,12 @@ Color CanvasModulate::get_color() const {
PackedStringArray CanvasModulate::get_configuration_warnings() const {
PackedStringArray warnings = Node::get_configuration_warnings();
- if (is_visible_in_tree() && is_inside_tree()) {
+ if (is_in_canvas && is_visible_in_tree()) {
List<Node *> nodes;
get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes);
if (nodes.size() > 1) {
- warnings.push_back(RTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored."));
+ warnings.push_back(RTR("Only one visible CanvasModulate is allowed per canvas.\nWhen there are more than one, only one of them will be active. Which one is undefined."));
}
}
diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h
index 3b11cf71f1..08ded52e23 100644
--- a/scene/2d/canvas_modulate.h
+++ b/scene/2d/canvas_modulate.h
@@ -38,6 +38,14 @@ class CanvasModulate : public Node2D {
Color color = Color(1, 1, 1, 1);
+ // CanvasModulate is in canvas-specific modulate group when both in canvas and visible in tree.
+ // Exactly one CanvasModulate in each such non-empty group is active.
+ bool is_in_canvas = false;
+ bool was_visible_in_tree = false; // Relevant only when in canvas.
+ bool is_active = false;
+
+ void _on_in_canvas_visibility_changed(bool p_new_visibility);
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 2d3aa66f2c..180efaaa60 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2955,7 +2955,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
void Viewport::_update_mouse_over() {
// Update gui.mouse_over and gui.subwindow_over in all Viewports.
- // Send necessary mouse_enter/mouse_exit signals and the NOTIFICATION_VP_MOUSE_ENTER/NOTIFICATION_VP_MOUSE_EXIT notifications for every Viewport in the SceneTree.
+ // Send necessary mouse_enter/mouse_exit signals and the MOUSE_ENTER/MOUSE_EXIT notifications for every Viewport in the SceneTree.
if (is_attached_in_viewport()) {
// Execute this function only, when it is processed by a native Window or a SubViewport, that has no SubViewportContainer as parent.
@@ -3009,7 +3009,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
}
gui.subwindow_over = sw;
if (!sw->is_input_disabled()) {
- sw->notification(NOTIFICATION_VP_MOUSE_ENTER);
+ sw->_propagate_window_notification(sw, NOTIFICATION_WM_MOUSE_ENTER);
}
}
if (!sw->is_input_disabled()) {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 331ce98cdd..1e107ea99c 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -468,7 +468,8 @@ private:
SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point);
void _update_mouse_over();
- void _update_mouse_over(Vector2 p_pos);
+ virtual void _update_mouse_over(Vector2 p_pos);
+ virtual void _mouse_leave_viewport();
virtual bool _can_consume_input_events() const { return true; }
uint64_t event_count = 0;
@@ -482,8 +483,6 @@ protected:
Size2i _get_size_2d_override() const;
bool _is_size_allocated() const;
- void _mouse_leave_viewport();
-
void _notification(int p_what);
void _process_picking();
static void _bind_methods();
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 0b95406b94..1af279d94c 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -679,7 +679,7 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
}
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
root->gui.windowmanager_window_over = this;
- notification(NOTIFICATION_VP_MOUSE_ENTER);
+ mouse_in_window = true;
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape
}
@@ -692,6 +692,7 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
#endif // DEV_ENABLED
return;
}
+ mouse_in_window = false;
root->gui.windowmanager_window_over->_mouse_leave_viewport();
root->gui.windowmanager_window_over = nullptr;
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
@@ -2552,6 +2553,41 @@ bool Window::is_attached_in_viewport() const {
return get_embedder();
}
+void Window::_update_mouse_over(Vector2 p_pos) {
+ if (!mouse_in_window) {
+ if (is_embedded()) {
+ mouse_in_window = true;
+ _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
+ } else {
+ // Prevent update based on delayed InputEvents from DisplayServer.
+ return;
+ }
+ }
+
+ bool new_in = get_visible_rect().has_point(p_pos);
+ if (new_in == gui.mouse_in_viewport) {
+ if (new_in) {
+ Viewport::_update_mouse_over(p_pos);
+ }
+ return;
+ }
+
+ if (new_in) {
+ notification(NOTIFICATION_VP_MOUSE_ENTER);
+ Viewport::_update_mouse_over(p_pos);
+ } else {
+ Viewport::_mouse_leave_viewport();
+ }
+}
+
+void Window::_mouse_leave_viewport() {
+ Viewport::_mouse_leave_viewport();
+ if (is_embedded()) {
+ mouse_in_window = false;
+ _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
+ }
+}
+
void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title);
diff --git a/scene/main/window.h b/scene/main/window.h
index d5b8cd4ead..c387ffa92a 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -203,6 +203,10 @@ private:
void _event_callback(DisplayServer::WindowEvent p_event);
virtual bool _can_consume_input_events() const override;
+ bool mouse_in_window = false;
+ void _update_mouse_over(Vector2 p_pos) override;
+ void _mouse_leave_viewport() override;
+
Ref<Shortcut> debugger_stop_shortcut;
protected:
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 4bfec8ae8d..56f2ea0b0c 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -1309,16 +1309,13 @@ void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) {
RID new_buffer = RD::get_singleton()->storage_buffer_create(new_buffer_size);
if (multimesh->buffer_set && multimesh->data_cache.is_empty()) {
- // If the buffer was set but there's no data cached in the CPU, we must download it from the GPU and
- // upload it because RD does not provide a way to copy the buffer directly yet.
+ // If the buffer was set but there's no data cached in the CPU, we copy the buffer directly on the GPU.
RD::get_singleton()->barrier();
- Vector<uint8_t> buffer_data = RD::get_singleton()->buffer_get_data(multimesh->buffer);
- ERR_FAIL_COND(buffer_data.size() != int(buffer_size));
- RD::get_singleton()->buffer_update(new_buffer, 0, buffer_size, buffer_data.ptr(), RD::BARRIER_MASK_NO_BARRIER);
- RD::get_singleton()->buffer_update(new_buffer, buffer_size, buffer_size, buffer_data.ptr());
+ RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, 0, buffer_size, RD::BARRIER_MASK_NO_BARRIER);
+ RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, buffer_size, buffer_size);
} else if (!multimesh->data_cache.is_empty()) {
// Simply upload the data cached in the CPU, which should already be doubled in size.
- ERR_FAIL_COND(multimesh->data_cache.size() != int(new_buffer_size));
+ ERR_FAIL_COND(multimesh->data_cache.size() * sizeof(float) != size_t(new_buffer_size));
RD::get_singleton()->buffer_update(new_buffer, 0, new_buffer_size, multimesh->data_cache.ptr());
}
@@ -1328,6 +1325,9 @@ void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) {
multimesh->buffer = new_buffer;
multimesh->uniform_set_3d = RID(); // Cleared by dependency.
+
+ // Invalidate any references to the buffer that was released and the uniform set that was pointing to it.
+ multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH);
}
void MeshStorage::_multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset) {
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 30d9b1c7b7..1ade1b25c4 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -840,6 +840,7 @@ public:
virtual bool uniform_set_is_valid(RID p_uniform_set) = 0;
virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) = 0;
+ virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0;
virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0;
virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0;
virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0) = 0; // This causes stall, only use to retrieve large buffers for saving.
diff --git a/tests/scene/test_window.h b/tests/scene/test_window.h
new file mode 100644
index 0000000000..e0c55101de
--- /dev/null
+++ b/tests/scene/test_window.h
@@ -0,0 +1,96 @@
+/**************************************************************************/
+/* test_window.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_WINDOW_H
+#define TEST_WINDOW_H
+
+#include "scene/gui/control.h"
+#include "scene/main/window.h"
+
+#include "tests/test_macros.h"
+
+namespace TestWindow {
+
+class NotificationControl : public Control {
+ GDCLASS(NotificationControl, Control);
+
+protected:
+ void _notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_MOUSE_ENTER: {
+ mouse_over = true;
+ } break;
+
+ case NOTIFICATION_MOUSE_EXIT: {
+ mouse_over = false;
+ } break;
+ }
+ }
+
+public:
+ bool mouse_over = false;
+};
+
+TEST_CASE("[SceneTree][Window]") {
+ Window *root = SceneTree::get_singleton()->get_root();
+
+ SUBCASE("Control-mouse-over within Window-black bars should not happen") {
+ Window *w = memnew(Window);
+ root->add_child(w);
+ w->set_size(Size2i(400, 200));
+ w->set_position(Size2i(0, 0));
+ w->set_content_scale_size(Size2i(200, 200));
+ w->set_content_scale_mode(Window::CONTENT_SCALE_MODE_CANVAS_ITEMS);
+ w->set_content_scale_aspect(Window::CONTENT_SCALE_ASPECT_KEEP);
+ NotificationControl *c = memnew(NotificationControl);
+ w->add_child(c);
+ c->set_size(Size2i(100, 100));
+ c->set_position(Size2i(-50, -50));
+
+ CHECK_FALSE(c->mouse_over);
+ SEND_GUI_MOUSE_MOTION_EVENT(Point2i(110, 10), MouseButtonMask::NONE, Key::NONE);
+ CHECK(c->mouse_over);
+ SEND_GUI_MOUSE_MOTION_EVENT(Point2i(90, 10), MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(c->mouse_over); // GH-80011
+
+ /* TODO:
+ SEND_GUI_MOUSE_BUTTON_EVENT(Point2i(90, 10), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Point2i(90, 10), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK(Control was not pressed);
+ */
+
+ memdelete(c);
+ memdelete(w);
+ }
+}
+
+} // namespace TestWindow
+
+#endif // TEST_WINDOW_H
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index 8ea69ec67c..a116559cb2 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -423,6 +423,7 @@ TEST_SUITE("[Navigation]") {
navigation_server->free(map);
}
+#ifndef DISABLE_DEPRECATED
// This test case uses only public APIs on purpose - other test cases use simplified baking.
// FIXME: Remove once deprecated `region_bake_navigation_mesh()` is removed.
TEST_CASE("[NavigationServer3D][SceneTree][DEPRECATED] Server should be able to bake map correctly") {
@@ -470,6 +471,7 @@ TEST_SUITE("[Navigation]") {
memdelete(mesh_instance);
memdelete(node_3d);
}
+#endif // DISABLE_DEPRECATED
// This test case uses only public APIs on purpose - other test cases use simplified baking.
TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to bake map correctly") {
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 5ca03a20af..f1e348345b 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -115,6 +115,7 @@
#include "tests/scene/test_theme.h"
#include "tests/scene/test_viewport.h"
#include "tests/scene/test_visual_shader.h"
+#include "tests/scene/test_window.h"
#include "tests/servers/rendering/test_shader_preprocessor.h"
#include "tests/servers/test_navigation_server_2d.h"
#include "tests/servers/test_navigation_server_3d.h"