diff options
58 files changed, 1733 insertions, 958 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index aef8f83a53..ac47967b26 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -85,6 +85,12 @@ jobs: sudo add-apt-repository ppa:kisak/kisak-mesa sudo apt-get install -qq mesa-vulkan-drivers + - name: Free disk space on runner + run: | + echo "Disk usage before:" && df -h + sudo rm -rf /usr/local/lib/android + echo "Disk usage after:" && df -h + - name: Setup Godot build cache uses: ./.github/actions/godot-cache with: diff --git a/core/io/image.cpp b/core/io/image.cpp index 7326563f18..a5fea09113 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3017,6 +3017,7 @@ ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_dds_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr; @@ -3488,6 +3489,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer); + ClassDB::bind_method(D_METHOD("load_dds_from_buffer", "buffer"), &Image::load_dds_from_buffer); ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0)); @@ -3863,6 +3865,14 @@ Error Image::load_svg_from_string(const String &p_svg_str, float scale) { return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale); } +Error Image::load_dds_from_buffer(const Vector<uint8_t> &p_array) { + ERR_FAIL_NULL_V_MSG( + _dds_mem_loader_func, + ERR_UNAVAILABLE, + "The DDS module isn't enabled. Recompile the Godot editor or export template binary with the `module_dds_enabled=yes` SCons option."); + return _load_from_buffer(p_array, _dds_mem_loader_func); +} + void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); diff --git a/core/io/image.h b/core/io/image.h index f877b00ee6..f68543ba24 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -150,6 +150,7 @@ public: static ImageMemLoadFunc _tga_mem_loader_func; static ImageMemLoadFunc _bmp_mem_loader_func; static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func; + static ImageMemLoadFunc _dds_mem_loader_func; static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels); @@ -402,6 +403,7 @@ public: Error load_webp_from_buffer(const Vector<uint8_t> &p_array); Error load_tga_from_buffer(const Vector<uint8_t> &p_array); Error load_bmp_from_buffer(const Vector<uint8_t> &p_array); + Error load_dds_from_buffer(const Vector<uint8_t> &p_array); Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0); Error load_svg_from_string(const String &p_svg_str, float scale = 1.0); diff --git a/core/string/locales.h b/core/string/locales.h index 8a7efb4fd1..840fca65a7 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -1057,8 +1057,8 @@ static const char *script_list[][2] = { { "Hangul", "Hang" }, { "Han", "Hani" }, { "Hanunoo", "Hano" }, - { "Simplified", "Hans" }, - { "Traditional", "Hant" }, + { "Simplified Han", "Hans" }, + { "Traditional Han", "Hant" }, { "Hatran", "Hatr" }, { "Hebrew", "Hebr" }, { "Hiragana", "Hira" }, @@ -1110,7 +1110,7 @@ static const char *script_list[][2] = { { "Mro", "Mroo" }, { "Meitei Mayek", "Mtei" }, { "Multani", "Mult" }, - { "Myanmar (Burmese)", "Mymr" }, + { "Myanmar / Burmese", "Mymr" }, { "Nag Mundari", "Nagm" }, { "Nandinagari", "Nand" }, { "Old North Arabian", "Narb" }, diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 54c38f2db9..ee790b6968 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1094,15 +1094,15 @@ </signal> <signal name="mouse_entered"> <description> - Emitted when the mouse enters the control's [code]Rect[/code] area, provided its [member mouse_filter] lets the event reach it. - [b]Note:[/b] [signal mouse_entered] will not be emitted if the mouse enters a child [Control] node before entering the parent's [code]Rect[/code] area, at least until the mouse is moved to reach the parent's [code]Rect[/code] area. + Emitted when the mouse cursor enters the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not. + [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the signal. </description> </signal> <signal name="mouse_exited"> <description> - Emitted when the mouse leaves the control's [code]Rect[/code] area, provided its [member mouse_filter] lets the event reach it. - [b]Note:[/b] [signal mouse_exited] will be emitted if the mouse enters a child [Control] node, even if the mouse cursor is still inside the parent's [code]Rect[/code] area. - If you want to check whether the mouse truly left the area, ignoring any top nodes, you can use code like this: + Emitted when the mouse cursor leaves the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not. + [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the signal. + [b]Note:[/b] If you want to check whether the mouse truly left the area, ignoring any top nodes, you can use code like this: [codeblock] func _on_mouse_exited(): if not Rect2(Vector2(), size).has_point(get_local_mouse_position()): @@ -1140,10 +1140,12 @@ Sent when the node changes size. Use [member size] to get the new size. </constant> <constant name="NOTIFICATION_MOUSE_ENTER" value="41"> - Sent when the mouse pointer enters the node. + Sent when the mouse cursor enters the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not. + [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the notification. </constant> <constant name="NOTIFICATION_MOUSE_EXIT" value="42"> - Sent when the mouse pointer exits the node. + Sent when the mouse cursor leaves the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not. + [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the notification. </constant> <constant name="NOTIFICATION_FOCUS_ENTER" value="43"> Sent when the node grabs focus. diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 81954dd7de..1486990995 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -312,6 +312,13 @@ [b]Note:[/b] Godot's BMP module doesn't support 16-bit per pixel images. Only 1-bit, 4-bit, 8-bit, 24-bit, and 32-bit per pixel images are supported. </description> </method> + <method name="load_dds_from_buffer"> + <return type="int" enum="Error" /> + <param index="0" name="buffer" type="PackedByteArray" /> + <description> + Loads an image from the binary contents of a DDS file. + </description> + </method> <method name="load_from_file" qualifiers="static"> <return type="Image" /> <param index="0" name="path" type="String" /> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index ce02c3e51a..49ab3918bb 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -1052,10 +1052,10 @@ Notification received from the OS when the screen's DPI has been changed. Only implemented on macOS. </constant> <constant name="NOTIFICATION_VP_MOUSE_ENTER" value="1010"> - Notification received when the mouse enters the viewport. + Notification received when the mouse cursor enters the [Viewport]'s visible area, that is not occluded behind other [Control]s or [Window]s, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not. </constant> <constant name="NOTIFICATION_VP_MOUSE_EXIT" value="1011"> - Notification received when the mouse leaves the viewport. + Notification received when the mouse cursor leaves the [Viewport]'s visible area, that is not occluded behind other [Control]s or [Window]s, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not. </constant> <constant name="NOTIFICATION_OS_MEMORY_WARNING" value="2009"> Notification received from the OS when the application is exceeding its allocated memory. diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 3205a33168..e4e194adf0 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -16,8 +16,8 @@ <methods> <method name="barrier"> <return type="void" /> - <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> - <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> + <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Puts a memory barrier in place. This is used for synchronization to avoid data races. See also [method full_barrier], which may be useful for debugging. </description> @@ -27,7 +27,7 @@ <param index="0" name="buffer" type="RID" /> <param index="1" name="offset" type="int" /> <param index="2" name="size_bytes" type="int" /> - <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> </description> </method> @@ -46,7 +46,7 @@ <param index="1" name="offset" type="int" /> <param index="2" name="size_bytes" type="int" /> <param index="3" name="data" type="PackedByteArray" /> - <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> </description> </method> @@ -114,7 +114,7 @@ </method> <method name="compute_list_end"> <return type="void" /> - <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Finishes a list of compute commands created with the [code]compute_*[/code] methods. </description> @@ -296,7 +296,7 @@ </method> <method name="draw_list_end"> <return type="void" /> - <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Finishes a list of raster drawing commands created with the [code]draw_*[/code] methods. </description> @@ -619,6 +619,7 @@ <method name="shader_create_from_bytecode"> <return type="RID" /> <param index="0" name="binary_data" type="PackedByteArray" /> + <param index="1" name="placeholder_rid" type="RID" default="RID()" /> <description> Creates a new shader instance from a binary compiled shader. It can be accessed with the RID that is returned. Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. See also [method shader_compile_binary_from_spirv] and [method shader_create_from_spirv]. @@ -633,6 +634,12 @@ Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. See also [method shader_compile_spirv_from_source] and [method shader_create_from_bytecode]. </description> </method> + <method name="shader_create_placeholder"> + <return type="RID" /> + <description> + Create a placeholder RID by allocating an RID without initializing it for use in [method shader_create_from_bytecode]. This allows you to create an RID for a shader and pass it around, but defer compiling the shader to a later time. + </description> + </method> <method name="shader_get_vertex_input_attribute_mask"> <return type="int" /> <param index="0" name="shader" type="RID" /> @@ -682,7 +689,7 @@ <param index="3" name="mipmap_count" type="int" /> <param index="4" name="base_layer" type="int" /> <param index="5" name="layer_count" type="int" /> - <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Clears the specified [param texture] by replacing all of its pixels with the specified [param color]. [param base_mipmap] and [param mipmap_count] determine which mipmaps of the texture are affected by this clear operation, while [param base_layer] and [param layer_count] determine which layers of a 3D texture (or texture array) are affected by this clear operation. For 2D textures (which only have one layer by design), [param base_layer] and [param layer_count] must both be [code]0[/code]. [b]Note:[/b] [param texture] can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to clear this texture. @@ -699,7 +706,7 @@ <param index="6" name="dst_mipmap" type="int" /> <param index="7" name="src_layer" type="int" /> <param index="8" name="dst_layer" type="int" /> - <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Copies the [param from_texture] to [param to_texture] with the specified [param from_pos], [param to_pos] and [param size] coordinates. The Z axis of the [param from_pos], [param to_pos] and [param size] must be [code]0[/code] for 2-dimensional textures. Source and destination mipmaps/layers must also be specified, with these parameters being [code]0[/code] for textures without mipmaps or single-layer textures. Returns [constant @GlobalScope.OK] if the texture copy was successful or [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] [param from_texture] texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to copy this texture. @@ -793,7 +800,7 @@ <return type="int" enum="Error" /> <param index="0" name="from_texture" type="RID" /> <param index="1" name="to_texture" type="RID" /> - <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Resolves the [param from_texture] texture onto [param to_texture] with multisample antialiasing enabled. This must be used when rendering a framebuffer for MSAA to work. Returns [constant @GlobalScope.OK] if successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] [param from_texture] and [param to_texture] textures must have the same dimension, format and type (color or depth). @@ -810,7 +817,7 @@ <param index="0" name="texture" type="RID" /> <param index="1" name="layer" type="int" /> <param index="2" name="data" type="PackedByteArray" /> - <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" /> + <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Updates texture data with new data, replacing the previous data in place. The updated texture data must have the same dimensions and format. For 2D textures (which only have one layer), [param layer] must be [code]0[/code]. Returns [constant @GlobalScope.OK] if the update was successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] Updating textures is forbidden during creation of a draw or compute list. @@ -1590,22 +1597,22 @@ <constant name="BARRIER_MASK_VERTEX" value="1" enum="BarrierMask" is_bitfield="true"> Vertex shader barrier mask. </constant> - <constant name="BARRIER_MASK_FRAGMENT" value="2" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_FRAGMENT" value="8" enum="BarrierMask" is_bitfield="true"> Fragment shader barrier mask. </constant> - <constant name="BARRIER_MASK_COMPUTE" value="4" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_COMPUTE" value="2" enum="BarrierMask" is_bitfield="true"> Compute barrier mask. </constant> - <constant name="BARRIER_MASK_TRANSFER" value="8" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_TRANSFER" value="4" enum="BarrierMask" is_bitfield="true"> Transfer barrier mask. </constant> - <constant name="BARRIER_MASK_RASTER" value="3" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_RASTER" value="9" enum="BarrierMask" is_bitfield="true"> Raster barrier mask (vertex and fragment). Equivalent to [code]BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT[/code]. </constant> - <constant name="BARRIER_MASK_ALL_BARRIERS" value="15" enum="BarrierMask" is_bitfield="true"> - Barrier mask for all types (raster, compute, transfer). Equivalent to [code]BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER[/code]. + <constant name="BARRIER_MASK_ALL_BARRIERS" value="32767" enum="BarrierMask" is_bitfield="true"> + Barrier mask for all types (vertex, fragment, compute, transfer). </constant> - <constant name="BARRIER_MASK_NO_BARRIER" value="16" enum="BarrierMask" is_bitfield="true"> + <constant name="BARRIER_MASK_NO_BARRIER" value="32768" enum="BarrierMask" is_bitfield="true"> No barrier for any type. </constant> <constant name="TEXTURE_TYPE_1D" value="0" enum="TextureType"> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index f104c5f107..7645b249e3 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -366,7 +366,7 @@ [/gdscript] [csharp] Tween tween = CreateTween(); - tween.TweenMethod(Callable.From(() => LookAt(Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // The LookAt() method takes up vector as second argument. + tween.TweenMethod(Callable.From((Vector3 target) => LookAt(target, Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // Use lambdas to bind additional arguments for the call. [/csharp] [/codeblocks] [b]Example:[/b] Setting the text of a [Label], using an intermediate method and after a delay: diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 4114a83584..33a64be50c 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -719,12 +719,12 @@ </signal> <signal name="mouse_entered"> <description> - Emitted when the mouse cursor enters the [Window]'s area, regardless if it's currently focused or not. + Emitted when the mouse cursor enters the [Window]'s visible area, that is not occluded behind other [Control]s or windows, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not. </description> </signal> <signal name="mouse_exited"> <description> - Emitted when the mouse cursor exits the [Window]'s area (including when it's hovered over another window on top of this one). + Emitted when the mouse cursor leaves the [Window]'s visible area, that is not occluded behind other [Control]s or windows, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not. </description> </signal> <signal name="theme_changed"> diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 6791a9db5e..1fe33b7914 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2171,7 +2171,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (scene_state.current_depth_draw != shader->depth_draw) { switch (shader->depth_draw) { case GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE: { - glDepthMask(p_pass_mode == PASS_MODE_COLOR); + glDepthMask((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) || + p_pass_mode == PASS_MODE_DEPTH || + p_pass_mode == PASS_MODE_SHADOW); } break; case GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS: { glDepthMask(GL_TRUE); diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index cd18d84837..d521f675fb 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -4858,7 +4858,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve return ret; } -RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary) { +RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder) { const uint8_t *binptr = p_shader_binary.ptr(); uint32_t binsize = p_shader_binary.size(); @@ -5184,14 +5184,23 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ ERR_FAIL_V_MSG(RID(), error_text); } - - RID id = shader_owner.make_rid(shader); + RID id; + if (p_placeholder.is_null()) { + id = shader_owner.make_rid(shader); + } else { + shader_owner.initialize_rid(p_placeholder, shader); + id = p_placeholder; + } #ifdef DEV_ENABLED set_resource_name(id, "RID:" + itos(id.get_id())); #endif return id; } +RID RenderingDeviceVulkan::shader_create_placeholder() { + return shader_owner.allocate_rid(); +} + uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) { _THREAD_SAFE_METHOD_ diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 010f7c9337..fd832312ac 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -1135,7 +1135,8 @@ public: virtual String shader_get_binary_cache_key() const; virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = ""); - virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary); + virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID()); + virtual RID shader_create_placeholder(); virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 3a1330b331..c167caeb7c 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -1195,12 +1195,15 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties)); vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props); for (uint32_t j = 0; j < device_queue_family_count; j++) { - VkBool32 supports; - vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports); - if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) { - present_supported = true; - } else { - continue; + if ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { + VkBool32 supports; + err = vkGetPhysicalDeviceSurfaceSupportKHR( + physical_devices[i], j, p_surface, &supports); + if (err == VK_SUCCESS && supports) { + present_supported = true; + } else { + continue; + } } } String name = props.deviceName; @@ -1804,6 +1807,16 @@ Error VulkanContext::_update_swap_chain(Window *window) { err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, window->surface, &surfCapabilities); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + { + VkBool32 supports = VK_FALSE; + err = vkGetPhysicalDeviceSurfaceSupportKHR( + gpu, present_queue_family_index, window->surface, &supports); + ERR_FAIL_COND_V_MSG(err != VK_SUCCESS || supports == false, ERR_CANT_CREATE, + "Window's surface is not supported by device. Did the GPU go offline? Was the window " + "created on another monitor? Check previous errors & try launching with " + "--gpu-validation."); + } + uint32_t presentModeCount; err = fpGetPhysicalDeviceSurfacePresentModesKHR(gpu, window->surface, &presentModeCount, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 074dd2ffe0..a58e17724b 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -322,7 +322,7 @@ void EditorNode::_update_scene_tabs() { scene_tabs->set_current_tab(editor_data.get_edited_scene()); } - const Size2 add_button_size = Size2(0, scene_tabs->get_size().y); + const Size2 add_button_size = Size2(scene_tab_add->get_size().x, scene_tabs->get_size().y); if (scene_tabs->get_offset_buttons_visible()) { // Move the add button to a fixed position. if (scene_tab_add->get_parent() == scene_tabs) { @@ -345,7 +345,7 @@ void EditorNode::_update_scene_tabs() { Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1); int hsep = scene_tabs->get_theme_constant(SNAME("h_separation")); if (scene_tabs->is_layout_rtl()) { - scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y), add_button_size)); + scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - add_button_size.x - hsep, last_tab.position.y), add_button_size)); } else { scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y), add_button_size)); } @@ -5680,11 +5680,17 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) { _scene_tab_closed(scene_tabs->get_hovered_tab()); } - } else { - if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { + } else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { + int tab_buttons = 0; + if (scene_tabs->get_offset_buttons_visible()) { + tab_buttons = theme->get_icon(SNAME("increment"), SNAME("TabBar"))->get_width() + theme->get_icon(SNAME("decrement"), SNAME("TabBar"))->get_width(); + } + + if ((gui_base->is_layout_rtl() && mb->get_position().x > tab_buttons) || (!gui_base->is_layout_rtl() && mb->get_position().x < scene_tabs->get_size().width - tab_buttons)) { _menu_option_confirm(FILE_NEW_SCENE, true); } } + if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { // Context menu. scene_tabs_context_menu->clear(); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 677b2e78bd..48fc20adf0 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1248,6 +1248,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< col->set_owner(p_node->get_owner()); col->set_transform(get_collision_shapes_transform(node_settings)); col->set_position(p_applied_root_scale * col->get_position()); + const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"]; + if (!pmo.is_null()) { + col->set_physics_material_override(pmo); + } base = col; } break; case MESH_PHYSICS_RIGID_BODY_AND_MESH: { @@ -1260,6 +1264,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< mi->set_transform(Transform3D()); rigid_body->add_child(mi, true); mi->set_owner(rigid_body->get_owner()); + const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"]; + if (!pmo.is_null()) { + rigid_body->set_physics_material_override(pmo); + } base = rigid_body; } break; case MESH_PHYSICS_STATIC_COLLIDER_ONLY: { @@ -1271,6 +1279,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< p_node->set_owner(nullptr); memdelete(p_node); p_node = col; + const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"]; + if (!pmo.is_null()) { + col->set_physics_material_override(pmo); + } base = col; } break; case MESH_PHYSICS_AREA_ONLY: { @@ -1287,6 +1299,9 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< } break; } + base->set_collision_layer(node_settings["physics/layer"]); + base->set_collision_mask(node_settings["physics/mask"]); + for (const Ref<Shape3D> &E : shapes) { CollisionShape3D *cshape = memnew(CollisionShape3D); cshape->set_shape(E); @@ -1605,6 +1620,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "Static,Dynamic,Area"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/shape_type", PROPERTY_HINT_ENUM, "Decompose Convex,Simple Convex,Trimesh,Box,Sphere,Cylinder,Capsule", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "physics/physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), Variant())); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1)); // Decomposition Ref<MeshConvexDecompositionSettings> decomposition_default = Ref<MeshConvexDecompositionSettings>(); @@ -1703,9 +1721,7 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor p_options.has("generate/physics") && p_options["generate/physics"].operator bool(); - if ( - p_option == "physics/body_type" || - p_option == "physics/shape_type") { + if (p_option.find("physics/") >= 0) { // Show if need to generate collisions. return generate_physics; } diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 57139a511f..114ba5653a 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -418,7 +418,7 @@ void ResourceImporterTexture::_save_editor_meta(const Dictionary &p_metadata, co Dictionary ResourceImporterTexture::_load_editor_meta(const String &p_path) const { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), Dictionary(), "Missing required editor-specific import metadata for a texture; please, reimport."); + ERR_FAIL_COND_V_MSG(f.is_null(), Dictionary(), vformat("Missing required editor-specific import metadata for a texture (please reimport it using the 'Import' tab): '%s'", p_path)); return f->get_var(); } diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 31e4692de9..8b58a45ea5 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -156,7 +156,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() { // Common to all type of sources. if (!source->get_name().is_empty()) { - item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id); + item_text = source->get_name(); } // Atlas source. @@ -165,7 +165,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() { texture = atlas_source->get_texture(); if (item_text.is_empty()) { if (texture.is_valid()) { - item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id); + item_text = texture->get_path().get_file(); } else { item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id); } diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 61d57e5eab..c98d9086d1 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2529,6 +2529,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { atlas_source_inspector = memnew(EditorInspector); atlas_source_inspector->set_v_size_flags(SIZE_EXPAND_FILL); atlas_source_inspector->set_show_categories(true); + atlas_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin)); atlas_source_inspector->edit(atlas_source_proxy_object); middle_vbox_container->add_child(atlas_source_inspector); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index eb79394cc9..f620e434ab 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -34,6 +34,7 @@ #include "tiles_editor_plugin.h" #include "editor/editor_file_system.h" +#include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" @@ -42,6 +43,7 @@ #include "scene/gui/box_container.h" #include "scene/gui/control.h" +#include "scene/gui/dialogs.h" #include "scene/gui/tab_container.h" TileSetEditor *TileSetEditor::singleton = nullptr; @@ -158,7 +160,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) { // Common to all type of sources. if (!source->get_name().is_empty()) { - item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id); + item_text = source->get_name(); } // Atlas source. @@ -167,7 +169,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) { texture = atlas_source->get_texture(); if (item_text.is_empty()) { if (texture.is_valid()) { - item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id); + item_text = texture->get_path().get_file(); } else { item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id); } @@ -964,3 +966,54 @@ TileSetEditor::TileSetEditor() { EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element)); EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback)); } + +void TileSourceInspectorPlugin::_show_id_edit_dialog(Object *p_for_source) { + if (!id_edit_dialog) { + id_edit_dialog = memnew(ConfirmationDialog); + TileSetEditor::get_singleton()->add_child(id_edit_dialog); + + VBoxContainer *vbox = memnew(VBoxContainer); + id_edit_dialog->add_child(vbox); + + Label *label = memnew(Label(TTR("Warning: Modifying a source ID will result in all TileMaps using that source to reference an invalid source instead. This may result in unexpected data loss. Change this ID carefully."))); + label->set_autowrap_mode(TextServer::AUTOWRAP_WORD); + vbox->add_child(label); + + id_input = memnew(SpinBox); + vbox->add_child(id_input); + id_input->set_max(INT_MAX); + + id_edit_dialog->connect("confirmed", callable_mp(this, &TileSourceInspectorPlugin::_confirm_change_id)); + } + edited_source = p_for_source; + id_input->set_value(p_for_source->get("id")); + id_edit_dialog->popup_centered(Vector2i(400, 0) * EDSCALE); + callable_mp((Control *)id_input->get_line_edit(), &Control::grab_focus).call_deferred(); +} + +void TileSourceInspectorPlugin::_confirm_change_id() { + edited_source->set("id", id_input->get_value()); + id_label->set_text(vformat(TTR("ID: %d"), edited_source->get("id"))); // Use get(), because the provided ID might've been invalid. +} + +bool TileSourceInspectorPlugin::can_handle(Object *p_object) { + return p_object->is_class("TileSetAtlasSourceProxyObject") || p_object->is_class("TileSetScenesCollectionProxyObject"); +} + +bool TileSourceInspectorPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) { + if (p_path == "id") { + HBoxContainer *hbox = memnew(HBoxContainer); + hbox->set_alignment(BoxContainer::ALIGNMENT_CENTER); + + id_label = memnew(Label(vformat(TTR("ID: %d"), p_object->get("id")))); + hbox->add_child(id_label); + + Button *button = memnew(Button(TTR("Edit"))); + hbox->add_child(button); + button->connect("pressed", callable_mp(this, &TileSourceInspectorPlugin::_show_id_edit_dialog).bind(p_object)); + + add_custom_control(hbox); + return true; + } + return false; +} diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index 40ca1f7ed7..86cd70d19e 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -38,9 +38,12 @@ #include "tile_set_atlas_source_editor.h" #include "tile_set_scenes_collection_source_editor.h" -class EditorFileDialog; +class AcceptDialog; +class SpinBox; class HBoxContainer; class SplitContainer; +class EditorFileDialog; +class EditorInspectorPlugin; class TileSetEditor : public Control { GDCLASS(TileSetEditor, Control); @@ -123,4 +126,20 @@ public: TileSetEditor(); }; +class TileSourceInspectorPlugin : public EditorInspectorPlugin { + GDCLASS(TileSourceInspectorPlugin, EditorInspectorPlugin); + + AcceptDialog *id_edit_dialog = nullptr; + Label *id_label = nullptr; + SpinBox *id_input = nullptr; + Object *edited_source = nullptr; + + void _show_id_edit_dialog(Object *p_for_source); + void _confirm_change_id(); + +public: + virtual bool can_handle(Object *p_object) override; + virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override; +}; + #endif // TILE_SET_EDITOR_H diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 5ffa7b4bd4..13270f3821 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -36,6 +36,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" +#include "editor/plugins/tiles/tile_set_editor.h" #include "scene/gui/button.h" #include "scene/gui/item_list.h" @@ -504,6 +505,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { scenes_collection_source_inspector = memnew(EditorInspector); scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); + scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin)); scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object); middle_vbox_container->add_child(scenes_collection_source_inspector); diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp index 9428d29088..ec044ee06e 100644 --- a/editor/pot_generator.cpp +++ b/editor/pot_generator.cpp @@ -99,25 +99,27 @@ void POTGenerator::_write_to_pot(const String &p_file) { return; } - String project_name = GLOBAL_GET("application/config/name"); + String project_name = GLOBAL_GET("application/config/name").operator String().replace("\n", "\\n"); Vector<String> files = GLOBAL_GET("internationalization/locale/translations_pot_files"); String extracted_files = ""; for (int i = 0; i < files.size(); i++) { - extracted_files += "# " + files[i] + "\n"; + extracted_files += "# " + files[i].replace("\n", "\\n") + "\n"; } const String header = - "# LANGUAGE translation for " + project_name + " for the following files:\n" + extracted_files + + "# LANGUAGE translation for " + project_name + " for the following files:\n" + + extracted_files + "#\n" - "# FIRST AUTHOR < EMAIL @ADDRESS>, YEAR.\n" + "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n" "#\n" "#, fuzzy\n" "msgid \"\"\n" "msgstr \"\"\n" "\"Project-Id-Version: " + - project_name + "\\n\"\n" - "\"MIME-Version: 1.0\\n\"\n" - "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" - "\"Content-Transfer-Encoding: 8-bit\\n\"\n"; + project_name + + "\\n\"\n" + "\"MIME-Version: 1.0\\n\"\n" + "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" + "\"Content-Transfer-Encoding: 8-bit\\n\"\n"; file->store_string(header); @@ -134,12 +136,12 @@ void POTGenerator::_write_to_pot(const String &p_file) { // Write file locations. for (const String &E : locations) { - file->store_line("#: " + E.trim_prefix("res://")); + file->store_line("#: " + E.trim_prefix("res://").replace("\n", "\\n")); } // Write context. if (!context.is_empty()) { - file->store_line("msgctxt \"" + context + "\""); + file->store_line("msgctxt " + context.c_escape().quote()); } // Write msgid. @@ -158,30 +160,34 @@ void POTGenerator::_write_to_pot(const String &p_file) { } void POTGenerator::_write_msgid(Ref<FileAccess> r_file, const String &p_id, bool p_plural) { - // Split \\n and \n. - Vector<String> msg_lines; - Vector<String> temp = p_id.split("\\n"); - for (int i = 0; i < temp.size(); i++) { - msg_lines.append_array(temp[i].split("\n")); - } - - // Add \n. - for (int i = 0; i < msg_lines.size() - 1; i++) { - msg_lines.set(i, msg_lines[i] + "\\n"); - } - if (p_plural) { r_file->store_string("msgid_plural "); } else { r_file->store_string("msgid "); } - if (msg_lines.size() > 1) { + if (p_id.is_empty()) { + r_file->store_line("\"\""); + return; + } + + const Vector<String> lines = p_id.split("\n"); + const String &last_line = lines[lines.size() - 1]; // `lines` cannot be empty. + int pot_line_count = lines.size(); + if (last_line.is_empty()) { + pot_line_count--; + } + + if (pot_line_count > 1) { r_file->store_line("\"\""); } - for (int i = 0; i < msg_lines.size(); i++) { - r_file->store_line("\"" + msg_lines[i] + "\""); + for (int i = 0; i < lines.size() - 1; i++) { + r_file->store_line((lines[i] + "\n").c_escape().quote()); + } + + if (!last_line.is_empty()) { + r_file->store_line(last_line.c_escape().quote()); } } diff --git a/misc/extension_api_validation/4.0-stable.expected b/misc/extension_api_validation/4.0-stable.expected index c9ebd83ba3..380c1d5193 100644 --- a/misc/extension_api_validation/4.0-stable.expected +++ b/misc/extension_api_validation/4.0-stable.expected @@ -358,3 +358,24 @@ Validate extension JSON: Error: Hash changed for 'classes/EditorUndoRedoManager/ Validate extension JSON: Error: Hash changed for 'classes/UndoRedo/methods/create_action', from 0AEC1BFC to E87757EB. This means that the function has changed and no compatibility function was provided. Added a optional parameters with default values. No adjustments should be necessary. + +GH-79911 +-------- +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_RASTER': value changed value in new API, from 1.0 to 9. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_ALL_BARRIERS': value changed value in new API, from 7.0 to 32767. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_NO_BARRIER': value changed value in new API, from 8.0 to 32768. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_update/arguments/3': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_copy/arguments/9': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_clear/arguments/6': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_resolve_multisample/arguments/2': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/buffer_update/arguments/4': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/buffer_clear/arguments/3': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_end/arguments/0': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/draw_list_end', from 19365687 to E9B4FA8E. This means that the function has changed and no compatibility function was provided. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/compute_list_end/arguments/0': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/compute_list_end', from 19365687 to E9B4FA8E. This means that the function has changed and no compatibility function was provided. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/arguments/0': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/arguments/1': default_value changed value in new API, from "7" to "32767". +Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/barrier', from 0FE50041 to DD9E8DAB. This means that the function has changed and no compatibility function was provided. + +Raster barrier was split into vertex and fragment barriers for use in mobile renderer. diff --git a/modules/dds/image_loader_dds.cpp b/modules/dds/image_loader_dds.cpp new file mode 100644 index 0000000000..42c8120595 --- /dev/null +++ b/modules/dds/image_loader_dds.cpp @@ -0,0 +1,422 @@ +/**************************************************************************/ +/* image_loader_dds.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "image_loader_dds.h" + +#include "core/os/os.h" + +#include "core/io/file_access.h" +#include "core/io/file_access_memory.h" + +#include <string.h> + +#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0]))) + +// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header + +enum { + DDS_MAGIC = 0x20534444, + DDSD_PITCH = 0x00000008, + DDSD_LINEARSIZE = 0x00080000, + DDSD_MIPMAPCOUNT = 0x00020000, + DDPF_FOURCC = 0x00000004, + DDPF_ALPHAPIXELS = 0x00000001, + DDPF_INDEXED = 0x00000020, + DDPF_RGB = 0x00000040, +}; + +enum DDSFormat { + DDS_DXT1, + DDS_DXT3, + DDS_DXT5, + DDS_ATI1, + DDS_ATI2, + DDS_A2XY, + DDS_BGRA8, + DDS_BGR8, + DDS_RGBA8, //flipped in dds + DDS_RGB8, //flipped in dds + DDS_BGR5A1, + DDS_BGR565, + DDS_BGR10A2, + DDS_INDEXED, + DDS_LUMINANCE, + DDS_LUMINANCE_ALPHA, + DDS_MAX +}; + +struct DDSFormatInfo { + const char *name = nullptr; + bool compressed = false; + bool palette = false; + uint32_t divisor = 0; + uint32_t block_size = 0; + Image::Format format = Image::Format::FORMAT_BPTC_RGBA; +}; + +static const DDSFormatInfo dds_format_info[DDS_MAX] = { + { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 }, + { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 }, + { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 }, + { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R }, + { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, + { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, + { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, + { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 }, + { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, + { "RGB8", false, false, 1, 3, Image::FORMAT_RGB8 }, + { "BGR5A1", false, false, 1, 2, Image::FORMAT_RGBA8 }, + { "BGR565", false, false, 1, 2, Image::FORMAT_RGB8 }, + { "BGR10A2", false, false, 1, 4, Image::FORMAT_RGBA8 }, + { "GRAYSCALE", false, false, 1, 1, Image::FORMAT_L8 }, + { "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 } +}; + +static Ref<Image> _dds_mem_loader_func(const uint8_t *p_buffer, int p_buffer_len) { + Ref<FileAccessMemory> memfile; + memfile.instantiate(); + Error open_memfile_error = memfile->open_custom(p_buffer, p_buffer_len); + ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for DDS image buffer."); + + Ref<Image> img; + img.instantiate(); + Error load_error = ImageLoaderDDS().load_image(img, memfile, false, 1.0f); + ERR_FAIL_COND_V_MSG(load_error, Ref<Image>(), "Failed to load DDS image."); + return img; +} + +Error ImageLoaderDDS::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) { + uint32_t magic = f->get_32(); + uint32_t hsize = f->get_32(); + uint32_t flags = f->get_32(); + uint32_t height = f->get_32(); + uint32_t width = f->get_32(); + uint32_t pitch = f->get_32(); + /* uint32_t depth = */ f->get_32(); + uint32_t mipmaps = f->get_32(); + + //skip 11 + for (int i = 0; i < 11; i++) { + f->get_32(); + } + + //validate + + // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing, + // but non-mandatory when reading (as some writers don't set them)... + if (magic != DDS_MAGIC || hsize != 124) { + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid or unsupported DDS texture file '" + f->get_path() + "'."); + } + + /* uint32_t format_size = */ f->get_32(); + uint32_t format_flags = f->get_32(); + uint32_t format_fourcc = f->get_32(); + uint32_t format_rgb_bits = f->get_32(); + uint32_t format_red_mask = f->get_32(); + uint32_t format_green_mask = f->get_32(); + uint32_t format_blue_mask = f->get_32(); + uint32_t format_alpha_mask = f->get_32(); + + /* uint32_t caps_1 = */ f->get_32(); + /* uint32_t caps_2 = */ f->get_32(); + /* uint32_t caps_ddsx = */ f->get_32(); + + //reserved skip + f->get_32(); + f->get_32(); + + /* + print_line("DDS width: "+itos(width)); + print_line("DDS height: "+itos(height)); + print_line("DDS mipmaps: "+itos(mipmaps)); + + printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size); + printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask); + */ + + //must avoid this later + while (f->get_position() < 128) { + f->get_8(); + } + + DDSFormat dds_format; + + if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) { + dds_format = DDS_DXT1; + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) { + dds_format = DDS_DXT3; + + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) { + dds_format = DDS_DXT5; + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) { + dds_format = DDS_ATI1; + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) { + dds_format = DDS_ATI2; + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) { + dds_format = DDS_A2XY; + + } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) { + dds_format = DDS_BGRA8; + } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) { + dds_format = DDS_BGR8; + } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) { + dds_format = DDS_RGBA8; + } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) { + dds_format = DDS_RGB8; + + } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) { + dds_format = DDS_BGR5A1; + } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) { + dds_format = DDS_BGR10A2; + } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { + dds_format = DDS_BGR565; + } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff) { + dds_format = DDS_LUMINANCE; + } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff && format_alpha_mask == 0xff00) { + dds_format = DDS_LUMINANCE_ALPHA; + } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) { + dds_format = DDS_BGR565; + } else { + //printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Unrecognized or unsupported color layout in DDS '" + f->get_path() + "'."); + } + + if (!(flags & DDSD_MIPMAPCOUNT)) { + mipmaps = 1; + } + + Vector<uint8_t> src_data; + + const DDSFormatInfo &info = dds_format_info[dds_format]; + uint32_t w = width; + uint32_t h = height; + + if (info.compressed) { + //compressed bc + + uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; + ERR_FAIL_COND_V(size != pitch, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), ERR_FILE_CORRUPT); + + for (uint32_t i = 1; i < mipmaps; i++) { + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + uint32_t bsize = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; + //printf("%i x %i - block: %i\n",w,h,bsize); + size += bsize; + } + + src_data.resize(size); + uint8_t *wb = src_data.ptrw(); + f->get_buffer(wb, size); + + } else if (info.palette) { + //indexed + ERR_FAIL_COND_V(!(flags & DDSD_PITCH), ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(format_rgb_bits != 8, ERR_FILE_CORRUPT); + + uint32_t size = pitch * height; + ERR_FAIL_COND_V(size != width * height * info.block_size, ERR_FILE_CORRUPT); + + uint8_t palette[256 * 4]; + f->get_buffer(palette, 256 * 4); + + int colsize = 3; + for (int i = 0; i < 256; i++) { + if (palette[i * 4 + 3] < 255) { + colsize = 4; + } + } + + int w2 = width; + int h2 = height; + + for (uint32_t i = 1; i < mipmaps; i++) { + w2 = (w2 + 1) >> 1; + h2 = (h2 + 1) >> 1; + size += w2 * h2 * info.block_size; + } + + src_data.resize(size + 256 * colsize); + uint8_t *wb = src_data.ptrw(); + f->get_buffer(wb, size); + + for (int i = 0; i < 256; i++) { + int dst_ofs = size + i * colsize; + int src_ofs = i * 4; + wb[dst_ofs + 0] = palette[src_ofs + 2]; + wb[dst_ofs + 1] = palette[src_ofs + 1]; + wb[dst_ofs + 2] = palette[src_ofs + 0]; + if (colsize == 4) { + wb[dst_ofs + 3] = palette[src_ofs + 3]; + } + } + } else { + //uncompressed generic... + + uint32_t size = width * height * info.block_size; + + for (uint32_t i = 1; i < mipmaps; i++) { + w = (w + 1) >> 1; + h = (h + 1) >> 1; + size += w * h * info.block_size; + } + + if (dds_format == DDS_BGR565) { + size = size * 3 / 2; + } else if (dds_format == DDS_BGR5A1) { + size = size * 2; + } + + src_data.resize(size); + uint8_t *wb = src_data.ptrw(); + f->get_buffer(wb, size); + + switch (dds_format) { + case DDS_BGR5A1: { + // TO RGBA + int colcount = size / 4; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i * 2; + int dst_ofs = i * 4; + + uint8_t a = wb[src_ofs + 1] & 0x80; + uint8_t b = wb[src_ofs] & 0x1F; + uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x3) << 3); + uint8_t r = (wb[src_ofs + 1] >> 2) & 0x1F; + wb[dst_ofs + 0] = r << 3; + wb[dst_ofs + 1] = g << 3; + wb[dst_ofs + 2] = b << 3; + wb[dst_ofs + 3] = a ? 255 : 0; + } + } break; + case DDS_BGR565: { + int colcount = size / 3; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i * 2; + int dst_ofs = i * 3; + + uint8_t b = wb[src_ofs] & 0x1F; + uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x7) << 3); + uint8_t r = wb[src_ofs + 1] >> 3; + wb[dst_ofs + 0] = r << 3; + wb[dst_ofs + 1] = g << 2; + wb[dst_ofs + 2] = b << 3; //b<<3; + } + + } break; + case DDS_BGR10A2: { + // TO RGBA + int colcount = size / 4; + + for (int i = colcount - 1; i >= 0; i--) { + int ofs = i * 4; + + uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24); + + uint8_t a = (w32 & 0xc0000000) >> 24; + uint8_t r = (w32 & 0x3ff00000) >> 22; + uint8_t g = (w32 & 0xffc00) >> 12; + uint8_t b = (w32 & 0x3ff) >> 2; + + wb[ofs + 0] = r; + wb[ofs + 1] = g; + wb[ofs + 2] = b; + wb[ofs + 3] = a == 0xc0 ? 255 : a; //0xc0 should be opaque + } + } break; + case DDS_BGRA8: { + int colcount = size / 4; + + for (int i = 0; i < colcount; i++) { + SWAP(wb[i * 4 + 0], wb[i * 4 + 2]); + } + + } break; + case DDS_BGR8: { + int colcount = size / 3; + + for (int i = 0; i < colcount; i++) { + SWAP(wb[i * 3 + 0], wb[i * 3 + 2]); + } + } break; + case DDS_RGBA8: { + /* do nothing either + int colcount = size/4; + + for(int i=0;i<colcount;i++) { + uint8_t r = wb[i*4+1]; + uint8_t g = wb[i*4+2]; + uint8_t b = wb[i*4+3]; + uint8_t a = wb[i*4+0]; + + wb[i*4+0]=r; + wb[i*4+1]=g; + wb[i*4+2]=b; + wb[i*4+3]=a; + } + */ + } break; + case DDS_RGB8: { + // do nothing + /* + int colcount = size/3; + + for(int i=0;i<colcount;i++) { + SWAP( wb[i*3+0],wb[i*3+2] ); + }*/ + } break; + case DDS_LUMINANCE: { + // do nothing i guess? + + } break; + case DDS_LUMINANCE_ALPHA: { + // do nothing i guess? + + } break; + + default: { + } + } + } + + p_image->set_data(width, height, mipmaps - 1, info.format, src_data); + return OK; +} + +ImageLoaderDDS::ImageLoaderDDS() { + Image::_dds_mem_loader_func = _dds_mem_loader_func; +} + +void ImageLoaderDDS::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("dds"); +} diff --git a/modules/dds/image_loader_dds.h b/modules/dds/image_loader_dds.h new file mode 100644 index 0000000000..81cfd43551 --- /dev/null +++ b/modules/dds/image_loader_dds.h @@ -0,0 +1,43 @@ +/**************************************************************************/ +/* image_loader_dds.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 IMAGE_LOADER_DDS_H +#define IMAGE_LOADER_DDS_H + +#include "core/io/image_loader.h" + +class ImageLoaderDDS : public ImageFormatLoader { +public: + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + ImageLoaderDDS(); +}; + +#endif // IMAGE_LOADER_DDS_H diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp index d336269eb3..b4d406eda9 100644 --- a/modules/dds/register_types.cpp +++ b/modules/dds/register_types.cpp @@ -30,9 +30,11 @@ #include "register_types.h" +#include "image_loader_dds.h" #include "texture_loader_dds.h" static Ref<ResourceFormatDDS> resource_loader_dds; +static Ref<ImageLoaderDDS> image_loader_dds; void initialize_dds_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { @@ -41,6 +43,9 @@ void initialize_dds_module(ModuleInitializationLevel p_level) { resource_loader_dds.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_dds); + + image_loader_dds.instantiate(); + ImageLoader::add_image_format_loader(image_loader_dds); } void uninitialize_dds_module(ModuleInitializationLevel p_level) { @@ -50,4 +55,7 @@ void uninitialize_dds_module(ModuleInitializationLevel p_level) { ResourceLoader::remove_resource_format_loader(resource_loader_dds); resource_loader_dds.unref(); + + ImageLoader::remove_image_format_loader(image_loader_dds); + image_loader_dds.unref(); } diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 8a3a36e84b..861cf20cc2 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -29,72 +29,11 @@ /**************************************************************************/ #include "texture_loader_dds.h" +#include "image_loader_dds.h" #include "core/io/file_access.h" #include "scene/resources/image_texture.h" -#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0]))) - -// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header - -enum { - DDS_MAGIC = 0x20534444, - DDSD_PITCH = 0x00000008, - DDSD_LINEARSIZE = 0x00080000, - DDSD_MIPMAPCOUNT = 0x00020000, - DDPF_FOURCC = 0x00000004, - DDPF_ALPHAPIXELS = 0x00000001, - DDPF_INDEXED = 0x00000020, - DDPF_RGB = 0x00000040, -}; - -enum DDSFormat { - DDS_DXT1, - DDS_DXT3, - DDS_DXT5, - DDS_ATI1, - DDS_ATI2, - DDS_A2XY, - DDS_BGRA8, - DDS_BGR8, - DDS_RGBA8, //flipped in dds - DDS_RGB8, //flipped in dds - DDS_BGR5A1, - DDS_BGR565, - DDS_BGR10A2, - DDS_INDEXED, - DDS_LUMINANCE, - DDS_LUMINANCE_ALPHA, - DDS_MAX -}; - -struct DDSFormatInfo { - const char *name = nullptr; - bool compressed = false; - bool palette = false; - uint32_t divisor = 0; - uint32_t block_size = 0; - Image::Format format = Image::Format::FORMAT_BPTC_RGBA; -}; - -static const DDSFormatInfo dds_format_info[DDS_MAX] = { - { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 }, - { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 }, - { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 }, - { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R }, - { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, - { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, - { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 }, - { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "RGB8", false, false, 1, 3, Image::FORMAT_RGB8 }, - { "BGR5A1", false, false, 1, 2, Image::FORMAT_RGBA8 }, - { "BGR565", false, false, 1, 2, Image::FORMAT_RGB8 }, - { "BGR10A2", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "GRAYSCALE", false, false, 1, 1, Image::FORMAT_L8 }, - { "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 } -}; - Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { if (r_error) { *r_error = ERR_CANT_OPEN; @@ -113,303 +52,12 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Unable to open DDS texture file '" + p_path + "'."); - uint32_t magic = f->get_32(); - uint32_t hsize = f->get_32(); - uint32_t flags = f->get_32(); - uint32_t height = f->get_32(); - uint32_t width = f->get_32(); - uint32_t pitch = f->get_32(); - /* uint32_t depth = */ f->get_32(); - uint32_t mipmaps = f->get_32(); - - //skip 11 - for (int i = 0; i < 11; i++) { - f->get_32(); - } - - //validate - - // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing, - // but non-mandatory when reading (as some writers don't set them)... - if (magic != DDS_MAGIC || hsize != 124) { - ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid or unsupported DDS texture file '" + p_path + "'."); - } - - /* uint32_t format_size = */ f->get_32(); - uint32_t format_flags = f->get_32(); - uint32_t format_fourcc = f->get_32(); - uint32_t format_rgb_bits = f->get_32(); - uint32_t format_red_mask = f->get_32(); - uint32_t format_green_mask = f->get_32(); - uint32_t format_blue_mask = f->get_32(); - uint32_t format_alpha_mask = f->get_32(); - - /* uint32_t caps_1 = */ f->get_32(); - /* uint32_t caps_2 = */ f->get_32(); - /* uint32_t caps_ddsx = */ f->get_32(); - - //reserved skip - f->get_32(); - f->get_32(); - - /* - print_line("DDS width: "+itos(width)); - print_line("DDS height: "+itos(height)); - print_line("DDS mipmaps: "+itos(mipmaps)); - - printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size); - printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask); - */ - - //must avoid this later - while (f->get_position() < 128) { - f->get_8(); - } - - DDSFormat dds_format; - - if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) { - dds_format = DDS_DXT1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) { - dds_format = DDS_DXT3; - - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) { - dds_format = DDS_DXT5; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) { - dds_format = DDS_ATI1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) { - dds_format = DDS_ATI2; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) { - dds_format = DDS_A2XY; - - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) { - dds_format = DDS_BGRA8; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) { - dds_format = DDS_BGR8; - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) { - dds_format = DDS_RGBA8; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) { - dds_format = DDS_RGB8; - - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) { - dds_format = DDS_BGR5A1; - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) { - dds_format = DDS_BGR10A2; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { - dds_format = DDS_BGR565; - } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff) { - dds_format = DDS_LUMINANCE; - } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff && format_alpha_mask == 0xff00) { - dds_format = DDS_LUMINANCE_ALPHA; - } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) { - dds_format = DDS_BGR565; - } else { - //printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask); - ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'."); - } - - if (!(flags & DDSD_MIPMAPCOUNT)) { - mipmaps = 1; - } - - Vector<uint8_t> src_data; - - const DDSFormatInfo &info = dds_format_info[dds_format]; - uint32_t w = width; - uint32_t h = height; - - if (info.compressed) { - //compressed bc - - uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; - ERR_FAIL_COND_V(size != pitch, Ref<Resource>()); - ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), Ref<Resource>()); - - for (uint32_t i = 1; i < mipmaps; i++) { - w = MAX(1u, w >> 1); - h = MAX(1u, h >> 1); - uint32_t bsize = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; - //printf("%i x %i - block: %i\n",w,h,bsize); - size += bsize; - } - - src_data.resize(size); - uint8_t *wb = src_data.ptrw(); - f->get_buffer(wb, size); - - } else if (info.palette) { - //indexed - ERR_FAIL_COND_V(!(flags & DDSD_PITCH), Ref<Resource>()); - ERR_FAIL_COND_V(format_rgb_bits != 8, Ref<Resource>()); - - uint32_t size = pitch * height; - ERR_FAIL_COND_V(size != width * height * info.block_size, Ref<Resource>()); - - uint8_t palette[256 * 4]; - f->get_buffer(palette, 256 * 4); - - int colsize = 3; - for (int i = 0; i < 256; i++) { - if (palette[i * 4 + 3] < 255) { - colsize = 4; - } - } - - int w2 = width; - int h2 = height; - - for (uint32_t i = 1; i < mipmaps; i++) { - w2 = (w2 + 1) >> 1; - h2 = (h2 + 1) >> 1; - size += w2 * h2 * info.block_size; - } - - src_data.resize(size + 256 * colsize); - uint8_t *wb = src_data.ptrw(); - f->get_buffer(wb, size); - - for (int i = 0; i < 256; i++) { - int dst_ofs = size + i * colsize; - int src_ofs = i * 4; - wb[dst_ofs + 0] = palette[src_ofs + 2]; - wb[dst_ofs + 1] = palette[src_ofs + 1]; - wb[dst_ofs + 2] = palette[src_ofs + 0]; - if (colsize == 4) { - wb[dst_ofs + 3] = palette[src_ofs + 3]; - } - } - } else { - //uncompressed generic... - - uint32_t size = width * height * info.block_size; - - for (uint32_t i = 1; i < mipmaps; i++) { - w = (w + 1) >> 1; - h = (h + 1) >> 1; - size += w * h * info.block_size; - } - - if (dds_format == DDS_BGR565) { - size = size * 3 / 2; - } else if (dds_format == DDS_BGR5A1) { - size = size * 2; - } - - src_data.resize(size); - uint8_t *wb = src_data.ptrw(); - f->get_buffer(wb, size); - - switch (dds_format) { - case DDS_BGR5A1: { - // TO RGBA - int colcount = size / 4; - - for (int i = colcount - 1; i >= 0; i--) { - int src_ofs = i * 2; - int dst_ofs = i * 4; - - uint8_t a = wb[src_ofs + 1] & 0x80; - uint8_t b = wb[src_ofs] & 0x1F; - uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x3) << 3); - uint8_t r = (wb[src_ofs + 1] >> 2) & 0x1F; - wb[dst_ofs + 0] = r << 3; - wb[dst_ofs + 1] = g << 3; - wb[dst_ofs + 2] = b << 3; - wb[dst_ofs + 3] = a ? 255 : 0; - } - } break; - case DDS_BGR565: { - int colcount = size / 3; - - for (int i = colcount - 1; i >= 0; i--) { - int src_ofs = i * 2; - int dst_ofs = i * 3; - - uint8_t b = wb[src_ofs] & 0x1F; - uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x7) << 3); - uint8_t r = wb[src_ofs + 1] >> 3; - wb[dst_ofs + 0] = r << 3; - wb[dst_ofs + 1] = g << 2; - wb[dst_ofs + 2] = b << 3; //b<<3; - } - - } break; - case DDS_BGR10A2: { - // TO RGBA - int colcount = size / 4; - - for (int i = colcount - 1; i >= 0; i--) { - int ofs = i * 4; - - uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24); - - uint8_t a = (w32 & 0xc0000000) >> 24; - uint8_t r = (w32 & 0x3ff00000) >> 22; - uint8_t g = (w32 & 0xffc00) >> 12; - uint8_t b = (w32 & 0x3ff) >> 2; - - wb[ofs + 0] = r; - wb[ofs + 1] = g; - wb[ofs + 2] = b; - wb[ofs + 3] = a == 0xc0 ? 255 : a; //0xc0 should be opaque - } - } break; - case DDS_BGRA8: { - int colcount = size / 4; - - for (int i = 0; i < colcount; i++) { - SWAP(wb[i * 4 + 0], wb[i * 4 + 2]); - } - - } break; - case DDS_BGR8: { - int colcount = size / 3; - - for (int i = 0; i < colcount; i++) { - SWAP(wb[i * 3 + 0], wb[i * 3 + 2]); - } - } break; - case DDS_RGBA8: { - /* do nothing either - int colcount = size/4; - - for(int i=0;i<colcount;i++) { - uint8_t r = wb[i*4+1]; - uint8_t g = wb[i*4+2]; - uint8_t b = wb[i*4+3]; - uint8_t a = wb[i*4+0]; - - wb[i*4+0]=r; - wb[i*4+1]=g; - wb[i*4+2]=b; - wb[i*4+3]=a; - } - */ - } break; - case DDS_RGB8: { - // do nothing - /* - int colcount = size/3; - - for(int i=0;i<colcount;i++) { - SWAP( wb[i*3+0],wb[i*3+2] ); - }*/ - } break; - case DDS_LUMINANCE: { - // do nothing i guess? - - } break; - case DDS_LUMINANCE_ALPHA: { - // do nothing i guess? - - } break; - - default: { - } - } + Ref<Image> img = memnew(Image); + Error i_error = ImageLoaderDDS().load_image(img, f, false, 1.0); + if (r_error) { + *r_error = i_error; } - Ref<Image> img = memnew(Image(width, height, mipmaps - 1, info.format, src_data)); Ref<ImageTexture> texture = ImageTexture::create_from_image(img); if (r_error) { diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 0c300eade4..9cc1fc0c31 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -533,6 +533,7 @@ [codeblock] @export_node_path("Button", "TouchScreenButton") var some_button [/codeblock] + [b]Note:[/b] The type must be a native class or a globally registered script (using the [code]class_name[/code] keyword) that inherits [Node]. </description> </annotation> <annotation name="@export_placeholder"> diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 47cd3f768b..6057a00f9b 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -226,7 +226,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { if (opcodes.size()) { function->code = opcodes; - function->_code_ptr = &function->code[0]; + function->_code_ptr = &function->code.write[0]; function->_code_size = opcodes.size(); } else { @@ -577,6 +577,12 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va append(Address()); append(p_target); append(p_operator); + append(0); // Signature storage. + append(0); // Return type storage. + constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*(opcodes.ptr())); + for (int i = 0; i < _pointer_size; i++) { + append(0); // Space for function pointer. + } } void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) { @@ -610,6 +616,12 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V append(p_right_operand); append(p_target); append(p_operator); + append(0); // Signature storage. + append(0); // Return type storage. + constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*(opcodes.ptr())); + for (int i = 0; i < _pointer_size; i++) { + append(0); // Space for function pointer. + } } void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) { diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index ec1d0af329..438ec02740 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -113,6 +113,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { switch (opcode) { case OPCODE_OPERATOR: { + constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr); int operation = _code_ptr[ip + 4]; text += "operator "; @@ -125,7 +126,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { text += " "; text += DADDR(2); - incr += 5; + incr += 7 + _pointer_size; } break; case OPCODE_OPERATOR_VALIDATED: { text += "validated operator "; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 8ad2486e2b..d27ea974e3 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -818,9 +818,10 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS); node.insert_text = node.display.quote(p_quote_style); r_result.insert(node.display, node); - List<StringName> node_types; - ClassDB::get_inheriters_from_class("Node", &node_types); - for (const StringName &E : node_types) { + + List<StringName> native_classes; + ClassDB::get_inheriters_from_class("Node", &native_classes); + for (const StringName &E : native_classes) { if (!ClassDB::is_class_exposed(E)) { continue; } @@ -828,6 +829,17 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a option.insert_text = option.display.quote(p_quote_style); r_result.insert(option.display, option); } + + List<StringName> global_script_classes; + ScriptServer::get_global_class_list(&global_script_classes); + for (const StringName &E : global_script_classes) { + if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(E), "Node")) { + continue; + } + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + option.insert_text = option.display.quote(p_quote_style); + r_result.insert(option.display, option); + } } else if (p_annotation->name == SNAME("@warning_ignore")) { for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) { ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index dfe66e6688..5230773c13 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -473,7 +473,7 @@ private: MethodBind **_methods_ptr = nullptr; int _lambdas_count = 0; GDScriptFunction **_lambdas_ptr = nullptr; - const int *_code_ptr = nullptr; + int *_code_ptr = nullptr; int _code_size = 0; int _argument_count = 0; int _stack_size = 0; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 0e602561c6..debc85ebbf 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3897,6 +3897,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node } } + // WARNING: Do not merge with the previous `if` because there `!=`, not `==`! if (p_annotation->name == SNAME("@export_flags")) { const int64_t max_flags = 32; Vector<String> t = arg_string.split(":", true, 1); @@ -3922,6 +3923,18 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Starting from argument %d, the flag value must be specified explicitly.)", i + 1, max_flags + 1), p_annotation->arguments[i]); return false; } + } else if (p_annotation->name == SNAME("@export_node_path")) { + String native_class = arg_string; + if (ScriptServer::is_global_class(arg_string)) { + native_class = ScriptServer::get_global_class_native_base(arg_string); + } + if (!ClassDB::class_exists(native_class)) { + push_error(vformat(R"(Invalid argument %d of annotation "@export_node_path": The class "%s" was not found in the global scope.)", i + 1, arg_string), p_annotation->arguments[i]); + return false; + } else if (!ClassDB::is_parent_class(native_class, SNAME("Node"))) { + push_error(vformat(R"(Invalid argument %d of annotation "@export_node_path": The class "%s" does not inherit "Node".)", i + 1, arg_string), p_annotation->arguments[i]); + return false; + } } if (i > 0) { @@ -3939,8 +3952,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node if (export_type.builtin_type == Variant::INT) { variable->export_info.type = Variant::INT; } - } - if (p_annotation->name == SNAME("@export_multiline")) { + } 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 (inner_type.builtin_type != Variant::STRING) { @@ -3968,6 +3980,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node } } + // WARNING: Do not merge with the previous `else if`! Otherwise `else` (default variable type check) + // will not work for the above annotations. `@export` and `@export_enum` validate the type separately. if (p_annotation->name == SNAME("@export")) { if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) { push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation); diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 44c4cb0fc3..1ddd54b323 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -685,7 +685,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_SWITCH(_code_ptr[ip]) { OPCODE(OPCODE_OPERATOR) { - CHECK_SPACE(5); + constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr); + CHECK_SPACE(7 + _pointer_size); bool valid; Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4]; @@ -694,28 +695,71 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(a, 0); GET_VARIANT_PTR(b, 1); GET_VARIANT_PTR(dst, 2); + // Compute signatures (types of operands) so it can be optimized when matching. + uint32_t op_signature = _code_ptr[ip + 5]; + uint32_t actual_signature = (a->get_type() << 8) | (b->get_type()); + + // Check if this is the first run. If so, store the current signature for the optimized path. + if (unlikely(op_signature == 0)) { + static Mutex initializer_mutex; + initializer_mutex.lock(); + Variant::Type a_type = (Variant::Type)((actual_signature >> 8) & 0xFF); + Variant::Type b_type = (Variant::Type)(actual_signature & 0xFF); + Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(op, a_type, b_type); + + if (unlikely(!op_func)) { +#ifdef DEBUG_ENABLED + err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; +#endif + initializer_mutex.unlock(); + OPCODE_BREAK; + } else { + Variant::Type ret_type = Variant::get_operator_return_type(op, a_type, b_type); + VariantInternal::initialize(dst, ret_type); + op_func(a, b, dst); + + // Check again in case another thread already set it. + if (_code_ptr[ip + 5] == 0) { + _code_ptr[ip + 5] = actual_signature; + _code_ptr[ip + 6] = static_cast<int>(ret_type); + Variant::ValidatedOperatorEvaluator *tmp = reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]); + *tmp = op_func; + } + } + initializer_mutex.unlock(); + } else if (likely(op_signature == actual_signature)) { + // If the signature matches, we can use the optimized path. + Variant::Type ret_type = static_cast<Variant::Type>(_code_ptr[ip + 6]); + Variant::ValidatedOperatorEvaluator op_func = *reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]); + + // Make sure the return value has the correct type. + VariantInternal::initialize(dst, ret_type); + op_func(a, b, dst); + } else { + // If the signature doesn't match, we have to use the slow path. #ifdef DEBUG_ENABLED - Variant ret; - Variant::evaluate(op, *a, *b, ret, valid); + Variant ret; + Variant::evaluate(op, *a, *b, ret, valid); #else - Variant::evaluate(op, *a, *b, *dst, valid); + Variant::evaluate(op, *a, *b, *dst, valid); #endif #ifdef DEBUG_ENABLED - if (!valid) { - if (ret.get_type() == Variant::STRING) { - //return a string when invalid with the error - err_text = ret; - err_text += " in operator '" + Variant::get_operator_name(op) + "'."; - } else { - err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; + if (!valid) { + if (ret.get_type() == Variant::STRING) { + //return a string when invalid with the error + err_text = ret; + err_text += " in operator '" + Variant::get_operator_name(op) + "'."; + } else { + err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; + } + OPCODE_BREAK; } - OPCODE_BREAK; - } - *dst = ret; + *dst = ret; #endif - ip += 5; + } + ip += 7 + _pointer_size; } DISPATCH_OPCODE; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs index 5bce66ea87..44b1c2554c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs @@ -10,301 +10,301 @@ namespace Godot { // Color names and values are derived from core/math/color_names.inc internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> { - { "ALICEBLUE", new Color(0xF0F8FFFF) }, - { "ANTIQUEWHITE", new Color(0xFAEBD7FF) }, - { "AQUA", new Color(0x00FFFFFF) }, - { "AQUAMARINE", new Color(0x7FFFD4FF) }, - { "AZURE", new Color(0xF0FFFFFF) }, - { "BEIGE", new Color(0xF5F5DCFF) }, - { "BISQUE", new Color(0xFFE4C4FF) }, - { "BLACK", new Color(0x000000FF) }, - { "BLANCHEDALMOND", new Color(0xFFEBCDFF) }, - { "BLUE", new Color(0x0000FFFF) }, - { "BLUEVIOLET", new Color(0x8A2BE2FF) }, - { "BROWN", new Color(0xA52A2AFF) }, - { "BURLYWOOD", new Color(0xDEB887FF) }, - { "CADETBLUE", new Color(0x5F9EA0FF) }, - { "CHARTREUSE", new Color(0x7FFF00FF) }, - { "CHOCOLATE", new Color(0xD2691EFF) }, - { "CORAL", new Color(0xFF7F50FF) }, - { "CORNFLOWERBLUE", new Color(0x6495EDFF) }, - { "CORNSILK", new Color(0xFFF8DCFF) }, - { "CRIMSON", new Color(0xDC143CFF) }, - { "CYAN", new Color(0x00FFFFFF) }, - { "DARKBLUE", new Color(0x00008BFF) }, - { "DARKCYAN", new Color(0x008B8BFF) }, - { "DARKGOLDENROD", new Color(0xB8860BFF) }, - { "DARKGRAY", new Color(0xA9A9A9FF) }, - { "DARKGREEN", new Color(0x006400FF) }, - { "DARKKHAKI", new Color(0xBDB76BFF) }, - { "DARKMAGENTA", new Color(0x8B008BFF) }, - { "DARKOLIVEGREEN", new Color(0x556B2FFF) }, - { "DARKORANGE", new Color(0xFF8C00FF) }, - { "DARKORCHID", new Color(0x9932CCFF) }, - { "DARKRED", new Color(0x8B0000FF) }, - { "DARKSALMON", new Color(0xE9967AFF) }, - { "DARKSEAGREEN", new Color(0x8FBC8FFF) }, - { "DARKSLATEBLUE", new Color(0x483D8BFF) }, - { "DARKSLATEGRAY", new Color(0x2F4F4FFF) }, - { "DARKTURQUOISE", new Color(0x00CED1FF) }, - { "DARKVIOLET", new Color(0x9400D3FF) }, - { "DEEPPINK", new Color(0xFF1493FF) }, - { "DEEPSKYBLUE", new Color(0x00BFFFFF) }, - { "DIMGRAY", new Color(0x696969FF) }, - { "DODGERBLUE", new Color(0x1E90FFFF) }, - { "FIREBRICK", new Color(0xB22222FF) }, - { "FLORALWHITE", new Color(0xFFFAF0FF) }, - { "FORESTGREEN", new Color(0x228B22FF) }, - { "FUCHSIA", new Color(0xFF00FFFF) }, - { "GAINSBORO", new Color(0xDCDCDCFF) }, - { "GHOSTWHITE", new Color(0xF8F8FFFF) }, - { "GOLD", new Color(0xFFD700FF) }, - { "GOLDENROD", new Color(0xDAA520FF) }, - { "GRAY", new Color(0xBEBEBEFF) }, - { "GREEN", new Color(0x00FF00FF) }, - { "GREENYELLOW", new Color(0xADFF2FFF) }, - { "HONEYDEW", new Color(0xF0FFF0FF) }, - { "HOTPINK", new Color(0xFF69B4FF) }, - { "INDIANRED", new Color(0xCD5C5CFF) }, - { "INDIGO", new Color(0x4B0082FF) }, - { "IVORY", new Color(0xFFFFF0FF) }, - { "KHAKI", new Color(0xF0E68CFF) }, - { "LAVENDER", new Color(0xE6E6FAFF) }, - { "LAVENDERBLUSH", new Color(0xFFF0F5FF) }, - { "LAWNGREEN", new Color(0x7CFC00FF) }, - { "LEMONCHIFFON", new Color(0xFFFACDFF) }, - { "LIGHTBLUE", new Color(0xADD8E6FF) }, - { "LIGHTCORAL", new Color(0xF08080FF) }, - { "LIGHTCYAN", new Color(0xE0FFFFFF) }, - { "LIGHTGOLDENROD", new Color(0xFAFAD2FF) }, - { "LIGHTGRAY", new Color(0xD3D3D3FF) }, - { "LIGHTGREEN", new Color(0x90EE90FF) }, - { "LIGHTPINK", new Color(0xFFB6C1FF) }, - { "LIGHTSALMON", new Color(0xFFA07AFF) }, - { "LIGHTSEAGREEN", new Color(0x20B2AAFF) }, - { "LIGHTSKYBLUE", new Color(0x87CEFAFF) }, - { "LIGHTSLATEGRAY", new Color(0x778899FF) }, - { "LIGHTSTEELBLUE", new Color(0xB0C4DEFF) }, - { "LIGHTYELLOW", new Color(0xFFFFE0FF) }, - { "LIME", new Color(0x00FF00FF) }, - { "LIMEGREEN", new Color(0x32CD32FF) }, - { "LINEN", new Color(0xFAF0E6FF) }, - { "MAGENTA", new Color(0xFF00FFFF) }, - { "MAROON", new Color(0xB03060FF) }, - { "MEDIUMAQUAMARINE", new Color(0x66CDAAFF) }, - { "MEDIUMBLUE", new Color(0x0000CDFF) }, - { "MEDIUMORCHID", new Color(0xBA55D3FF) }, - { "MEDIUMPURPLE", new Color(0x9370DBFF) }, - { "MEDIUMSEAGREEN", new Color(0x3CB371FF) }, - { "MEDIUMSLATEBLUE", new Color(0x7B68EEFF) }, - { "MEDIUMSPRINGGREEN", new Color(0x00FA9AFF) }, - { "MEDIUMTURQUOISE", new Color(0x48D1CCFF) }, - { "MEDIUMVIOLETRED", new Color(0xC71585FF) }, - { "MIDNIGHTBLUE", new Color(0x191970FF) }, - { "MINTCREAM", new Color(0xF5FFFAFF) }, - { "MISTYROSE", new Color(0xFFE4E1FF) }, - { "MOCCASIN", new Color(0xFFE4B5FF) }, - { "NAVAJOWHITE", new Color(0xFFDEADFF) }, - { "NAVYBLUE", new Color(0x000080FF) }, - { "OLDLACE", new Color(0xFDF5E6FF) }, - { "OLIVE", new Color(0x808000FF) }, - { "OLIVEDRAB", new Color(0x6B8E23FF) }, - { "ORANGE", new Color(0xFFA500FF) }, - { "ORANGERED", new Color(0xFF4500FF) }, - { "ORCHID", new Color(0xDA70D6FF) }, - { "PALEGOLDENROD", new Color(0xEEE8AAFF) }, - { "PALEGREEN", new Color(0x98FB98FF) }, - { "PALETURQUOISE", new Color(0xAFEEEEFF) }, - { "PALEVIOLETRED", new Color(0xDB7093FF) }, - { "PAPAYAWHIP", new Color(0xFFEFD5FF) }, - { "PEACHPUFF", new Color(0xFFDAB9FF) }, - { "PERU", new Color(0xCD853FFF) }, - { "PINK", new Color(0xFFC0CBFF) }, - { "PLUM", new Color(0xDDA0DDFF) }, - { "POWDERBLUE", new Color(0xB0E0E6FF) }, - { "PURPLE", new Color(0xA020F0FF) }, - { "REBECCAPURPLE", new Color(0x663399FF) }, - { "RED", new Color(0xFF0000FF) }, - { "ROSYBROWN", new Color(0xBC8F8FFF) }, - { "ROYALBLUE", new Color(0x4169E1FF) }, - { "SADDLEBROWN", new Color(0x8B4513FF) }, - { "SALMON", new Color(0xFA8072FF) }, - { "SANDYBROWN", new Color(0xF4A460FF) }, - { "SEAGREEN", new Color(0x2E8B57FF) }, - { "SEASHELL", new Color(0xFFF5EEFF) }, - { "SIENNA", new Color(0xA0522DFF) }, - { "SILVER", new Color(0xC0C0C0FF) }, - { "SKYBLUE", new Color(0x87CEEBFF) }, - { "SLATEBLUE", new Color(0x6A5ACDFF) }, - { "SLATEGRAY", new Color(0x708090FF) }, - { "SNOW", new Color(0xFFFAFAFF) }, - { "SPRINGGREEN", new Color(0x00FF7FFF) }, - { "STEELBLUE", new Color(0x4682B4FF) }, - { "TAN", new Color(0xD2B48CFF) }, - { "TEAL", new Color(0x008080FF) }, - { "THISTLE", new Color(0xD8BFD8FF) }, - { "TOMATO", new Color(0xFF6347FF) }, - { "TRANSPARENT", new Color(0xFFFFFF00) }, - { "TURQUOISE", new Color(0x40E0D0FF) }, - { "VIOLET", new Color(0xEE82EEFF) }, - { "WEBGRAY", new Color(0x808080FF) }, - { "WEBGREEN", new Color(0x008000FF) }, - { "WEBMAROON", new Color(0x800000FF) }, - { "WEBPURPLE", new Color(0x800080FF) }, - { "WHEAT", new Color(0xF5DEB3FF) }, - { "WHITE", new Color(0xFFFFFFFF) }, - { "WHITESMOKE", new Color(0xF5F5F5FF) }, - { "YELLOW", new Color(0xFFFF00FF) }, - { "YELLOWGREEN", new Color(0x9ACD32FF) }, + { "ALICEBLUE", Colors.AliceBlue }, + { "ANTIQUEWHITE", Colors.AntiqueWhite }, + { "AQUA", Colors.Aqua }, + { "AQUAMARINE", Colors.Aquamarine }, + { "AZURE", Colors.Azure }, + { "BEIGE", Colors.Beige }, + { "BISQUE", Colors.Bisque }, + { "BLACK", Colors.Black }, + { "BLANCHEDALMOND", Colors.BlanchedAlmond }, + { "BLUE", Colors.Blue }, + { "BLUEVIOLET", Colors.BlueViolet }, + { "BROWN", Colors.Brown }, + { "BURLYWOOD", Colors.Burlywood }, + { "CADETBLUE", Colors.CadetBlue }, + { "CHARTREUSE", Colors.Chartreuse }, + { "CHOCOLATE", Colors.Chocolate }, + { "CORAL", Colors.Coral }, + { "CORNFLOWERBLUE", Colors.CornflowerBlue }, + { "CORNSILK", Colors.Cornsilk }, + { "CRIMSON", Colors.Crimson }, + { "CYAN", Colors.Cyan }, + { "DARKBLUE", Colors.DarkBlue }, + { "DARKCYAN", Colors.DarkCyan }, + { "DARKGOLDENROD", Colors.DarkGoldenrod }, + { "DARKGRAY", Colors.DarkGray }, + { "DARKGREEN", Colors.DarkGreen }, + { "DARKKHAKI", Colors.DarkKhaki }, + { "DARKMAGENTA", Colors.DarkMagenta }, + { "DARKOLIVEGREEN", Colors.DarkOliveGreen }, + { "DARKORANGE", Colors.DarkOrange }, + { "DARKORCHID", Colors.DarkOrchid }, + { "DARKRED", Colors.DarkRed }, + { "DARKSALMON", Colors.DarkSalmon }, + { "DARKSEAGREEN", Colors.DarkSeaGreen }, + { "DARKSLATEBLUE", Colors.DarkSlateBlue }, + { "DARKSLATEGRAY", Colors.DarkSlateGray }, + { "DARKTURQUOISE", Colors.DarkTurquoise }, + { "DARKVIOLET", Colors.DarkViolet }, + { "DEEPPINK", Colors.DeepPink }, + { "DEEPSKYBLUE", Colors.DeepSkyBlue }, + { "DIMGRAY", Colors.DimGray }, + { "DODGERBLUE", Colors.DodgerBlue }, + { "FIREBRICK", Colors.Firebrick }, + { "FLORALWHITE", Colors.FloralWhite }, + { "FORESTGREEN", Colors.ForestGreen }, + { "FUCHSIA", Colors.Fuchsia }, + { "GAINSBORO", Colors.Gainsboro }, + { "GHOSTWHITE", Colors.GhostWhite }, + { "GOLD", Colors.Gold }, + { "GOLDENROD", Colors.Goldenrod }, + { "GRAY", Colors.Gray }, + { "GREEN", Colors.Green }, + { "GREENYELLOW", Colors.GreenYellow }, + { "HONEYDEW", Colors.Honeydew }, + { "HOTPINK", Colors.HotPink }, + { "INDIANRED", Colors.IndianRed }, + { "INDIGO", Colors.Indigo }, + { "IVORY", Colors.Ivory }, + { "KHAKI", Colors.Khaki }, + { "LAVENDER", Colors.Lavender }, + { "LAVENDERBLUSH", Colors.LavenderBlush }, + { "LAWNGREEN", Colors.LawnGreen }, + { "LEMONCHIFFON", Colors.LemonChiffon }, + { "LIGHTBLUE", Colors.LightBlue }, + { "LIGHTCORAL", Colors.LightCoral }, + { "LIGHTCYAN", Colors.LightCyan }, + { "LIGHTGOLDENROD", Colors.LightGoldenrod }, + { "LIGHTGRAY", Colors.LightGray }, + { "LIGHTGREEN", Colors.LightGreen }, + { "LIGHTPINK", Colors.LightPink }, + { "LIGHTSALMON", Colors.LightSalmon }, + { "LIGHTSEAGREEN", Colors.LightSeaGreen }, + { "LIGHTSKYBLUE", Colors.LightSkyBlue }, + { "LIGHTSLATEGRAY", Colors.LightSlateGray }, + { "LIGHTSTEELBLUE", Colors.LightSteelBlue }, + { "LIGHTYELLOW", Colors.LightYellow }, + { "LIME", Colors.Lime }, + { "LIMEGREEN", Colors.LimeGreen }, + { "LINEN", Colors.Linen }, + { "MAGENTA", Colors.Magenta }, + { "MAROON", Colors.Maroon }, + { "MEDIUMAQUAMARINE", Colors.MediumAquamarine }, + { "MEDIUMBLUE", Colors.MediumBlue }, + { "MEDIUMORCHID", Colors.MediumOrchid }, + { "MEDIUMPURPLE", Colors.MediumPurple }, + { "MEDIUMSEAGREEN", Colors.MediumSeaGreen }, + { "MEDIUMSLATEBLUE", Colors.MediumSlateBlue }, + { "MEDIUMSPRINGGREEN", Colors.MediumSpringGreen }, + { "MEDIUMTURQUOISE", Colors.MediumTurquoise }, + { "MEDIUMVIOLETRED", Colors.MediumVioletRed }, + { "MIDNIGHTBLUE", Colors.MidnightBlue }, + { "MINTCREAM", Colors.MintCream }, + { "MISTYROSE", Colors.MistyRose }, + { "MOCCASIN", Colors.Moccasin }, + { "NAVAJOWHITE", Colors.NavajoWhite }, + { "NAVYBLUE", Colors.NavyBlue }, + { "OLDLACE", Colors.OldLace }, + { "OLIVE", Colors.Olive }, + { "OLIVEDRAB", Colors.OliveDrab }, + { "ORANGE", Colors.Orange }, + { "ORANGERED", Colors.OrangeRed }, + { "ORCHID", Colors.Orchid }, + { "PALEGOLDENROD", Colors.PaleGoldenrod }, + { "PALEGREEN", Colors.PaleGreen }, + { "PALETURQUOISE", Colors.PaleTurquoise }, + { "PALEVIOLETRED", Colors.PaleVioletRed }, + { "PAPAYAWHIP", Colors.PapayaWhip }, + { "PEACHPUFF", Colors.PeachPuff }, + { "PERU", Colors.Peru }, + { "PINK", Colors.Pink }, + { "PLUM", Colors.Plum }, + { "POWDERBLUE", Colors.PowderBlue }, + { "PURPLE", Colors.Purple }, + { "REBECCAPURPLE", Colors.RebeccaPurple }, + { "RED", Colors.Red }, + { "ROSYBROWN", Colors.RosyBrown }, + { "ROYALBLUE", Colors.RoyalBlue }, + { "SADDLEBROWN", Colors.SaddleBrown }, + { "SALMON", Colors.Salmon }, + { "SANDYBROWN", Colors.SandyBrown }, + { "SEAGREEN", Colors.SeaGreen }, + { "SEASHELL", Colors.Seashell }, + { "SIENNA", Colors.Sienna }, + { "SILVER", Colors.Silver }, + { "SKYBLUE", Colors.SkyBlue }, + { "SLATEBLUE", Colors.SlateBlue }, + { "SLATEGRAY", Colors.SlateGray }, + { "SNOW", Colors.Snow }, + { "SPRINGGREEN", Colors.SpringGreen }, + { "STEELBLUE", Colors.SteelBlue }, + { "TAN", Colors.Tan }, + { "TEAL", Colors.Teal }, + { "THISTLE", Colors.Thistle }, + { "TOMATO", Colors.Tomato }, + { "TRANSPARENT", Colors.Transparent }, + { "TURQUOISE", Colors.Turquoise }, + { "VIOLET", Colors.Violet }, + { "WEBGRAY", Colors.WebGray }, + { "WEBGREEN", Colors.WebGreen }, + { "WEBMAROON", Colors.WebMaroon }, + { "WEBPURPLE", Colors.WebPurple }, + { "WHEAT", Colors.Wheat }, + { "WHITE", Colors.White }, + { "WHITESMOKE", Colors.WhiteSmoke }, + { "YELLOW", Colors.Yellow }, + { "YELLOWGREEN", Colors.YellowGreen }, }; #pragma warning disable CS1591 // Disable warning: "Missing XML comment for publicly visible type or member" - public static Color AliceBlue { get { return namedColors["ALICEBLUE"]; } } - public static Color AntiqueWhite { get { return namedColors["ANTIQUEWHITE"]; } } - public static Color Aqua { get { return namedColors["AQUA"]; } } - public static Color Aquamarine { get { return namedColors["AQUAMARINE"]; } } - public static Color Azure { get { return namedColors["AZURE"]; } } - public static Color Beige { get { return namedColors["BEIGE"]; } } - public static Color Bisque { get { return namedColors["BISQUE"]; } } - public static Color Black { get { return namedColors["BLACK"]; } } - public static Color BlanchedAlmond { get { return namedColors["BLANCHEDALMOND"]; } } - public static Color Blue { get { return namedColors["BLUE"]; } } - public static Color BlueViolet { get { return namedColors["BLUEVIOLET"]; } } - public static Color Brown { get { return namedColors["BROWN"]; } } - public static Color Burlywood { get { return namedColors["BURLYWOOD"]; } } - public static Color CadetBlue { get { return namedColors["CADETBLUE"]; } } - public static Color Chartreuse { get { return namedColors["CHARTREUSE"]; } } - public static Color Chocolate { get { return namedColors["CHOCOLATE"]; } } - public static Color Coral { get { return namedColors["CORAL"]; } } - public static Color CornflowerBlue { get { return namedColors["CORNFLOWERBLUE"]; } } - public static Color Cornsilk { get { return namedColors["CORNSILK"]; } } - public static Color Crimson { get { return namedColors["CRIMSON"]; } } - public static Color Cyan { get { return namedColors["CYAN"]; } } - public static Color DarkBlue { get { return namedColors["DARKBLUE"]; } } - public static Color DarkCyan { get { return namedColors["DARKCYAN"]; } } - public static Color DarkGoldenrod { get { return namedColors["DARKGOLDENROD"]; } } - public static Color DarkGray { get { return namedColors["DARKGRAY"]; } } - public static Color DarkGreen { get { return namedColors["DARKGREEN"]; } } - public static Color DarkKhaki { get { return namedColors["DARKKHAKI"]; } } - public static Color DarkMagenta { get { return namedColors["DARKMAGENTA"]; } } - public static Color DarkOliveGreen { get { return namedColors["DARKOLIVEGREEN"]; } } - public static Color DarkOrange { get { return namedColors["DARKORANGE"]; } } - public static Color DarkOrchid { get { return namedColors["DARKORCHID"]; } } - public static Color DarkRed { get { return namedColors["DARKRED"]; } } - public static Color DarkSalmon { get { return namedColors["DARKSALMON"]; } } - public static Color DarkSeaGreen { get { return namedColors["DARKSEAGREEN"]; } } - public static Color DarkSlateBlue { get { return namedColors["DARKSLATEBLUE"]; } } - public static Color DarkSlateGray { get { return namedColors["DARKSLATEGRAY"]; } } - public static Color DarkTurquoise { get { return namedColors["DARKTURQUOISE"]; } } - public static Color DarkViolet { get { return namedColors["DARKVIOLET"]; } } - public static Color DeepPink { get { return namedColors["DEEPPINK"]; } } - public static Color DeepSkyBlue { get { return namedColors["DEEPSKYBLUE"]; } } - public static Color DimGray { get { return namedColors["DIMGRAY"]; } } - public static Color DodgerBlue { get { return namedColors["DODGERBLUE"]; } } - public static Color Firebrick { get { return namedColors["FIREBRICK"]; } } - public static Color FloralWhite { get { return namedColors["FLORALWHITE"]; } } - public static Color ForestGreen { get { return namedColors["FORESTGREEN"]; } } - public static Color Fuchsia { get { return namedColors["FUCHSIA"]; } } - public static Color Gainsboro { get { return namedColors["GAINSBORO"]; } } - public static Color GhostWhite { get { return namedColors["GHOSTWHITE"]; } } - public static Color Gold { get { return namedColors["GOLD"]; } } - public static Color Goldenrod { get { return namedColors["GOLDENROD"]; } } - public static Color Gray { get { return namedColors["GRAY"]; } } - public static Color Green { get { return namedColors["GREEN"]; } } - public static Color GreenYellow { get { return namedColors["GREENYELLOW"]; } } - public static Color Honeydew { get { return namedColors["HONEYDEW"]; } } - public static Color HotPink { get { return namedColors["HOTPINK"]; } } - public static Color IndianRed { get { return namedColors["INDIANRED"]; } } - public static Color Indigo { get { return namedColors["INDIGO"]; } } - public static Color Ivory { get { return namedColors["IVORY"]; } } - public static Color Khaki { get { return namedColors["KHAKI"]; } } - public static Color Lavender { get { return namedColors["LAVENDER"]; } } - public static Color LavenderBlush { get { return namedColors["LAVENDERBLUSH"]; } } - public static Color LawnGreen { get { return namedColors["LAWNGREEN"]; } } - public static Color LemonChiffon { get { return namedColors["LEMONCHIFFON"]; } } - public static Color LightBlue { get { return namedColors["LIGHTBLUE"]; } } - public static Color LightCoral { get { return namedColors["LIGHTCORAL"]; } } - public static Color LightCyan { get { return namedColors["LIGHTCYAN"]; } } - public static Color LightGoldenrod { get { return namedColors["LIGHTGOLDENROD"]; } } - public static Color LightGray { get { return namedColors["LIGHTGRAY"]; } } - public static Color LightGreen { get { return namedColors["LIGHTGREEN"]; } } - public static Color LightPink { get { return namedColors["LIGHTPINK"]; } } - public static Color LightSalmon { get { return namedColors["LIGHTSALMON"]; } } - public static Color LightSeaGreen { get { return namedColors["LIGHTSEAGREEN"]; } } - public static Color LightSkyBlue { get { return namedColors["LIGHTSKYBLUE"]; } } - public static Color LightSlateGray { get { return namedColors["LIGHTSLATEGRAY"]; } } - public static Color LightSteelBlue { get { return namedColors["LIGHTSTEELBLUE"]; } } - public static Color LightYellow { get { return namedColors["LIGHTYELLOW"]; } } - public static Color Lime { get { return namedColors["LIME"]; } } - public static Color LimeGreen { get { return namedColors["LIMEGREEN"]; } } - public static Color Linen { get { return namedColors["LINEN"]; } } - public static Color Magenta { get { return namedColors["MAGENTA"]; } } - public static Color Maroon { get { return namedColors["MAROON"]; } } - public static Color MediumAquamarine { get { return namedColors["MEDIUMAQUAMARINE"]; } } - public static Color MediumBlue { get { return namedColors["MEDIUMBLUE"]; } } - public static Color MediumOrchid { get { return namedColors["MEDIUMORCHID"]; } } - public static Color MediumPurple { get { return namedColors["MEDIUMPURPLE"]; } } - public static Color MediumSeaGreen { get { return namedColors["MEDIUMSEAGREEN"]; } } - public static Color MediumSlateBlue { get { return namedColors["MEDIUMSLATEBLUE"]; } } - public static Color MediumSpringGreen { get { return namedColors["MEDIUMSPRINGGREEN"]; } } - public static Color MediumTurquoise { get { return namedColors["MEDIUMTURQUOISE"]; } } - public static Color MediumVioletRed { get { return namedColors["MEDIUMVIOLETRED"]; } } - public static Color MidnightBlue { get { return namedColors["MIDNIGHTBLUE"]; } } - public static Color MintCream { get { return namedColors["MINTCREAM"]; } } - public static Color MistyRose { get { return namedColors["MISTYROSE"]; } } - public static Color Moccasin { get { return namedColors["MOCCASIN"]; } } - public static Color NavajoWhite { get { return namedColors["NAVAJOWHITE"]; } } - public static Color NavyBlue { get { return namedColors["NAVYBLUE"]; } } - public static Color OldLace { get { return namedColors["OLDLACE"]; } } - public static Color Olive { get { return namedColors["OLIVE"]; } } - public static Color OliveDrab { get { return namedColors["OLIVEDRAB"]; } } - public static Color Orange { get { return namedColors["ORANGE"]; } } - public static Color OrangeRed { get { return namedColors["ORANGERED"]; } } - public static Color Orchid { get { return namedColors["ORCHID"]; } } - public static Color PaleGoldenrod { get { return namedColors["PALEGOLDENROD"]; } } - public static Color PaleGreen { get { return namedColors["PALEGREEN"]; } } - public static Color PaleTurquoise { get { return namedColors["PALETURQUOISE"]; } } - public static Color PaleVioletRed { get { return namedColors["PALEVIOLETRED"]; } } - public static Color PapayaWhip { get { return namedColors["PAPAYAWHIP"]; } } - public static Color PeachPuff { get { return namedColors["PEACHPUFF"]; } } - public static Color Peru { get { return namedColors["PERU"]; } } - public static Color Pink { get { return namedColors["PINK"]; } } - public static Color Plum { get { return namedColors["PLUM"]; } } - public static Color PowderBlue { get { return namedColors["POWDERBLUE"]; } } - public static Color Purple { get { return namedColors["PURPLE"]; } } - public static Color RebeccaPurple { get { return namedColors["REBECCAPURPLE"]; } } - public static Color Red { get { return namedColors["RED"]; } } - public static Color RosyBrown { get { return namedColors["ROSYBROWN"]; } } - public static Color RoyalBlue { get { return namedColors["ROYALBLUE"]; } } - public static Color SaddleBrown { get { return namedColors["SADDLEBROWN"]; } } - public static Color Salmon { get { return namedColors["SALMON"]; } } - public static Color SandyBrown { get { return namedColors["SANDYBROWN"]; } } - public static Color SeaGreen { get { return namedColors["SEAGREEN"]; } } - public static Color Seashell { get { return namedColors["SEASHELL"]; } } - public static Color Sienna { get { return namedColors["SIENNA"]; } } - public static Color Silver { get { return namedColors["SILVER"]; } } - public static Color SkyBlue { get { return namedColors["SKYBLUE"]; } } - public static Color SlateBlue { get { return namedColors["SLATEBLUE"]; } } - public static Color SlateGray { get { return namedColors["SLATEGRAY"]; } } - public static Color Snow { get { return namedColors["SNOW"]; } } - public static Color SpringGreen { get { return namedColors["SPRINGGREEN"]; } } - public static Color SteelBlue { get { return namedColors["STEELBLUE"]; } } - public static Color Tan { get { return namedColors["TAN"]; } } - public static Color Teal { get { return namedColors["TEAL"]; } } - public static Color Thistle { get { return namedColors["THISTLE"]; } } - public static Color Tomato { get { return namedColors["TOMATO"]; } } - public static Color Transparent { get { return namedColors["TRANSPARENT"]; } } - public static Color Turquoise { get { return namedColors["TURQUOISE"]; } } - public static Color Violet { get { return namedColors["VIOLET"]; } } - public static Color WebGray { get { return namedColors["WEBGRAY"]; } } - public static Color WebGreen { get { return namedColors["WEBGREEN"]; } } - public static Color WebMaroon { get { return namedColors["WEBMAROON"]; } } - public static Color WebPurple { get { return namedColors["WEBPURPLE"]; } } - public static Color Wheat { get { return namedColors["WHEAT"]; } } - public static Color White { get { return namedColors["WHITE"]; } } - public static Color WhiteSmoke { get { return namedColors["WHITESMOKE"]; } } - public static Color Yellow { get { return namedColors["YELLOW"]; } } - public static Color YellowGreen { get { return namedColors["YELLOWGREEN"]; } } + public static Color AliceBlue => new Color(0xF0F8FFFF); + public static Color AntiqueWhite => new Color(0xFAEBD7FF); + public static Color Aqua => new Color(0x00FFFFFF); + public static Color Aquamarine => new Color(0x7FFFD4FF); + public static Color Azure => new Color(0xF0FFFFFF); + public static Color Beige => new Color(0xF5F5DCFF); + public static Color Bisque => new Color(0xFFE4C4FF); + public static Color Black => new Color(0x000000FF); + public static Color BlanchedAlmond => new Color(0xFFEBCDFF); + public static Color Blue => new Color(0x0000FFFF); + public static Color BlueViolet => new Color(0x8A2BE2FF); + public static Color Brown => new Color(0xA52A2AFF); + public static Color Burlywood => new Color(0xDEB887FF); + public static Color CadetBlue => new Color(0x5F9EA0FF); + public static Color Chartreuse => new Color(0x7FFF00FF); + public static Color Chocolate => new Color(0xD2691EFF); + public static Color Coral => new Color(0xFF7F50FF); + public static Color CornflowerBlue => new Color(0x6495EDFF); + public static Color Cornsilk => new Color(0xFFF8DCFF); + public static Color Crimson => new Color(0xDC143CFF); + public static Color Cyan => new Color(0x00FFFFFF); + public static Color DarkBlue => new Color(0x00008BFF); + public static Color DarkCyan => new Color(0x008B8BFF); + public static Color DarkGoldenrod => new Color(0xB8860BFF); + public static Color DarkGray => new Color(0xA9A9A9FF); + public static Color DarkGreen => new Color(0x006400FF); + public static Color DarkKhaki => new Color(0xBDB76BFF); + public static Color DarkMagenta => new Color(0x8B008BFF); + public static Color DarkOliveGreen => new Color(0x556B2FFF); + public static Color DarkOrange => new Color(0xFF8C00FF); + public static Color DarkOrchid => new Color(0x9932CCFF); + public static Color DarkRed => new Color(0x8B0000FF); + public static Color DarkSalmon => new Color(0xE9967AFF); + public static Color DarkSeaGreen => new Color(0x8FBC8FFF); + public static Color DarkSlateBlue => new Color(0x483D8BFF); + public static Color DarkSlateGray => new Color(0x2F4F4FFF); + public static Color DarkTurquoise => new Color(0x00CED1FF); + public static Color DarkViolet => new Color(0x9400D3FF); + public static Color DeepPink => new Color(0xFF1493FF); + public static Color DeepSkyBlue => new Color(0x00BFFFFF); + public static Color DimGray => new Color(0x696969FF); + public static Color DodgerBlue => new Color(0x1E90FFFF); + public static Color Firebrick => new Color(0xB22222FF); + public static Color FloralWhite => new Color(0xFFFAF0FF); + public static Color ForestGreen => new Color(0x228B22FF); + public static Color Fuchsia => new Color(0xFF00FFFF); + public static Color Gainsboro => new Color(0xDCDCDCFF); + public static Color GhostWhite => new Color(0xF8F8FFFF); + public static Color Gold => new Color(0xFFD700FF); + public static Color Goldenrod => new Color(0xDAA520FF); + public static Color Gray => new Color(0xBEBEBEFF); + public static Color Green => new Color(0x00FF00FF); + public static Color GreenYellow => new Color(0xADFF2FFF); + public static Color Honeydew => new Color(0xF0FFF0FF); + public static Color HotPink => new Color(0xFF69B4FF); + public static Color IndianRed => new Color(0xCD5C5CFF); + public static Color Indigo => new Color(0x4B0082FF); + public static Color Ivory => new Color(0xFFFFF0FF); + public static Color Khaki => new Color(0xF0E68CFF); + public static Color Lavender => new Color(0xE6E6FAFF); + public static Color LavenderBlush => new Color(0xFFF0F5FF); + public static Color LawnGreen => new Color(0x7CFC00FF); + public static Color LemonChiffon => new Color(0xFFFACDFF); + public static Color LightBlue => new Color(0xADD8E6FF); + public static Color LightCoral => new Color(0xF08080FF); + public static Color LightCyan => new Color(0xE0FFFFFF); + public static Color LightGoldenrod => new Color(0xFAFAD2FF); + public static Color LightGray => new Color(0xD3D3D3FF); + public static Color LightGreen => new Color(0x90EE90FF); + public static Color LightPink => new Color(0xFFB6C1FF); + public static Color LightSalmon => new Color(0xFFA07AFF); + public static Color LightSeaGreen => new Color(0x20B2AAFF); + public static Color LightSkyBlue => new Color(0x87CEFAFF); + public static Color LightSlateGray => new Color(0x778899FF); + public static Color LightSteelBlue => new Color(0xB0C4DEFF); + public static Color LightYellow => new Color(0xFFFFE0FF); + public static Color Lime => new Color(0x00FF00FF); + public static Color LimeGreen => new Color(0x32CD32FF); + public static Color Linen => new Color(0xFAF0E6FF); + public static Color Magenta => new Color(0xFF00FFFF); + public static Color Maroon => new Color(0xB03060FF); + public static Color MediumAquamarine => new Color(0x66CDAAFF); + public static Color MediumBlue => new Color(0x0000CDFF); + public static Color MediumOrchid => new Color(0xBA55D3FF); + public static Color MediumPurple => new Color(0x9370DBFF); + public static Color MediumSeaGreen => new Color(0x3CB371FF); + public static Color MediumSlateBlue => new Color(0x7B68EEFF); + public static Color MediumSpringGreen => new Color(0x00FA9AFF); + public static Color MediumTurquoise => new Color(0x48D1CCFF); + public static Color MediumVioletRed => new Color(0xC71585FF); + public static Color MidnightBlue => new Color(0x191970FF); + public static Color MintCream => new Color(0xF5FFFAFF); + public static Color MistyRose => new Color(0xFFE4E1FF); + public static Color Moccasin => new Color(0xFFE4B5FF); + public static Color NavajoWhite => new Color(0xFFDEADFF); + public static Color NavyBlue => new Color(0x000080FF); + public static Color OldLace => new Color(0xFDF5E6FF); + public static Color Olive => new Color(0x808000FF); + public static Color OliveDrab => new Color(0x6B8E23FF); + public static Color Orange => new Color(0xFFA500FF); + public static Color OrangeRed => new Color(0xFF4500FF); + public static Color Orchid => new Color(0xDA70D6FF); + public static Color PaleGoldenrod => new Color(0xEEE8AAFF); + public static Color PaleGreen => new Color(0x98FB98FF); + public static Color PaleTurquoise => new Color(0xAFEEEEFF); + public static Color PaleVioletRed => new Color(0xDB7093FF); + public static Color PapayaWhip => new Color(0xFFEFD5FF); + public static Color PeachPuff => new Color(0xFFDAB9FF); + public static Color Peru => new Color(0xCD853FFF); + public static Color Pink => new Color(0xFFC0CBFF); + public static Color Plum => new Color(0xDDA0DDFF); + public static Color PowderBlue => new Color(0xB0E0E6FF); + public static Color Purple => new Color(0xA020F0FF); + public static Color RebeccaPurple => new Color(0x663399FF); + public static Color Red => new Color(0xFF0000FF); + public static Color RosyBrown => new Color(0xBC8F8FFF); + public static Color RoyalBlue => new Color(0x4169E1FF); + public static Color SaddleBrown => new Color(0x8B4513FF); + public static Color Salmon => new Color(0xFA8072FF); + public static Color SandyBrown => new Color(0xF4A460FF); + public static Color SeaGreen => new Color(0x2E8B57FF); + public static Color Seashell => new Color(0xFFF5EEFF); + public static Color Sienna => new Color(0xA0522DFF); + public static Color Silver => new Color(0xC0C0C0FF); + public static Color SkyBlue => new Color(0x87CEEBFF); + public static Color SlateBlue => new Color(0x6A5ACDFF); + public static Color SlateGray => new Color(0x708090FF); + public static Color Snow => new Color(0xFFFAFAFF); + public static Color SpringGreen => new Color(0x00FF7FFF); + public static Color SteelBlue => new Color(0x4682B4FF); + public static Color Tan => new Color(0xD2B48CFF); + public static Color Teal => new Color(0x008080FF); + public static Color Thistle => new Color(0xD8BFD8FF); + public static Color Tomato => new Color(0xFF6347FF); + public static Color Transparent => new Color(0xFFFFFF00); + public static Color Turquoise => new Color(0x40E0D0FF); + public static Color Violet => new Color(0xEE82EEFF); + public static Color WebGray => new Color(0x808080FF); + public static Color WebGreen => new Color(0x008000FF); + public static Color WebMaroon => new Color(0x800000FF); + public static Color WebPurple => new Color(0x800080FF); + public static Color Wheat => new Color(0xF5DEB3FF); + public static Color White => new Color(0xFFFFFFFF); + public static Color WhiteSmoke => new Color(0xF5F5F5FF); + public static Color Yellow => new Color(0xFFFF00FF); + public static Color YellowGreen => new Color(0x9ACD32FF); #pragma warning restore CS1591 } } diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 105c35383b..851a94b32f 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -147,14 +147,6 @@ void SubViewportContainer::_notification(int p_what) { } } break; - case NOTIFICATION_MOUSE_ENTER: { - _notify_viewports(NOTIFICATION_VP_MOUSE_ENTER); - } break; - - case NOTIFICATION_MOUSE_EXIT: { - _notify_viewports(NOTIFICATION_VP_MOUSE_EXIT); - } break; - case NOTIFICATION_FOCUS_ENTER: { // If focused, send InputEvent to the SubViewport before the Gui-Input stage. set_process_input(true); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index cb72e4ec08..8d5133311a 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -645,7 +645,8 @@ int Node::get_multiplayer_authority() const { bool Node::is_multiplayer_authority() const { ERR_FAIL_COND_V(!is_inside_tree(), false); - return get_multiplayer()->get_unique_id() == data.multiplayer_authority; + Ref<MultiplayerAPI> api = get_multiplayer(); + return api.is_valid() && (api->get_unique_id() == data.multiplayer_authority); } /***** RPC CONFIG ********/ @@ -724,7 +725,12 @@ Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallE Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED); - return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount); + + Ref<MultiplayerAPI> api = get_multiplayer(); + if (api.is_null()) { + return ERR_UNCONFIGURED; + } + return api->rpcp(this, p_peer_id, p_method, p_arg, p_argcount); } Ref<MultiplayerAPI> Node::get_multiplayer() const { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index d9a1cfe965..94e5c07552 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -421,7 +421,12 @@ void Viewport::_sub_window_remove(Window *p_window) { ERR_FAIL_NULL(RenderingServer::get_singleton()); - RS::get_singleton()->free(gui.sub_windows[index].canvas_item); + SubWindow sw = gui.sub_windows[index]; + if (gui.subwindow_over == sw.window) { + sw.window->_mouse_leave_viewport(); + gui.subwindow_over = nullptr; + } + RS::get_singleton()->free(sw.canvas_item); gui.sub_windows.remove_at(index); if (gui.sub_windows.size() == 0) { @@ -633,10 +638,8 @@ void Viewport::_notification(int p_what) { case NOTIFICATION_VP_MOUSE_EXIT: { gui.mouse_in_viewport = false; _drop_physics_mouseover(); - _drop_mouse_over(); - _gui_cancel_tooltip(); - // When the mouse exits the viewport, we want to end mouse_over, but - // not mouse_focus, because, for example, we want to continue + // When the mouse exits the viewport, we don't want to end + // mouse_focus, because, for example, we want to continue // dragging a scrollbar even if the mouse has left the viewport. } break; @@ -1885,25 +1888,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } Control *over = nullptr; - if (gui.mouse_in_viewport) { - over = gui_find_control(mpos); - } - - if (over != gui.mouse_over) { - if (!gui.mouse_over) { - _drop_physics_mouseover(); - } - _drop_mouse_over(); - _gui_cancel_tooltip(); - - if (over) { - _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER); - gui.mouse_over = over; - } - } - if (gui.mouse_focus) { over = gui.mouse_focus; + } else if (gui.mouse_in_viewport) { + over = gui_find_control(mpos); } DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape(); @@ -2382,7 +2370,7 @@ void Viewport::_gui_hide_control(Control *p_control) { gui_release_focus(); } if (gui.mouse_over == p_control) { - gui.mouse_over = nullptr; + _drop_mouse_over(); } if (gui.drag_mouse_over == p_control) { gui.drag_mouse_over = nullptr; @@ -2405,7 +2393,7 @@ void Viewport::_gui_remove_control(Control *p_control) { gui.key_focus = nullptr; } if (gui.mouse_over == p_control) { - gui.mouse_over = nullptr; + _drop_mouse_over(); } if (gui.drag_mouse_over == p_control) { gui.drag_mouse_over = nullptr; @@ -2459,13 +2447,6 @@ void Viewport::_gui_accept_event() { } } -void Viewport::_drop_mouse_over() { - if (gui.mouse_over) { - _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT); - gui.mouse_over = nullptr; - } -} - void Viewport::_drop_mouse_focus() { Control *c = gui.mouse_focus; BitField<MouseButtonMask> mask = gui.mouse_focus_mask; @@ -2949,6 +2930,156 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { return true; } +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. + + 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. + return; + } + + if (get_tree()->get_root()->is_embedding_subwindows() || is_sub_viewport()) { + // Use embedder logic for calculating mouse position. + _update_mouse_over(gui.last_mouse_pos); + } else { + // Native Window: Use DisplayServer logic for calculating mouse position. + Window *receiving_window = get_tree()->get_root()->gui.windowmanager_window_over; + if (!receiving_window) { + return; + } + + Vector2 pos = DisplayServer::get_singleton()->mouse_get_position() - receiving_window->get_position(); + pos = receiving_window->get_final_transform().affine_inverse().xform(pos); + + receiving_window->_update_mouse_over(pos); + } +} + +void Viewport::_update_mouse_over(Vector2 p_pos) { + // Look for embedded windows at mouse position. + if (is_embedding_subwindows()) { + for (int i = gui.sub_windows.size() - 1; i >= 0; i--) { + Window *sw = gui.sub_windows[i].window; + Rect2 swrect = Rect2(sw->get_position(), sw->get_size()); + Rect2 swrect_border = swrect; + + if (!sw->get_flag(Window::FLAG_BORDERLESS)) { + int title_height = sw->get_theme_constant(SNAME("title_height")); + int margin = sw->get_theme_constant(SNAME("resize_margin")); + swrect_border.position.y -= title_height + margin; + swrect_border.size.y += title_height + margin * 2; + swrect_border.position.x -= margin; + swrect_border.size.x += margin * 2; + } + + if (swrect_border.has_point(p_pos)) { + if (gui.mouse_over) { + _drop_mouse_over(); + } else if (!gui.subwindow_over) { + _drop_physics_mouseover(); + } + if (swrect.has_point(p_pos)) { + if (sw != gui.subwindow_over) { + if (gui.subwindow_over) { + gui.subwindow_over->_mouse_leave_viewport(); + } + gui.subwindow_over = sw; + if (!sw->is_input_disabled()) { + sw->notification(NOTIFICATION_VP_MOUSE_ENTER); + } + } + if (!sw->is_input_disabled()) { + sw->_update_mouse_over(sw->get_final_transform().affine_inverse().xform(p_pos - sw->get_position())); + } + } else { + if (gui.subwindow_over) { + gui.subwindow_over->_mouse_leave_viewport(); + gui.subwindow_over = nullptr; + } + } + return; + } + } + + if (gui.subwindow_over) { + // Take care of moving mouse out of any embedded Window. + gui.subwindow_over->_mouse_leave_viewport(); + gui.subwindow_over = nullptr; + } + } + + // Look for Controls at mouse position. + Control *over = gui_find_control(p_pos); + bool notify_embedded_viewports = false; + if (over != gui.mouse_over) { + if (gui.mouse_over) { + _drop_mouse_over(); + } else { + _drop_physics_mouseover(); + } + + gui.mouse_over = over; + if (over) { + over->notification(Control::NOTIFICATION_MOUSE_ENTER); + notify_embedded_viewports = true; + } + } + + if (over) { + SubViewportContainer *c = Object::cast_to<SubViewportContainer>(over); + if (!c) { + return; + } + Vector2 pos = c->get_global_transform_with_canvas().affine_inverse().xform(p_pos); + if (c->is_stretch_enabled()) { + pos /= c->get_stretch_shrink(); + } + + for (int i = 0; i < c->get_child_count(); i++) { + SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i)); + if (!v || v->is_input_disabled()) { + continue; + } + if (notify_embedded_viewports) { + v->notification(NOTIFICATION_VP_MOUSE_ENTER); + } + v->_update_mouse_over(v->get_final_transform().affine_inverse().xform(pos)); + } + } +} + +void Viewport::_mouse_leave_viewport() { + if (!is_inside_tree() || is_input_disabled()) { + return; + } + if (gui.subwindow_over) { + gui.subwindow_over->_mouse_leave_viewport(); + gui.subwindow_over = nullptr; + } else if (gui.mouse_over) { + _drop_mouse_over(); + } + notification(NOTIFICATION_VP_MOUSE_EXIT); +} + +void Viewport::_drop_mouse_over() { + _gui_cancel_tooltip(); + SubViewportContainer *c = Object::cast_to<SubViewportContainer>(gui.mouse_over); + if (c) { + for (int i = 0; i < c->get_child_count(); i++) { + SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i)); + if (!v) { + continue; + } + v->_mouse_leave_viewport(); + } + } + if (gui.mouse_over->is_inside_tree()) { + gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT); + } + gui.mouse_over = nullptr; +} + void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); @@ -2974,6 +3105,8 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { Ref<InputEventMouse> me = ev; if (me.is_valid()) { gui.last_mouse_pos = me->get_position(); + + _update_mouse_over(); } if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) { @@ -3111,7 +3244,7 @@ void Viewport::set_disable_input(bool p_disable) { } if (p_disable) { _drop_mouse_focus(); - _drop_mouse_over(); + _mouse_leave_viewport(); _gui_cancel_tooltip(); } disable_input = p_disable; @@ -4616,6 +4749,10 @@ bool SubViewport::is_directly_attached_to_screen() const { return Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport() && get_parent()->get_viewport()->is_directly_attached_to_screen(); } +bool SubViewport::is_attached_in_viewport() const { + return Object::cast_to<SubViewportContainer>(get_parent()); +} + void SubViewport::_notification(int p_what) { ERR_MAIN_THREAD_GUARD; switch (p_what) { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index e56d69a868..7cfad42119 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -346,8 +346,6 @@ private: Ref<Texture2D> vrs_texture; struct GUI { - // info used when this is a window - bool forced_mouse_focus = false; //used for menu buttons bool mouse_in_viewport = true; bool key_event_accepted = false; @@ -358,6 +356,8 @@ private: BitField<MouseButtonMask> mouse_focus_mask; Control *key_focus = nullptr; Control *mouse_over = nullptr; + Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr. + Window *windowmanager_window_over = nullptr; // Only used in root Viewport. Control *drag_mouse_over = nullptr; Vector2 drag_mouse_over_pos; Control *tooltip_control = nullptr; @@ -466,6 +466,9 @@ private: bool _sub_windows_forward_input(const Ref<InputEvent> &p_event); 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 bool _can_consume_input_events() const { return true; } uint64_t event_count = 0; @@ -478,6 +481,8 @@ 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(); @@ -665,6 +670,8 @@ public: virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const; virtual Transform2D get_popup_base_transform() const { return Transform2D(); } virtual bool is_directly_attached_to_screen() const { return false; }; + virtual bool is_attached_in_viewport() const { return false; }; + virtual bool is_sub_viewport() const { return false; }; #ifndef _3D_DISABLED bool use_xr = false; @@ -797,6 +804,8 @@ public: virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override; virtual Transform2D get_popup_base_transform() const override; virtual bool is_directly_attached_to_screen() const override; + virtual bool is_attached_in_viewport() const override; + virtual bool is_sub_viewport() const override { return true; }; void _validate_property(PropertyInfo &p_property) const; SubViewport(); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 3ea53da141..6182530431 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -677,16 +677,20 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { switch (p_event) { case DisplayServer::WINDOW_EVENT_MOUSE_ENTER: { _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); - emit_signal(SNAME("mouse_entered")); + Window *root = get_tree()->get_root(); + DEV_ASSERT(!root->gui.windowmanager_window_over); // Entering a window while a window is hovered should never happen. + root->gui.windowmanager_window_over = this; notification(NOTIFICATION_VP_MOUSE_ENTER); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape } } break; case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: { - notification(NOTIFICATION_VP_MOUSE_EXIT); + Window *root = get_tree()->get_root(); + DEV_ASSERT(root->gui.windowmanager_window_over); // Exiting a window, while no window is hovered should never happen. + root->gui.windowmanager_window_over->_mouse_leave_viewport(); + root->gui.windowmanager_window_over = nullptr; _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT); - emit_signal(SNAME("mouse_exited")); } break; case DisplayServer::WINDOW_EVENT_FOCUS_IN: { focused = true; @@ -1283,6 +1287,14 @@ void Window::_notification(int p_what) { RS::get_singleton()->viewport_set_active(get_viewport_rid(), false); } break; + + case NOTIFICATION_VP_MOUSE_ENTER: { + emit_signal(SceneStringNames::get_singleton()->mouse_entered); + } break; + + case NOTIFICATION_VP_MOUSE_EXIT: { + emit_signal(SceneStringNames::get_singleton()->mouse_exited); + } break; } } @@ -2495,6 +2507,10 @@ bool Window::is_directly_attached_to_screen() const { return is_inside_tree(); } +bool Window::is_attached_in_viewport() const { + return get_embedder(); +} + 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 7a10499d9b..24142b8a91 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -404,6 +404,7 @@ public: virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override; virtual Transform2D get_popup_base_transform() const override; virtual bool is_directly_attached_to_screen() const override; + virtual bool is_attached_in_viewport() const override; Rect2i get_parent_rect() const; virtual DisplayServer::WindowID get_window_id() const override; diff --git a/scene/resources/placeholder_textures.cpp b/scene/resources/placeholder_textures.cpp index 224ea1f177..c395195c47 100644 --- a/scene/resources/placeholder_textures.cpp +++ b/scene/resources/placeholder_textures.cpp @@ -51,6 +51,9 @@ Ref<Image> PlaceholderTexture2D::get_image() const { } RID PlaceholderTexture2D::get_rid() const { + if (rid.is_null()) { + rid = RenderingServer::get_singleton()->texture_2d_placeholder_create(); + } return rid; } @@ -61,12 +64,13 @@ void PlaceholderTexture2D::_bind_methods() { } PlaceholderTexture2D::PlaceholderTexture2D() { - rid = RS::get_singleton()->texture_2d_placeholder_create(); } PlaceholderTexture2D::~PlaceholderTexture2D() { ERR_FAIL_NULL(RenderingServer::get_singleton()); - RS::get_singleton()->free(rid); + if (rid.is_valid()) { + RS::get_singleton()->free(rid); + } } /////////////////////////////////////////////// @@ -103,6 +107,13 @@ Vector<Ref<Image>> PlaceholderTexture3D::get_data() const { return Vector<Ref<Image>>(); } +RID PlaceholderTexture3D::get_rid() const { + if (rid.is_null()) { + rid = RenderingServer::get_singleton()->texture_3d_placeholder_create(); + } + return rid; +} + void PlaceholderTexture3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture3D::set_size); ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTexture3D::get_size); @@ -110,11 +121,12 @@ void PlaceholderTexture3D::_bind_methods() { } PlaceholderTexture3D::PlaceholderTexture3D() { - rid = RS::get_singleton()->texture_3d_placeholder_create(); } PlaceholderTexture3D::~PlaceholderTexture3D() { ERR_FAIL_NULL(RenderingServer::get_singleton()); - RS::get_singleton()->free(rid); + if (rid.is_valid()) { + RS::get_singleton()->free(rid); + } } ///////////////////////////////////////////////// @@ -159,6 +171,13 @@ Ref<Image> PlaceholderTextureLayered::get_layer_data(int p_layer) const { return Ref<Image>(); } +RID PlaceholderTextureLayered::get_rid() const { + if (rid.is_null()) { + rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type)); + } + return rid; +} + void PlaceholderTextureLayered::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTextureLayered::set_size); ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTextureLayered::get_size); @@ -169,9 +188,10 @@ void PlaceholderTextureLayered::_bind_methods() { PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) { layered_type = p_type; - rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type)); } PlaceholderTextureLayered::~PlaceholderTextureLayered() { ERR_FAIL_NULL(RenderingServer::get_singleton()); - RS::get_singleton()->free(rid); + if (rid.is_valid()) { + RS::get_singleton()->free(rid); + } } diff --git a/scene/resources/placeholder_textures.h b/scene/resources/placeholder_textures.h index 116ed0f0f0..300d641cc3 100644 --- a/scene/resources/placeholder_textures.h +++ b/scene/resources/placeholder_textures.h @@ -36,7 +36,7 @@ class PlaceholderTexture2D : public Texture2D { GDCLASS(PlaceholderTexture2D, Texture2D) - RID rid; + mutable RID rid; Size2 size = Size2(1, 1); protected: @@ -59,7 +59,7 @@ public: class PlaceholderTexture3D : public Texture3D { GDCLASS(PlaceholderTexture3D, Texture3D) - RID rid; + mutable RID rid; Vector3i size = Vector3i(1, 1, 1); protected: @@ -74,6 +74,7 @@ public: virtual int get_depth() const override; virtual bool has_mipmaps() const override; virtual Vector<Ref<Image>> get_data() const override; + virtual RID get_rid() const override; PlaceholderTexture3D(); ~PlaceholderTexture3D(); @@ -82,7 +83,7 @@ public: class PlaceholderTextureLayered : public TextureLayered { GDCLASS(PlaceholderTextureLayered, TextureLayered) - RID rid; + mutable RID rid; Size2i size = Size2i(1, 1); int layers = 1; LayeredType layered_type = LAYERED_TYPE_2D_ARRAY; @@ -101,6 +102,7 @@ public: virtual int get_layers() const override; virtual bool has_mipmaps() const override; virtual Ref<Image> get_layer_data(int p_layer) const override; + virtual RID get_rid() const override; PlaceholderTextureLayered(LayeredType p_type); ~PlaceholderTextureLayered(); diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp index ee1a47c338..dc8545426f 100644 --- a/scene/resources/video_stream.cpp +++ b/scene/resources/video_stream.cpp @@ -75,7 +75,7 @@ bool VideoStreamPlayback::is_playing() const { } void VideoStreamPlayback::set_paused(bool p_paused) { - GDVIRTUAL_CALL(_is_playing, p_paused); + GDVIRTUAL_CALL(_set_paused, p_paused); } bool VideoStreamPlayback::is_paused() const { diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index ef09552603..7bca1dc3be 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -779,6 +779,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con scene_state.used_screen_texture = false; scene_state.used_normal_texture = false; scene_state.used_depth_texture = false; + scene_state.used_lightmap = false; } uint32_t lightmap_captures_used = 0; @@ -994,6 +995,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con if (uses_lightmap) { surf->sort.uses_lightmap = 1; + scene_state.used_lightmap = true; } if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_SUBSURFACE_SCATTERING) { @@ -1628,6 +1630,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co if (rb->get_use_taa() || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) { color_pass_flags |= COLOR_PASS_FLAG_MOTION_VECTORS; + scene_shader.enable_advanced_shader_group(); } if (p_render_data->voxel_gi_instances->size() > 0) { @@ -1647,6 +1650,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co if (p_render_data->scene_data->view_count > 1) { color_pass_flags |= COLOR_PASS_FLAG_MULTIVIEW; + // Try enabling here in case is_xr_enabled() returns false. + scene_shader.shader.enable_group(SceneShaderForwardClustered::SHADER_GROUP_MULTIVIEW); } color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); @@ -1712,6 +1717,11 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR; color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); } + + if (using_sss || using_separate_specular || scene_state.used_lightmap || using_voxelgi) { + scene_shader.enable_advanced_shader_group(p_render_data->scene_data->view_count > 1); + } + RID radiance_texture; bool draw_sky = false; bool draw_sky_fog_only = false; @@ -2484,6 +2494,8 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform render_data.cluster_max_elements = 32; render_data.instances = &p_instances; + scene_shader.enable_advanced_shader_group(); + _update_render_base_uniform_set(); _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2533,6 +2545,8 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance render_data.cluster_max_elements = 32; render_data.instances = &p_instances; + scene_shader.enable_advanced_shader_group(); + _update_render_base_uniform_set(); _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -3321,6 +3335,10 @@ void RenderForwardClustered::sdfgi_update(const Ref<RenderSceneBuffers> &p_rende return; } + // Ensure advanced shaders are available if SDFGI is used. + // Call here as this is the first entry point for SDFGI. + scene_shader.enable_advanced_shader_group(); + static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 }; uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge]; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 155e8b4d19..a89c77c652 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -321,6 +321,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { bool used_normal_texture = false; bool used_depth_texture = false; bool used_sss = false; + bool used_lightmap = false; struct ShadowPass { uint32_t element_from; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 377aab1354..298a6c0752 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -301,7 +301,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { if (k == PIPELINE_VERSION_COLOR_PASS) { for (int l = 0; l < PIPELINE_COLOR_PASS_FLAG_COUNT; l++) { - if (!shader_singleton->valid_color_pass_pipelines.has(l)) { + if (!shader_singleton->valid_color_pass_pipelines[l]) { continue; } @@ -476,16 +476,16 @@ void SceneShaderForwardClustered::init(const String p_defines) { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); { - Vector<String> shader_versions; - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_DEPTH_PASS_DP - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); // SHADER_VERSION_DEPTH_PASS_WITH_SDF - shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW - shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW - shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW + Vector<ShaderRD::VariantDefine> shader_versions; + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n", true)); // SHADER_VERSION_DEPTH_PASS + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n", true)); // SHADER_VERSION_DEPTH_PASS_DP + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", true)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_SDF + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n", false)); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW + shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW Vector<String> color_pass_flags = { "\n#define MODE_SEPARATE_SPECULAR\n", // SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR @@ -501,54 +501,38 @@ void SceneShaderForwardClustered::init(const String p_defines) { version += color_pass_flags[j]; } } - shader_versions.push_back(version); + + // Assign a group based on what features this pass contains. + ShaderGroup group = SHADER_GROUP_BASE; + bool advanced_group = (i & SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR) || (i & SHADER_COLOR_PASS_FLAG_LIGHTMAP) || (i & SHADER_COLOR_PASS_FLAG_MOTION_VECTORS); + bool multiview_group = i & SHADER_COLOR_PASS_FLAG_MULTIVIEW; + if (advanced_group && multiview_group) { + group = SHADER_GROUP_ADVANCED_MULTIVIEW; + } else if (advanced_group) { + group = SHADER_GROUP_ADVANCED; + } else if (multiview_group) { + group = SHADER_GROUP_MULTIVIEW; + } + + shader_versions.push_back(ShaderRD::VariantDefine(group, version, false)); } shader.initialize(shader_versions, p_defines); - if (!RendererCompositorRD::get_singleton()->is_xr_enabled()) { - shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_MULTIVIEW, false); - shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW, false); - shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW, false); - - // Disable Color Passes - for (int i = 0; i < SHADER_COLOR_PASS_FLAG_COUNT; i++) { - // Selectively disable any shader pass that includes Multiview. - if ((i & SHADER_COLOR_PASS_FLAG_MULTIVIEW)) { - shader.set_variant_enabled(i + SHADER_VERSION_COLOR_PASS, false); - } - } + if (RendererCompositorRD::get_singleton()->is_xr_enabled()) { + shader.enable_group(SHADER_GROUP_MULTIVIEW); } } - valid_color_pass_pipelines.insert(0); - - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MULTIVIEW); - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); - - valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS); + // Set flag to true if a combination is valid. + // The only invalid combinations are those that include both TRANSPARENT and SEPARATE_SPECULAR. + for (int i = 0; i < PIPELINE_COLOR_PASS_FLAG_COUNT; i++) { + if ((i & PIPELINE_COLOR_PASS_FLAG_TRANSPARENT) && (i & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR)) { + valid_color_pass_pipelines[i] = false; + } else { + valid_color_pass_pipelines[i] = true; + } + } material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_shader_funcs); material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_material_funcs); @@ -854,3 +838,11 @@ void SceneShaderForwardClustered::set_default_specialization_constants(const Vec } } } + +void SceneShaderForwardClustered::enable_advanced_shader_group(bool p_needs_multiview) { + if (p_needs_multiview || RendererCompositorRD::get_singleton()->is_xr_enabled()) { + shader.enable_group(SHADER_GROUP_ADVANCED_MULTIVIEW); + } else { + shader.enable_group(SHADER_GROUP_ADVANCED); + } +} diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index bdafc4c27e..761b74defa 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -41,6 +41,13 @@ private: static SceneShaderForwardClustered *singleton; public: + enum ShaderGroup { + SHADER_GROUP_BASE, // Always compiled at the beginning. + SHADER_GROUP_ADVANCED, + SHADER_GROUP_MULTIVIEW, + SHADER_GROUP_ADVANCED_MULTIVIEW, + }; + enum ShaderVersion { SHADER_VERSION_DEPTH_PASS, SHADER_VERSION_DEPTH_PASS_DP, @@ -78,8 +85,8 @@ public: }; enum PipelineColorPassFlags { - PIPELINE_COLOR_PASS_FLAG_TRANSPARENT = 1 << 0, - PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1, + PIPELINE_COLOR_PASS_FLAG_TRANSPARENT = 1 << 0, // Can't combine with SEPARATE_SPECULAR. + PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1, // Can't combine with TRANSPARENT. PIPELINE_COLOR_PASS_FLAG_LIGHTMAP = 1 << 2, PIPELINE_COLOR_PASS_FLAG_MULTIVIEW = 1 << 3, PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 4, @@ -242,12 +249,13 @@ public: ShaderData *debug_shadow_splits_material_shader_ptr = nullptr; Vector<RD::PipelineSpecializationConstant> default_specialization_constants; - HashSet<uint32_t> valid_color_pass_pipelines; + bool valid_color_pass_pipelines[PIPELINE_COLOR_PASS_FLAG_COUNT]; SceneShaderForwardClustered(); ~SceneShaderForwardClustered(); void init(const String p_defines); void set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants); + void enable_advanced_shader_group(bool p_needs_multiview = false); }; } // namespace RendererSceneRenderImplementation diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 077bf4e3b7..2053c3a9a6 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -781,24 +781,20 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - /* - if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment)) { + if (environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } - */ } break; case RS::ENV_BG_COLOR: { clear_color = environment_get_bg_color(p_render_data->environment); clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - /* - if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment)) { + if (environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } - */ } break; case RS::ENV_BG_SKY: { draw_sky = true; diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp index f3595c5917..c00e5f8b5e 100644 --- a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp +++ b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp @@ -89,7 +89,7 @@ void PipelineCacheRD::setup(RID p_shader, RD::RenderPrimitive p_primitive, const ERR_FAIL_COND(p_shader.is_null()); _clear(); shader = p_shader; - input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(p_shader); + input_mask = 0; render_primitive = p_primitive; rasterization_state = p_rasterization_state; multisample_state = p_multisample; diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.h b/servers/rendering/renderer_rd/pipeline_cache_rd.h index 58c1278312..52877109f7 100644 --- a/servers/rendering/renderer_rd/pipeline_cache_rd.h +++ b/servers/rendering/renderer_rd/pipeline_cache_rd.h @@ -91,7 +91,11 @@ public: return result; } - _FORCE_INLINE_ uint32_t get_vertex_input_mask() const { + _FORCE_INLINE_ uint32_t get_vertex_input_mask() { + if (input_mask == 0) { + ERR_FAIL_COND_V(shader.is_null(), 0); + input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader); + } return input_mask; } void clear(); diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index eaa84b7614..da5597751b 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -138,7 +138,7 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con RID ShaderRD::version_create() { //initialize() was never called - ERR_FAIL_COND_V(variant_defines.size() == 0, RID()); + ERR_FAIL_COND_V(group_to_variant_map.size() == 0, RID()); Version version; version.dirty = true; @@ -148,11 +148,20 @@ RID ShaderRD::version_create() { return version_owner.make_rid(version); } +void ShaderRD::_initialize_version(Version *p_version) { + _clear_version(p_version); + + p_version->valid = false; + p_version->dirty = false; + + p_version->variants = memnew_arr(RID, variant_defines.size()); +} + void ShaderRD::_clear_version(Version *p_version) { - //clear versions if they exist + // Clear versions if they exist. if (p_version->variants) { for (int i = 0; i < variant_defines.size(); i++) { - if (variants_enabled[i]) { + if (variants_enabled[i] && group_enabled[variant_defines[i].group]) { RD::get_singleton()->free(p_version->variants[i]); } } @@ -171,7 +180,7 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c case StageTemplate::Chunk::TYPE_VERSION_DEFINES: { builder.append("\n"); //make sure defines begin at newline builder.append(general_defines.get_data()); - builder.append(variant_defines[p_variant].get_data()); + builder.append(variant_defines[p_variant].text.get_data()); for (int j = 0; j < p_version->custom_defines.size(); j++) { builder.append(p_version->custom_defines[j].get_data()); } @@ -211,9 +220,11 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c } } -void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { - if (!variants_enabled[p_variant]) { - return; //variant is disabled, return +void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) { + uint32_t variant = group_to_variant_map[p_data->group][p_variant]; + + if (!variants_enabled[variant]) { + return; // Variant is disabled, return. } Vector<RD::ShaderStageSPIRVData> stages; @@ -227,7 +238,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { //vertex stage StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX]); + _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_VERTEX]); current_source = builder.as_string(); RD::ShaderStageSPIRVData stage; @@ -245,7 +256,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { current_stage = RD::SHADER_STAGE_FRAGMENT; StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT]); + _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_FRAGMENT]); current_source = builder.as_string(); RD::ShaderStageSPIRVData stage; @@ -263,7 +274,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { current_stage = RD::SHADER_STAGE_COMPUTE; StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_COMPUTE]); + _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_COMPUTE]); current_source = builder.as_string(); @@ -279,7 +290,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { if (!build_ok) { MutexLock lock(variant_set_mutex); //properly print the errors - ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(p_variant) + " (" + variant_defines[p_variant].get_data() + ")."); + ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(variant) + " (" + variant_defines[variant].text.get_data() + ")."); ERR_PRINT(error); #ifdef DEBUG_ENABLED @@ -288,15 +299,15 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { return; } - Vector<uint8_t> shader_data = RD::get_singleton()->shader_compile_binary_from_spirv(stages, name + ":" + itos(p_variant)); + Vector<uint8_t> shader_data = RD::get_singleton()->shader_compile_binary_from_spirv(stages, name + ":" + itos(variant)); ERR_FAIL_COND(shader_data.size() == 0); - RID shader = RD::get_singleton()->shader_create_from_bytecode(shader_data); { MutexLock lock(variant_set_mutex); - p_version->variants[p_variant] = shader; - p_version->variant_data[p_variant] = shader_data; + + p_data->version->variants[variant] = RD::get_singleton()->shader_create_from_bytecode(shader_data, p_data->version->variants[variant]); + p_data->version->variant_data[variant] = shader_data; } } @@ -384,9 +395,9 @@ String ShaderRD::_version_get_sha1(Version *p_version) const { static const char *shader_file_header = "GDSC"; static const uint32_t cache_file_version = 3; -bool ShaderRD::_load_from_cache(Version *p_version) { +bool ShaderRD::_load_from_cache(Version *p_version, int p_group) { String sha1 = _version_get_sha1(p_version); - String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache"; + String path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + ".cache"; Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ); if (f.is_null()) { @@ -404,12 +415,13 @@ bool ShaderRD::_load_from_cache(Version *p_version) { uint32_t variant_count = f->get_32(); - ERR_FAIL_COND_V(variant_count != (uint32_t)variant_defines.size(), false); //should not happen but check + ERR_FAIL_COND_V(variant_count != (uint32_t)group_to_variant_map[p_group].size(), false); //should not happen but check for (uint32_t i = 0; i < variant_count; i++) { + int variant_id = group_to_variant_map[p_group][i]; uint32_t variant_size = f->get_32(); - ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[i], false); - if (!variants_enabled[i]) { + ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[variant_id], false); + if (!variants_enabled[variant_id]) { continue; } Vector<uint8_t> variant_bytes; @@ -419,25 +431,28 @@ bool ShaderRD::_load_from_cache(Version *p_version) { ERR_FAIL_COND_V(br != variant_size, false); - p_version->variant_data[i] = variant_bytes; + p_version->variant_data[variant_id] = variant_bytes; } for (uint32_t i = 0; i < variant_count; i++) { - if (!variants_enabled[i]) { + int variant_id = group_to_variant_map[p_group][i]; + if (!variants_enabled[variant_id]) { MutexLock lock(variant_set_mutex); - p_version->variants[i] = RID(); + p_version->variants[variant_id] = RID(); continue; } - RID shader = RD::get_singleton()->shader_create_from_bytecode(p_version->variant_data[i]); - if (shader.is_null()) { - for (uint32_t j = 0; j < i; j++) { - RD::get_singleton()->free(p_version->variants[i]); - } - ERR_FAIL_COND_V(shader.is_null(), false); - } { MutexLock lock(variant_set_mutex); - p_version->variants[i] = shader; + RID shader = RD::get_singleton()->shader_create_from_bytecode(p_version->variant_data[variant_id], p_version->variants[variant_id]); + if (shader.is_null()) { + for (uint32_t j = 0; j < i; j++) { + int variant_free_id = group_to_variant_map[p_group][j]; + RD::get_singleton()->free(p_version->variants[variant_free_id]); + } + ERR_FAIL_COND_V(shader.is_null(), false); + } + + p_version->variants[variant_id] = shader; } } @@ -447,66 +462,85 @@ bool ShaderRD::_load_from_cache(Version *p_version) { return true; } -void ShaderRD::_save_to_cache(Version *p_version) { +void ShaderRD::_save_to_cache(Version *p_version, int p_group) { String sha1 = _version_get_sha1(p_version); - String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache"; + String path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + ".cache"; Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE); ERR_FAIL_COND(f.is_null()); f->store_buffer((const uint8_t *)shader_file_header, 4); - f->store_32(cache_file_version); //file version - uint32_t variant_count = variant_defines.size(); - f->store_32(variant_count); //variant count - + f->store_32(cache_file_version); // File version. + uint32_t variant_count = group_to_variant_map[p_group].size(); + f->store_32(variant_count); // Variant count. for (uint32_t i = 0; i < variant_count; i++) { - f->store_32(p_version->variant_data[i].size()); //stage count - f->store_buffer(p_version->variant_data[i].ptr(), p_version->variant_data[i].size()); + int variant_id = group_to_variant_map[p_group][i]; + f->store_32(p_version->variant_data[variant_id].size()); // Stage count. + f->store_buffer(p_version->variant_data[variant_id].ptr(), p_version->variant_data[variant_id].size()); } } -void ShaderRD::_compile_version(Version *p_version) { - _clear_version(p_version); +void ShaderRD::_allocate_placeholders(Version *p_version, int p_group) { + for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) { + int variant_id = group_to_variant_map[p_group][i]; + RID shader = RD::get_singleton()->shader_create_placeholder(); + { + MutexLock lock(variant_set_mutex); + p_version->variants[variant_id] = shader; + } + } +} - p_version->valid = false; - p_version->dirty = false; +// Try to compile all variants for a given group. +// Will skip variants that are disabled. +void ShaderRD::_compile_version(Version *p_version, int p_group) { + if (!group_enabled[p_group]) { + return; + } - p_version->variants = memnew_arr(RID, variant_defines.size()); typedef Vector<uint8_t> ShaderStageData; p_version->variant_data = memnew_arr(ShaderStageData, variant_defines.size()); + p_version->dirty = false; + if (shader_cache_dir_valid) { - if (_load_from_cache(p_version)) { + if (_load_from_cache(p_version, p_group)) { + print_line("loaded from cache!"); return; } } -#if 1 + CompileData compile_data; + compile_data.version = p_version; + compile_data.group = p_group; - WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, p_version, variant_defines.size(), -1, true, SNAME("ShaderCompilation")); +#if 1 + WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, &compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation")); WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); #else - for (int i = 0; i < variant_defines.size(); i++) { - _compile_variant(i, p_version); + for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) { + _compile_variant(i, &compile_data); } #endif bool all_valid = true; - for (int i = 0; i < variant_defines.size(); i++) { - if (!variants_enabled[i]) { - continue; //disabled + + for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) { + int variant_id = group_to_variant_map[p_group][i]; + if (!variants_enabled[variant_id]) { + continue; // Disabled. } - if (p_version->variants[i].is_null()) { + if (p_version->variants[variant_id].is_null()) { all_valid = false; break; } } if (!all_valid) { - //clear versions if they exist + // Clear versions if they exist. for (int i = 0; i < variant_defines.size(); i++) { - if (!variants_enabled[i]) { - continue; //disabled + if (!variants_enabled[i] || !group_enabled[variant_defines[i].group]) { + continue; // Disabled. } if (!p_version->variants[i].is_null()) { RD::get_singleton()->free(p_version->variants[i]); @@ -520,8 +554,8 @@ void ShaderRD::_compile_version(Version *p_version) { p_version->variant_data = nullptr; return; } else if (shader_cache_dir_valid) { - //save shader cache - _save_to_cache(p_version); + // Save shader cache. + _save_to_cache(p_version, p_group); } memdelete_arr(p_version->variant_data); //clear stages @@ -550,7 +584,14 @@ void ShaderRD::version_set_code(RID p_version, const HashMap<String, String> &p_ version->dirty = true; if (version->initialize_needed) { - _compile_version(version); + _initialize_version(version); + for (int i = 0; i < group_enabled.size(); i++) { + if (!group_enabled[i]) { + _allocate_placeholders(version, i); + continue; + } + _compile_version(version, i); + } version->initialize_needed = false; } } @@ -576,7 +617,14 @@ void ShaderRD::version_set_compute_code(RID p_version, const HashMap<String, Str version->dirty = true; if (version->initialize_needed) { - _compile_version(version); + _initialize_version(version); + for (int i = 0; i < group_enabled.size(); i++) { + if (!group_enabled[i]) { + _allocate_placeholders(version, i); + continue; + } + _compile_version(version, i); + } version->initialize_needed = false; } } @@ -586,7 +634,14 @@ bool ShaderRD::version_is_valid(RID p_version) { ERR_FAIL_COND_V(!version, false); if (version->dirty) { - _compile_version(version); + _initialize_version(version); + for (int i = 0; i < group_enabled.size(); i++) { + if (!group_enabled[i]) { + _allocate_placeholders(version, i); + continue; + } + _compile_version(version, i); + } } return version->valid; @@ -615,6 +670,29 @@ bool ShaderRD::is_variant_enabled(int p_variant) const { return variants_enabled[p_variant]; } +void ShaderRD::enable_group(int p_group) { + ERR_FAIL_INDEX(p_group, group_enabled.size()); + + if (group_enabled[p_group]) { + // Group already enabled, do nothing. + return; + } + + group_enabled.write[p_group] = true; + + // Compile all versions again to include the new group. + List<RID> all_versions; + version_owner.get_owned_list(&all_versions); + for (int i = 0; i < all_versions.size(); i++) { + Version *version = version_owner.get_or_null(all_versions[i]); + _compile_version(version, p_group); + } +} + +bool ShaderRD::is_group_enabled(int p_group) const { + return group_enabled[p_group]; +} + bool ShaderRD::shader_cache_cleanup_on_start = false; ShaderRD::ShaderRD() { @@ -639,24 +717,38 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String general_defines = p_general_defines.utf8(); + // When initialized this way, there is just one group and its always enabled. + group_to_variant_map.insert(0, LocalVector<int>{}); + group_enabled.push_back(true); + for (int i = 0; i < p_variant_defines.size(); i++) { - variant_defines.push_back(p_variant_defines[i].utf8()); + variant_defines.push_back(VariantDefine(0, p_variant_defines[i], true)); variants_enabled.push_back(true); + group_to_variant_map[0].push_back(i); } if (!shader_cache_dir.is_empty()) { + group_sha256.resize(1); + _initialize_cache(); + } +} + +void ShaderRD::_initialize_cache() { + for (const KeyValue<int, LocalVector<int>> &E : group_to_variant_map) { StringBuilder hash_build; hash_build.append("[base_hash]"); hash_build.append(base_sha256); hash_build.append("[general_defines]"); hash_build.append(general_defines.get_data()); - for (int i = 0; i < variant_defines.size(); i++) { - hash_build.append("[variant_defines:" + itos(i) + "]"); - hash_build.append(variant_defines[i].get_data()); + hash_build.append("[group_id]"); + hash_build.append(itos(E.key)); + for (uint32_t i = 0; i < E.value.size(); i++) { + hash_build.append("[variant_defines:" + itos(E.value[i]) + "]"); + hash_build.append(variant_defines[E.value[i]].text.get_data()); } - base_sha256 = hash_build.as_string().sha256_text(); + group_sha256[E.key] = hash_build.as_string().sha256_text(); Ref<DirAccess> d = DirAccess::open(shader_cache_dir); ERR_FAIL_COND(d.is_null()); @@ -666,17 +758,58 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String d->change_dir(name); } - //erase other versions? + // Erase other versions? if (shader_cache_cleanup_on_start) { } // - if (d->change_dir(base_sha256) != OK) { - Error err = d->make_dir(base_sha256); + if (d->change_dir(group_sha256[E.key]) != OK) { + Error err = d->make_dir(group_sha256[E.key]); ERR_FAIL_COND(err != OK); } shader_cache_dir_valid = true; - print_verbose("Shader '" + name + "' SHA256: " + base_sha256); + print_verbose("Shader '" + name + "' (group " + itos(E.key) + ") SHA256: " + group_sha256[E.key]); + } +} + +// Same as above, but allows specifying shader compilation groups. +void ShaderRD::initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines) { + ERR_FAIL_COND(variant_defines.size()); + ERR_FAIL_COND(p_variant_defines.size() == 0); + + general_defines = p_general_defines.utf8(); + + int max_group_id = 0; + + for (int i = 0; i < p_variant_defines.size(); i++) { + // Fill variant array. + variant_defines.push_back(p_variant_defines[i]); + variants_enabled.push_back(true); + + // Map variant array index to group id, so we can iterate over groups later. + if (!group_to_variant_map.has(p_variant_defines[i].group)) { + group_to_variant_map.insert(p_variant_defines[i].group, LocalVector<int>{}); + } + group_to_variant_map[p_variant_defines[i].group].push_back(i); + + // Track max size. + if (p_variant_defines[i].group > max_group_id) { + max_group_id = p_variant_defines[i].group; + } + } + + // Set all to groups to false, then enable those that should be default. + group_enabled.resize_zeroed(max_group_id + 1); + bool *enabled_ptr = group_enabled.ptrw(); + for (int i = 0; i < p_variant_defines.size(); i++) { + if (p_variant_defines[i].default_enabled) { + enabled_ptr[p_variant_defines[i].group] = true; + } + } + + if (!shader_cache_dir.is_empty()) { + group_sha256.resize(max_group_id + 1); + _initialize_cache(); } } diff --git a/servers/rendering/renderer_rd/shader_rd.h b/servers/rendering/renderer_rd/shader_rd.h index d0871ca16c..01eb99f7a2 100644 --- a/servers/rendering/renderer_rd/shader_rd.h +++ b/servers/rendering/renderer_rd/shader_rd.h @@ -41,10 +41,26 @@ #include "servers/rendering_server.h" class ShaderRD { +public: + struct VariantDefine { + int group = 0; + CharString text; + bool default_enabled = true; + VariantDefine(){}; + VariantDefine(int p_group, const String &p_text, bool p_default_enabled) { + group = p_group; + default_enabled = p_default_enabled; + text = p_text.utf8(); + } + }; + +private: //versions CharString general_defines; - Vector<CharString> variant_defines; + Vector<VariantDefine> variant_defines; Vector<bool> variants_enabled; + HashMap<int, LocalVector<int>> group_to_variant_map; + Vector<bool> group_enabled; struct Version { CharString uniforms; @@ -55,7 +71,7 @@ class ShaderRD { Vector<CharString> custom_defines; Vector<uint8_t> *variant_data = nullptr; - RID *variants = nullptr; //same size as version defines + RID *variants = nullptr; // Same size as variant defines. bool valid; bool dirty; @@ -64,10 +80,17 @@ class ShaderRD { Mutex variant_set_mutex; - void _compile_variant(uint32_t p_variant, Version *p_version); + struct CompileData { + Version *version; + int group = 0; + }; + + void _compile_variant(uint32_t p_variant, const CompileData *p_data); + void _initialize_version(Version *p_version); void _clear_version(Version *p_version); - void _compile_version(Version *p_version); + void _compile_version(Version *p_version, int p_group); + void _allocate_placeholders(Version *p_version, int p_group); RID_Owner<Version> version_owner; @@ -97,6 +120,7 @@ class ShaderRD { CharString base_compute_defines; String base_sha256; + LocalVector<String> group_sha256; static String shader_cache_dir; static bool shader_cache_cleanup_on_start; @@ -119,8 +143,9 @@ class ShaderRD { void _add_stage(const char *p_code, StageType p_stage_type); String _version_get_sha1(Version *p_version) const; - bool _load_from_cache(Version *p_version); - void _save_to_cache(Version *p_version); + bool _load_from_cache(Version *p_version, int p_group); + void _save_to_cache(Version *p_version, int p_group); + void _initialize_cache(); protected: ShaderRD(); @@ -140,7 +165,14 @@ public: ERR_FAIL_COND_V(!version, RID()); if (version->dirty) { - _compile_version(version); + _initialize_version(version); + for (int i = 0; i < group_enabled.size(); i++) { + if (!group_enabled[i]) { + _allocate_placeholders(version, i); + continue; + } + _compile_version(version, i); + } } if (!version->valid) { @@ -154,9 +186,14 @@ public: bool version_free(RID p_version); + // Enable/disable variants for things that you know won't be used at engine initialization time . void set_variant_enabled(int p_variant, bool p_enabled); bool is_variant_enabled(int p_variant) const; + // Enable/disable groups for things that might be enabled at run time. + void enable_group(int p_group); + bool is_group_enabled(int p_group) const; + static void set_shader_cache_dir(const String &p_dir); static void set_shader_cache_save_compressed(bool p_enable); static void set_shader_cache_save_compressed_zstd(bool p_enable); @@ -165,6 +202,8 @@ public: RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version); void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = ""); + void initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines = ""); + virtual ~ShaderRD(); }; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 7bfa23bfa5..1b0a3e9d0f 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -754,7 +754,9 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("shader_compile_spirv_from_source", "shader_source", "allow_cache"), &RenderingDevice::_shader_compile_spirv_from_source, DEFVAL(true)); ClassDB::bind_method(D_METHOD("shader_compile_binary_from_spirv", "spirv_data", "name"), &RenderingDevice::_shader_compile_binary_from_spirv, DEFVAL("")); ClassDB::bind_method(D_METHOD("shader_create_from_spirv", "spirv_data", "name"), &RenderingDevice::_shader_create_from_spirv, DEFVAL("")); - ClassDB::bind_method(D_METHOD("shader_create_from_bytecode", "binary_data"), &RenderingDevice::shader_create_from_bytecode); + ClassDB::bind_method(D_METHOD("shader_create_from_bytecode", "binary_data", "placeholder_rid"), &RenderingDevice::shader_create_from_bytecode, DEFVAL(RID())); + ClassDB::bind_method(D_METHOD("shader_create_placeholder"), &RenderingDevice::shader_create_placeholder); + ClassDB::bind_method(D_METHOD("shader_get_vertex_input_attribute_mask", "shader"), &RenderingDevice::shader_get_vertex_input_attribute_mask); ClassDB::bind_method(D_METHOD("uniform_buffer_create", "size_bytes", "data"), &RenderingDevice::uniform_buffer_create, DEFVAL(Vector<uint8_t>())); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 5edeb109e2..58da3df577 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -394,13 +394,13 @@ public: enum BarrierMask { BARRIER_MASK_VERTEX = 1, - BARRIER_MASK_FRAGMENT = 2, - BARRIER_MASK_COMPUTE = 4, - BARRIER_MASK_TRANSFER = 8, + BARRIER_MASK_FRAGMENT = 8, + BARRIER_MASK_COMPUTE = 2, + BARRIER_MASK_TRANSFER = 4, - BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 3, - BARRIER_MASK_ALL_BARRIERS = BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER, // 7 - BARRIER_MASK_NO_BARRIER = 16, + BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 9, + BARRIER_MASK_ALL_BARRIERS = 0x7FFF, // all flags set + BARRIER_MASK_NO_BARRIER = 0x8000, }; /*****************/ @@ -734,7 +734,8 @@ public: virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "") = 0; virtual RID shader_create_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = ""); - virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary) = 0; + virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID()) = 0; + virtual RID shader_create_placeholder() = 0; virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader) = 0; diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index a117f518de..92ab166ae8 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -115,6 +115,16 @@ TEST_CASE("[Image] Saving and loading") { image_bmp->load_bmp_from_buffer(data_bmp) == OK, "The BMP image should load successfully."); + // Load DDS + Ref<Image> image_dds = memnew(Image()); + Ref<FileAccess> f_dds = FileAccess::open(TestUtils::get_data_path("images/icon.dds"), FileAccess::READ, &err); + PackedByteArray data_dds; + data_dds.resize(f_dds->get_length() + 1); + f_dds->get_buffer(data_dds.ptrw(), f_dds->get_length()); + CHECK_MESSAGE( + image_dds->load_dds_from_buffer(data_dds) == OK, + "The DDS image should load successfully."); + // Load JPG Ref<Image> image_jpg = memnew(Image()); Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err); diff --git a/tests/data/images/icon.dds b/tests/data/images/icon.dds Binary files differnew file mode 100644 index 0000000000..8a9de402cb --- /dev/null +++ b/tests/data/images/icon.dds |