summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--SConstruct12
-rw-r--r--core/extension/gdextension_interface.cpp20
-rw-r--r--core/extension/gdextension_interface.h13
-rw-r--r--doc/classes/ProjectSettings.xml3
-rw-r--r--doc/classes/RenderingServer.xml9
-rw-r--r--doc/classes/Viewport.xml4
-rw-r--r--drivers/gles3/shaders/canvas_uniforms_inc.glsl2
-rw-r--r--drivers/gles3/storage/texture_storage.h2
-rw-r--r--editor/directory_create_dialog.cpp40
-rw-r--r--editor/directory_create_dialog.h10
-rw-r--r--editor/editor_inspector.cpp56
-rw-r--r--editor/editor_inspector.h6
-rw-r--r--editor/editor_node.cpp3
-rw-r--r--editor/editor_themes.cpp3
-rw-r--r--editor/gui/editor_validation_panel.cpp134
-rw-r--r--editor/gui/editor_validation_panel.h88
-rw-r--r--editor/icons/Camera2D.svg2
-rw-r--r--editor/icons/Camera3D.svg2
-rw-r--r--editor/icons/CameraAttributes.svg2
-rw-r--r--editor/icons/CameraAttributesPhysical.svg1
-rw-r--r--editor/icons/CameraAttributesPractical.svg1
-rw-r--r--editor/icons/CameraTexture.svg2
-rw-r--r--editor/icons/GizmoCamera3D.svg1
-rw-r--r--editor/icons/XRCamera3D.svg2
-rw-r--r--editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp3
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp3
-rw-r--r--editor/scene_create_dialog.cpp90
-rw-r--r--editor/scene_create_dialog.h14
-rw-r--r--editor/script_create_dialog.cpp147
-rw-r--r--editor/script_create_dialog.h18
-rw-r--r--editor/shader_create_dialog.cpp104
-rw-r--r--editor/shader_create_dialog.h15
-rw-r--r--modules/noise/noise_texture_3d.cpp4
-rw-r--r--modules/noise/noise_texture_3d.h2
-rw-r--r--scene/2d/area_2d.cpp2
-rw-r--r--scene/2d/audio_stream_player_2d.cpp6
-rw-r--r--scene/2d/audio_stream_player_2d.h3
-rw-r--r--scene/3d/area_3d.cpp4
-rw-r--r--scene/3d/area_3d.h5
-rw-r--r--scene/3d/audio_stream_player_3d.cpp3
-rw-r--r--scene/audio/audio_stream_player.cpp2
-rw-r--r--scene/audio/audio_stream_player.h3
-rw-r--r--scene/gui/button.cpp7
-rw-r--r--scene/gui/color_picker.cpp48
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/option_button.cpp4
-rw-r--r--scene/gui/video_stream_player.cpp2
-rw-r--r--scene/main/scene_tree.cpp3
-rw-r--r--scene/main/viewport.cpp27
-rw-r--r--scene/main/viewport.h4
-rw-r--r--scene/main/window.cpp16
-rw-r--r--scene/scene_string_names.cpp3
-rw-r--r--scene/scene_string_names.h2
-rw-r--r--scu_builders.py23
-rw-r--r--servers/audio_server.cpp7
-rw-r--r--servers/rendering/dummy/storage/texture_storage.h2
-rw-r--r--servers/rendering/renderer_rd/effects/tone_mapper.cpp26
-rw-r--r--servers/rendering/renderer_rd/effects/tone_mapper.h25
-rw-r--r--servers/rendering/renderer_rd/environment/fog.cpp2
-rw-r--r--servers/rendering/renderer_rd/environment/sky.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp3
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp12
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp73
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h3
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.h2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp4
-rw-r--r--servers/rendering/renderer_rd/shaders/blit.glsl13
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/copy.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/tonemap.glsl48
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp8
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.h4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp93
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.h7
-rw-r--r--servers/rendering/renderer_viewport.cpp11
-rw-r--r--servers/rendering/renderer_viewport.h3
-rw-r--r--servers/rendering/rendering_server_default.h1
-rw-r--r--servers/rendering/shader_preprocessor.cpp65
-rw-r--r--servers/rendering/shader_preprocessor.h1
-rw-r--r--servers/rendering/storage/texture_storage.h2
-rw-r--r--servers/rendering_server.cpp1
-rw-r--r--servers/rendering_server.h1
-rw-r--r--tests/servers/rendering/test_shader_preprocessor.h334
-rw-r--r--tests/test_main.cpp1
-rw-r--r--thirdparty/README.md4
-rw-r--r--thirdparty/libwebp/AUTHORS2
-rw-r--r--thirdparty/libwebp/sharpyuv/sharpyuv.c1
-rw-r--r--thirdparty/libwebp/sharpyuv/sharpyuv.h2
-rw-r--r--thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c1
-rw-r--r--thirdparty/libwebp/src/dec/tree_dec.c3
-rw-r--r--thirdparty/libwebp/src/dec/vp8_dec.c2
-rw-r--r--thirdparty/libwebp/src/dec/vp8i_dec.h2
-rw-r--r--thirdparty/libwebp/src/dec/webp_dec.c33
-rw-r--r--thirdparty/libwebp/src/demux/demux.c2
-rw-r--r--thirdparty/libwebp/src/dsp/alpha_processing.c1
-rw-r--r--thirdparty/libwebp/src/dsp/cost.c1
-rw-r--r--thirdparty/libwebp/src/dsp/cost_neon.c4
-rw-r--r--thirdparty/libwebp/src/dsp/cpu.c6
-rw-r--r--thirdparty/libwebp/src/dsp/cpu.h36
-rw-r--r--thirdparty/libwebp/src/dsp/dec.c1
-rw-r--r--thirdparty/libwebp/src/dsp/dec_neon.c4
-rw-r--r--thirdparty/libwebp/src/dsp/enc.c1
-rw-r--r--thirdparty/libwebp/src/dsp/enc_neon.c4
-rw-r--r--thirdparty/libwebp/src/dsp/enc_sse2.c242
-rw-r--r--thirdparty/libwebp/src/dsp/filters.c1
-rw-r--r--thirdparty/libwebp/src/dsp/lossless.c1
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_enc.c1
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_enc_neon.c2
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_neon.c2
-rw-r--r--thirdparty/libwebp/src/dsp/neon.h4
-rw-r--r--thirdparty/libwebp/src/dsp/quant.h2
-rw-r--r--thirdparty/libwebp/src/dsp/rescaler.c1
-rw-r--r--thirdparty/libwebp/src/dsp/ssim.c1
-rw-r--r--thirdparty/libwebp/src/dsp/upsampling.c1
-rw-r--r--thirdparty/libwebp/src/dsp/upsampling_neon.c2
-rw-r--r--thirdparty/libwebp/src/dsp/yuv.c1
-rw-r--r--thirdparty/libwebp/src/enc/alpha_enc.c20
-rw-r--r--thirdparty/libwebp/src/enc/analysis_enc.c4
-rw-r--r--thirdparty/libwebp/src/enc/backward_references_enc.c9
-rw-r--r--thirdparty/libwebp/src/enc/frame_enc.c8
-rw-r--r--thirdparty/libwebp/src/enc/picture_csp_enc.c5
-rw-r--r--thirdparty/libwebp/src/enc/picture_rescale_enc.c20
-rw-r--r--thirdparty/libwebp/src/enc/syntax_enc.c6
-rw-r--r--thirdparty/libwebp/src/enc/vp8i_enc.h2
-rw-r--r--thirdparty/libwebp/src/enc/vp8l_enc.c38
-rw-r--r--thirdparty/libwebp/src/enc/webp_enc.c10
-rw-r--r--thirdparty/libwebp/src/mux/muxi.h2
-rw-r--r--thirdparty/libwebp/src/mux/muxread.c7
-rw-r--r--thirdparty/libwebp/src/utils/bit_reader_utils.c3
-rw-r--r--thirdparty/libwebp/src/utils/bit_reader_utils.h3
-rw-r--r--thirdparty/libwebp/src/webp/decode.h5
-rw-r--r--thirdparty/miniupnpc/LICENSE2
-rw-r--r--thirdparty/miniupnpc/include/miniupnpc.h4
-rw-r--r--thirdparty/miniupnpc/src/miniupnpcstrings.h2
-rw-r--r--thirdparty/tinyexr/tinyexr.h96
141 files changed, 1691 insertions, 697 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 582784d78e..f884c690fa 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -302,7 +302,7 @@ License: CC0-1.0
Files: ./thirdparty/miniupnpc/
Comment: MiniUPnP Project
-Copyright: 2005-2022, Thomas Bernard
+Copyright: 2005-2023, Thomas Bernard
License: BSD-3-clause
Files: ./thirdparty/minizip/
diff --git a/SConstruct b/SConstruct
index f82c9c656e..6968967380 100644
--- a/SConstruct
+++ b/SConstruct
@@ -220,6 +220,7 @@ opts.Add(
)
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
+opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0")
# Thirdparty libraries
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
@@ -551,7 +552,16 @@ if selected_platform in platform_list:
# Run SCU file generation script if in a SCU build.
if env["scu_build"]:
- methods.set_scu_folders(scu_builders.generate_scu_files(env["verbose"], env_base.dev_build == False))
+ max_includes_per_scu = 8
+ if env_base.dev_build == True:
+ max_includes_per_scu = 1024
+
+ read_scu_limit = int(env["scu_limit"])
+ read_scu_limit = max(0, min(read_scu_limit, 1024))
+ if read_scu_limit != 0:
+ max_includes_per_scu = read_scu_limit
+
+ methods.set_scu_folders(scu_builders.generate_scu_files(env["verbose"], max_includes_per_scu))
# Must happen after the flags' definition, as configure is when most flags
# are actually handled to change compile options, etc.
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 7ef956a470..b4541de8fe 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -1048,6 +1048,25 @@ static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDE
return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension);
}
+static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) {
+ if (!p_object || !p_language) {
+ return nullptr;
+ }
+
+ const Object *o = (const Object *)p_object;
+ ScriptInstanceExtension *script_instance_extension = reinterpret_cast<ScriptInstanceExtension *>(o->get_script_instance());
+ if (!script_instance_extension) {
+ return nullptr;
+ }
+
+ const ScriptLanguage *language = script_instance_extension->get_language();
+ if (language != p_language) {
+ return nullptr;
+ }
+
+ return script_instance_extension->instance;
+}
+
static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname);
@@ -1216,6 +1235,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(ref_get_object);
REGISTER_INTERFACE_FUNC(ref_set_object);
REGISTER_INTERFACE_FUNC(script_instance_create);
+ REGISTER_INTERFACE_FUNC(object_get_script_instance);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 6c05f3988b..cfc21473d6 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -2126,6 +2126,19 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte
*/
typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
+/**
+ * @name object_get_script_instance
+ * @since 4.2
+ *
+ * Get the script instance data attached to this object.
+ *
+ * @param p_object A pointer to the Object.
+ * @param p_language A pointer to the language expected for this script instance.
+ *
+ * @return A GDExtensionScriptInstanceDataPtr that was attached to this object as part of script_instance_create.
+ */
+typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language);
+
/* INTERFACE: ClassDB */
/**
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index de667a02e6..40a475851d 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2653,6 +2653,9 @@
<member name="rendering/textures/webp_compression/lossless_compression_factor" type="float" setter="" getter="" default="25">
The default compression factor for lossless WebP. Decompression speed is mostly unaffected by the compression factor. Supported values are 0 to 100.
</member>
+ <member name="rendering/viewport/hdr_2d" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], enables [member Viewport.use_hdr_2d] on the root viewport. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow).
+ </member>
<member name="rendering/viewport/transparent_background" type="bool" setter="" getter="" default="false">
If [code]true[/code], enables [member Viewport.transparent_bg] on the root viewport. This allows per-pixel transparency to be effective after also enabling [member display/window/size/transparent] and [member display/window/per_pixel_transparency/allowed].
</member>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index a36afc7ff1..bb4a0f8a16 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3756,6 +3756,15 @@
If [code]true[/code], enables debanding on the specified viewport. Equivalent to [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding].
</description>
</method>
+ <method name="viewport_set_use_hdr_2d">
+ <return type="void" />
+ <param index="0" name="viewport" type="RID" />
+ <param index="1" name="enabled" type="bool" />
+ <description>
+ If [code]true[/code], 2D rendering will use a high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be a [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be a [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. This setting has the same effect as [member Viewport.use_hdr_2d].
+ [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons.
+ </description>
+ </method>
<method name="viewport_set_use_occlusion_culling">
<return type="void" />
<param index="0" name="viewport" type="RID" />
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 685a5e113b..98836db157 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -348,6 +348,10 @@
If [code]true[/code], uses a fast post-processing filter to make banding significantly less visible in 3D. 2D rendering is [i]not[/i] affected by debanding unless the [member Environment.background_mode] is [constant Environment.BG_CANVAS]. See also [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding].
In some cases, debanding may introduce a slightly noticeable dithering pattern. It's recommended to enable debanding only when actually needed since the dithering pattern will make lossless-compressed screenshots larger.
</member>
+ <member name="use_hdr_2d" type="bool" setter="set_use_hdr_2d" getter="is_using_hdr_2d" default="false">
+ If [code]true[/code], 2D rendering will use an high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be a [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be a [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients.
+ [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons.
+ </member>
<member name="use_occlusion_culling" type="bool" setter="set_use_occlusion_culling" getter="is_using_occlusion_culling" default="false">
If [code]true[/code], [OccluderInstance3D] nodes will be usable for occlusion culling in 3D for this viewport. For the root viewport, [member ProjectSettings.rendering/occlusion_culling/use_occlusion_culling] must be set to [code]true[/code] instead.
[b]Note:[/b] Enabling occlusion culling has a cost on the CPU. Only enable occlusion culling if you actually plan to use it, and think whether your scene can actually benefit from occlusion culling. Large, open scenes with few or no objects blocking the view will generally not benefit much from occlusion culling. Large open scenes generally benefit more from mesh LOD and visibility ranges ([member GeometryInstance3D.visibility_range_begin] and [member GeometryInstance3D.visibility_range_end]) compared to occlusion culling.
diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
index d53c0fcb26..21fd4d3d9d 100644
--- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl
+++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
@@ -12,7 +12,7 @@
#define FLAGS_CLIP_RECT_UV uint(1 << 9)
#define FLAGS_TRANSPOSE_RECT uint(1 << 10)
-#define FLAGS_USING_LIGHT_MASK uint(1 << 11)
+// (1 << 11) is for FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR in RD backends, unused here.
#define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12)
#define FLAGS_USING_PARTICLES uint(1 << 13)
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index bad2b31a31..c25dbef288 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -629,6 +629,8 @@ public:
void render_target_clear_used(RID p_render_target);
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override;
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override;
+ virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {}
+ virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; }
// new
void render_target_set_as_unused(RID p_render_target) override {
diff --git a/editor/directory_create_dialog.cpp b/editor/directory_create_dialog.cpp
index df860bab2c..fed7cb82c9 100644
--- a/editor/directory_create_dialog.cpp
+++ b/editor/directory_create_dialog.cpp
@@ -33,10 +33,10 @@
#include "core/io/dir_access.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
+#include "editor/gui/editor_validation_panel.h"
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
-#include "scene/gui/panel_container.h"
static String sanitize_input(const String &p_path) {
String path = p_path.strip_edges();
@@ -73,24 +73,17 @@ String DirectoryCreateDialog::_validate_path(const String &p_path) const {
return String();
}
-void DirectoryCreateDialog::_on_dir_path_changed(const String &p_text) {
- const String path = sanitize_input(p_text);
+void DirectoryCreateDialog::_on_dir_path_changed() {
+ const String path = sanitize_input(dir_path->get_text());
const String error = _validate_path(path);
if (error.is_empty()) {
- status_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
-
if (path.contains("/")) {
- status_label->set_text(TTR("Using slashes in folder names will create subfolders recursively."));
- } else {
- status_label->set_text(TTR("Folder name is valid."));
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Using slashes in folder names will create subfolders recursively."), EditorValidationPanel::MSG_OK);
}
} else {
- status_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- status_label->set_text(error);
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, error, EditorValidationPanel::MSG_ERROR);
}
-
- get_ok_button()->set_disabled(!error.is_empty());
}
void DirectoryCreateDialog::ok_pressed() {
@@ -127,21 +120,13 @@ void DirectoryCreateDialog::config(const String &p_base_dir) {
label->set_text(vformat(TTR("Create new folder in %s:"), base_dir));
dir_path->set_text("new folder");
dir_path->select_all();
- _on_dir_path_changed(dir_path->get_text());
+ validation_panel->update();
}
void DirectoryCreateDialog::_bind_methods() {
ADD_SIGNAL(MethodInfo("dir_created"));
}
-void DirectoryCreateDialog::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_THEME_CHANGED: {
- status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
- } break;
- }
-}
-
DirectoryCreateDialog::DirectoryCreateDialog() {
set_title(TTR("Create Folder"));
set_min_size(Size2i(480, 0) * EDSCALE);
@@ -154,7 +139,6 @@ DirectoryCreateDialog::DirectoryCreateDialog() {
vb->add_child(label);
dir_path = memnew(LineEdit);
- dir_path->connect("text_changed", callable_mp(this, &DirectoryCreateDialog::_on_dir_path_changed));
vb->add_child(dir_path);
register_text_enter(dir_path);
@@ -162,11 +146,11 @@ DirectoryCreateDialog::DirectoryCreateDialog() {
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
vb->add_child(spacing);
- status_panel = memnew(PanelContainer);
- status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- vb->add_child(status_panel);
+ validation_panel = memnew(EditorValidationPanel);
+ vb->add_child(validation_panel);
+ validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Folder name is valid."));
+ validation_panel->set_update_callback(callable_mp(this, &DirectoryCreateDialog::_on_dir_path_changed));
+ validation_panel->set_accept_button(get_ok_button());
- status_label = memnew(Label);
- status_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- status_panel->add_child(status_label);
+ dir_path->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
}
diff --git a/editor/directory_create_dialog.h b/editor/directory_create_dialog.h
index e2601181da..82e2e98ae5 100644
--- a/editor/directory_create_dialog.h
+++ b/editor/directory_create_dialog.h
@@ -33,9 +33,9 @@
#include "scene/gui/dialogs.h"
+class EditorValidationPanel;
class Label;
class LineEdit;
-class PanelContainer;
class DirectoryCreateDialog : public ConfirmationDialog {
GDCLASS(DirectoryCreateDialog, ConfirmationDialog);
@@ -44,17 +44,13 @@ class DirectoryCreateDialog : public ConfirmationDialog {
Label *label = nullptr;
LineEdit *dir_path = nullptr;
-
- PanelContainer *status_panel = nullptr;
- Label *status_label = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
String _validate_path(const String &p_path) const;
-
- void _on_dir_path_changed(const String &p_text);
+ void _on_dir_path_changed();
protected:
static void _bind_methods();
- void _notification(int p_what);
virtual void ok_pressed() override;
virtual void _post_popup() override;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index d95b1de365..7ac812101a 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_validation_panel.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/script_editor_plugin.h"
#include "multi_node_edit.h"
@@ -3927,12 +3928,6 @@ void EditorInspector::_notification(int p_what) {
}
} break;
- case NOTIFICATION_THEME_CHANGED: {
- if (add_meta_error_panel) {
- add_meta_error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
- }
- } break;
-
case NOTIFICATION_PREDELETE: {
edit(nullptr); //just in case
} break;
@@ -4083,27 +4078,17 @@ void EditorInspector::_add_meta_confirm() {
undo_redo->commit_action();
}
-void EditorInspector::_check_meta_name(const String &p_name) {
- String error;
-
- if (p_name == "") {
- error = TTR("Metadata name can't be empty.");
- } else if (!p_name.is_valid_identifier()) {
- error = TTR("Metadata name must be a valid identifier.");
- } else if (object->has_meta(p_name)) {
- error = vformat(TTR("Metadata with name \"%s\" already exists."), p_name);
- } else if (p_name[0] == '_') {
- error = TTR("Names starting with _ are reserved for editor-only metadata.");
- }
+void EditorInspector::_check_meta_name() {
+ const String meta_name = add_meta_name->get_text();
- if (error != "") {
- add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- add_meta_error->set_text(error);
- add_meta_dialog->get_ok_button()->set_disabled(true);
- } else {
- add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
- add_meta_error->set_text(TTR("Metadata name is valid."));
- add_meta_dialog->get_ok_button()->set_disabled(false);
+ if (meta_name.is_empty()) {
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name can't be empty."), EditorValidationPanel::MSG_ERROR);
+ } else if (!meta_name.is_valid_identifier()) {
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name must be a valid identifier."), EditorValidationPanel::MSG_ERROR);
+ } else if (object->has_meta(meta_name)) {
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, vformat(TTR("Metadata with name \"%s\" already exists."), meta_name), EditorValidationPanel::MSG_ERROR);
+ } else if (meta_name[0] == '_') {
+ validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Names starting with _ are reserved for editor-only metadata."), EditorValidationPanel::MSG_ERROR);
}
}
@@ -4143,16 +4128,13 @@ void EditorInspector::_show_add_meta_dialog() {
add_meta_dialog->register_text_enter(add_meta_name);
add_meta_dialog->connect("confirmed", callable_mp(this, &EditorInspector::_add_meta_confirm));
- add_meta_error_panel = memnew(PanelContainer);
- vbc->add_child(add_meta_error_panel);
- if (is_inside_tree()) {
- add_meta_error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
- }
-
- add_meta_error = memnew(Label);
- add_meta_error_panel->add_child(add_meta_error);
+ validation_panel = memnew(EditorValidationPanel);
+ vbc->add_child(validation_panel);
+ validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name is valid."));
+ validation_panel->set_update_callback(callable_mp(this, &EditorInspector::_check_meta_name));
+ validation_panel->set_accept_button(add_meta_dialog->get_ok_button());
- add_meta_name->connect("text_changed", callable_mp(this, &EditorInspector::_check_meta_name));
+ add_meta_name->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
}
Node *node = Object::cast_to<Node>(object);
@@ -4164,9 +4146,9 @@ void EditorInspector::_show_add_meta_dialog() {
}
add_meta_dialog->popup_centered();
- add_meta_name->set_text("");
- _check_meta_name("");
add_meta_name->grab_focus();
+ add_meta_name->set_text("");
+ validation_panel->update();
}
void EditorInspector::_bind_methods() {
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 9a4c4f7f99..ed0d0ec373 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -39,6 +39,7 @@ class AcceptDialog;
class Button;
class ConfirmationDialog;
class EditorInspector;
+class EditorValidationPanel;
class LineEdit;
class OptionButton;
class PanelContainer;
@@ -543,12 +544,11 @@ class EditorInspector : public ScrollContainer {
ConfirmationDialog *add_meta_dialog = nullptr;
LineEdit *add_meta_name = nullptr;
OptionButton *add_meta_type = nullptr;
- PanelContainer *add_meta_error_panel = nullptr;
- Label *add_meta_error = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
void _add_meta_confirm();
void _show_add_meta_dialog();
- void _check_meta_name(const String &p_name);
+ void _check_meta_name();
protected:
static void _bind_methods();
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 10512abf6e..2efd3c031e 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -496,6 +496,9 @@ void EditorNode::_update_from_settings() {
Viewport::MSAA msaa = Viewport::MSAA(int(GLOBAL_GET("rendering/anti_aliasing/quality/msaa_2d")));
scene_root->set_msaa_2d(msaa);
+ bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
+ scene_root->set_use_hdr_2d(use_hdr_2d);
+
float mesh_lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels");
scene_root->set_mesh_lod_threshold(mesh_lod_threshold);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 80cefecee2..7952efff97 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1280,6 +1280,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
}
theme->set_stylebox("panel", "Tree", style_tree_bg);
+ theme->set_stylebox("panel", "EditorValidationPanel", style_tree_bg);
// Tree
theme->set_icon("checked", "Tree", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons")));
@@ -1969,7 +1970,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("files_disabled", "FileDialog", font_disabled_color);
// ColorPicker
- theme->set_constant("margin", "ColorPicker", popup_margin_size);
+ theme->set_constant("margin", "ColorPicker", default_margin_size);
theme->set_constant("sv_width", "ColorPicker", 256 * EDSCALE);
theme->set_constant("sv_height", "ColorPicker", 256 * EDSCALE);
theme->set_constant("h_width", "ColorPicker", 30 * EDSCALE);
diff --git a/editor/gui/editor_validation_panel.cpp b/editor/gui/editor_validation_panel.cpp
new file mode 100644
index 0000000000..af15010b78
--- /dev/null
+++ b/editor/gui/editor_validation_panel.cpp
@@ -0,0 +1,134 @@
+/**************************************************************************/
+/* editor_validation_panel.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 "editor_validation_panel.h"
+
+#include "editor/editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/label.h"
+
+void EditorValidationPanel::_update() {
+ for (const KeyValue<int, String> &E : valid_messages) {
+ set_message(E.key, E.value, MSG_OK);
+ }
+
+ valid = true;
+ update_callback.callv(Array());
+
+ if (accept_button) {
+ accept_button->set_disabled(!valid);
+ }
+ pending_update = false;
+}
+
+void EditorValidationPanel::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ theme_cache.valid_color = get_theme_color(SNAME("success_color"), SNAME("Editor"));
+ theme_cache.warning_color = get_theme_color(SNAME("warning_color"), SNAME("Editor"));
+ theme_cache.error_color = get_theme_color(SNAME("error_color"), SNAME("Editor"));
+ } break;
+ }
+}
+
+void EditorValidationPanel::add_line(int p_id, const String &p_valid_message) {
+ ERR_FAIL_COND(valid_messages.has(p_id));
+
+ Label *label = memnew(Label);
+ message_container->add_child(label);
+ label->set_custom_minimum_size(Size2(200 * EDSCALE, 0));
+ label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
+
+ valid_messages[p_id] = p_valid_message;
+ labels[p_id] = label;
+}
+
+void EditorValidationPanel::set_accept_button(Button *p_button) {
+ accept_button = p_button;
+}
+
+void EditorValidationPanel::set_update_callback(const Callable &p_callback) {
+ update_callback = p_callback;
+}
+
+void EditorValidationPanel::update() {
+ ERR_FAIL_COND(update_callback.is_null());
+
+ if (pending_update) {
+ return;
+ }
+ pending_update = true;
+ callable_mp(this, &EditorValidationPanel::_update).call_deferred();
+}
+
+void EditorValidationPanel::set_message(int p_id, const String &p_text, MessageType p_type, bool p_auto_prefix) {
+ ERR_FAIL_COND(!valid_messages.has(p_id));
+
+ Label *label = labels[p_id];
+ if (p_text.is_empty()) {
+ label->hide();
+ return;
+ }
+ label->show();
+
+ if (p_auto_prefix) {
+ label->set_text(String(U"• ") + p_text);
+ } else {
+ label->set_text(p_text);
+ }
+
+ switch (p_type) {
+ case MSG_OK:
+ label->add_theme_color_override(SNAME("font_color"), theme_cache.valid_color);
+ break;
+ case MSG_WARNING:
+ label->add_theme_color_override(SNAME("font_color"), theme_cache.warning_color);
+ break;
+ case MSG_ERROR:
+ label->add_theme_color_override(SNAME("font_color"), theme_cache.error_color);
+ valid = false;
+ break;
+ case MSG_INFO:
+ label->remove_theme_color_override(SNAME("font_color"));
+ break;
+ }
+}
+
+bool EditorValidationPanel::is_valid() const {
+ return valid;
+}
+
+EditorValidationPanel::EditorValidationPanel() {
+ set_v_size_flags(SIZE_EXPAND_FILL);
+
+ message_container = memnew(VBoxContainer);
+ add_child(message_container);
+}
diff --git a/editor/gui/editor_validation_panel.h b/editor/gui/editor_validation_panel.h
new file mode 100644
index 0000000000..c371795e15
--- /dev/null
+++ b/editor/gui/editor_validation_panel.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* editor_validation_panel.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 EDITOR_VALIDATION_PANEL_H
+#define EDITOR_VALIDATION_PANEL_H
+
+#include "scene/gui/panel_container.h"
+
+class Button;
+class Label;
+class VBoxContainer;
+
+class EditorValidationPanel : public PanelContainer {
+ GDCLASS(EditorValidationPanel, PanelContainer);
+
+public:
+ enum MessageType {
+ MSG_OK,
+ MSG_WARNING,
+ MSG_ERROR,
+ MSG_INFO,
+ };
+
+ static const int MSG_ID_DEFAULT = 0; // Avoids hard-coding ID in dialogs with single-line validation.
+
+private:
+ VBoxContainer *message_container = nullptr;
+
+ HashMap<int, String> valid_messages;
+ HashMap<int, Label *> labels;
+
+ bool valid = false;
+ bool pending_update = false;
+
+ struct ThemeCache {
+ Color valid_color;
+ Color warning_color;
+ Color error_color;
+ } theme_cache;
+
+ void _update();
+
+ Callable update_callback;
+ Button *accept_button = nullptr;
+
+protected:
+ void _notification(int p_what);
+
+public:
+ void add_line(int p_id, const String &p_valid_message = "");
+ void set_accept_button(Button *p_button);
+ void set_update_callback(const Callable &p_callback);
+
+ void update();
+ void set_message(int p_id, const String &p_text, MessageType p_type, bool p_auto_prefix = true);
+ bool is_valid() const;
+
+ EditorValidationPanel();
+};
+
+#endif // EDITOR_VALIDATION_PANEL_H
diff --git a/editor/icons/Camera2D.svg b/editor/icons/Camera2D.svg
index 81e5cc2c8e..24198a8f06 100644
--- a/editor/icons/Camera2D.svg
+++ b/editor/icons/Camera2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z" fill="#8da5f3"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#8da5f3"><path d="m7 2-2 4h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-4zm1 5a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3z"/><path d="m2 3h2v2h-2z"/></g></svg>
diff --git a/editor/icons/Camera3D.svg b/editor/icons/Camera3D.svg
index bf61aa48fc..69d435ff17 100644
--- a/editor/icons/Camera3D.svg
+++ b/editor/icons/Camera3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f" fill-opacity=".996078"><path d="m7 2-2 4h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-4zm1 5a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3z"/><path d="m2 3h2v2h-2z"/></g></svg>
diff --git a/editor/icons/CameraAttributes.svg b/editor/icons/CameraAttributes.svg
index 459c64e11c..72d88ace7a 100644
--- a/editor/icons/CameraAttributes.svg
+++ b/editor/icons/CameraAttributes.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a3 3 0 0 0-2 5.23V9L1 7v6l3-2v1a1 1 0 0 0 1 1h3a2 2 0 0 1 .73-1.24 1.83 1.83 0 0 1 1.828-3.143 1.8 1.8 0 0 1 3.313-.75 3 3 0 0 0-4.883-3.09A3 3 0 0 0 6 2z" fill="#e0e0e0"/><path d="M12.36 8.598a.533 3.2 0 0 0-.51 2.275 3.2.533 30 0 0-.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0-.515-.887.533 3.2 0 0 0-.51-2.275z" fill="#c38ef1"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#c38ef1"/></svg>
diff --git a/editor/icons/CameraAttributesPhysical.svg b/editor/icons/CameraAttributesPhysical.svg
new file mode 100644
index 0000000000..f75cd0dc44
--- /dev/null
+++ b/editor/icons/CameraAttributesPhysical.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#ffca5f"/></svg>
diff --git a/editor/icons/CameraAttributesPractical.svg b/editor/icons/CameraAttributesPractical.svg
new file mode 100644
index 0000000000..0aed99056d
--- /dev/null
+++ b/editor/icons/CameraAttributesPractical.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#5fb2ff"/></svg>
diff --git a/editor/icons/CameraTexture.svg b/editor/icons/CameraTexture.svg
index 145a817b7e..5a050b900e 100644
--- a/editor/icons/CameraTexture.svg
+++ b/editor/icons/CameraTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm5.818 1A1.65 1.65 0 0 0 7.19 5.514a1.65 1.65 0 1 0-1.641 2.752v1.19a.55.55 0 0 0 .545.544h3.27a.55.55 0 0 0 .544-.545V8.91L11.545 10V6.728L9.908 7.82v-.965a1.65 1.65 0 0 0-1.09-2.851z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m2 1c-.5522847 0-1 .4477153-1 1v12c0 .552285.4477153 1 1 1h12c.552285 0 1-.447715 1-1v-12c0-.5522847-.447715-1-1-1zm1 2h10v8h-10z"/><g stroke-width="1.97307" transform="matrix(.50682329 0 0 .50682329 3.994604 2.971119)"><path d="m5.9298703 2.0300582-1.9730742 3.934252-2.95288.01033s-1.00000003.000001-1.00000003 1v5.9004198s-.00049262 1.000376.99950633 1.000351l13.7985506-.000351s1-.000001 1-1v-5.9004195s-.000008-.9979028-1-1l-2.95288-.01033-1.9730741-3.934252zm2.0701297 4.8922972c1.6568542 0 3 1.3431458 3 3 0 1.6568536-1.3431458 2.9999996-3 2.9999996s-3-1.343146-3-2.9999996c0-1.6568542 1.3431458-3 3-3z"/><path d="m.0106475 2.0300582 3.9461486-.0000002v1.9730745h-3.9461486z"/></g></g></svg>
diff --git a/editor/icons/GizmoCamera3D.svg b/editor/icons/GizmoCamera3D.svg
new file mode 100644
index 0000000000..c22fa18ee4
--- /dev/null
+++ b/editor/icons/GizmoCamera3D.svg
@@ -0,0 +1 @@
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m52.789062 12.285156-.068359.001953s-2.40509.20501-4.607422 1.613282c-2.202331 1.408271-4.620558 4.01711-6.382812 8.287109l-6.40625 15.527344h-.966797v-3.986328s.05404-2.69271-1.351563-5.503907c-1.405598-2.811197-5.028556-5.923828-10.076171-5.923828-5.047616 0-8.672527 3.112631-10.078126 5.923828s-1.351562 5.503907-1.351562 5.503907v4.796875c-2.8348528 1.102579-4.8904632 3.167645-5.8632812 5.113281-1.4055984 2.811197-1.3515624 5.503906-1.3515626 5.503906v59.427732c0 5.04762 3.1145841 8.67253 5.9257818 10.07813 2.811195 1.4056 5.503906 1.35156 5.503906 1.35156h96.820316c5.04761 0 8.67252-3.11458 10.07812-5.92578s1.35156-5.50391 1.35156-5.50391v-59.427732c0-5.047615-3.11458-8.672526-5.92578-10.078125-2.81119-1.405599-5.5039-1.349609-5.5039-1.349609h-19.859379l-6.40625-15.527344c-1.693114-4.102471-4.120301-6.670605-6.261719-8.09375-2.141416-1.423145-4.378906-1.779297-4.378906-1.779297l-.242187-.029297zm.201172 8h21.722657c.009-.0059.05394-.06976.867187.470703.864378.57445 2.152133 1.720094 3.292969 4.484375l8.451172 20.47461h25.210941s1.02269.05432 1.92578.505859c.90308.451543 1.5039.540926 1.5039 2.921875v59.427732s-.0563 1.0227-.50781 1.92578c-.45154.90309-.54093 1.50391-2.92187 1.50391h-96.820316s-1.022696-.0543-1.925782-.50586c-.903086-.45154-1.503906-.54288-1.503906-2.92383v-59.427732s.05627-1.022696.507813-1.925781c.451542-.903085.540927-1.501953 2.921875-1.501953h24.960937l8.451172-20.47461c1.241133-3.007304 2.535771-4.112924 3.296875-4.599609.756188-.483541.568815-.357095.566406-.355469zm12.009766 24c-18.572491 0-33.714844 15.142353-33.714844 33.714844s15.142352 33.71484 33.714844 33.71484 33.714844-15.142349 33.714844-33.71484-15.142353-33.714844-33.714844-33.714844zm0 8c14.24897 0 25.714844 11.465874 25.714844 25.714844s-11.465875 25.71484-25.714844 25.71484-25.714844-11.46587-25.714844-25.71484 11.465874-25.714844 25.714844-25.714844z" fill-opacity=".3"/><g fill="#f7f5cf" stroke-width="8.00001"><path d="m52.857144 16.285714s-4.425182.151262-7.428572 7.428572l-7.428571 17.999668h-22.285715c-7.4285711 0-7.4285711 7.428572-7.4285711 7.428572v59.428574c0 7.42857 7.4285711 7.42857 7.4285711 7.42857h96.821434c7.42857 0 7.42857-7.42857 7.42857-7.42857v-59.428574c0-7.428572-7.42857-7.428572-7.42857-7.428572h-22.535719l-7.428571-17.999668c-2.833953-6.866759-7.428572-7.428572-7.428572-7.428572zm12.142856 31.999999c16.410747 0 29.714286 13.303539 29.714286 29.714286s-13.303539 29.714291-29.714286 29.714291-29.714286-13.303544-29.714286-29.714291 13.303539-29.714286 29.714286-29.714286z"/><path d="m15.500291 33.728577h14.857143s0-7.428572-7.428571-7.428572c-7.428572 0-7.428572 7.428572-7.428572 7.428572z"/></g></svg>
diff --git a/editor/icons/XRCamera3D.svg b/editor/icons/XRCamera3D.svg
index 501a86a9f7..88e11cfe34 100644
--- a/editor/icons/XRCamera3D.svg
+++ b/editor/icons/XRCamera3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9.5 0a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9l3 2V5l-3 2V5.23A3 3 0 0 0 9.5 0zM4 12v1a1 1 0 0 0 1 1 1 1 0 0 0-1 1v1h1v-1h1v1h1v-1a1 1 0 0 0-1-1 1 1 0 0 0 1-1v-1H6v1H5v-1zm5 0v4h1v-1h1v1h1v-1a1 1 0 0 0-.137-.5A1 1 0 0 0 12 14v-1a1 1 0 0 0-1-1zm1 1h1v1h-1z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><g fill-opacity=".996078" transform="translate(0 -2)"><path d="m7 2-2 3h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-3zm1 4c1.6568542 0 3 1.3431458 3 3 0 1.656854-1.3431458 3-3 3s-3-1.343146-3-3c0-1.6568542 1.3431458-3 3-3z"/><path d="m2 2h2v2h-2z"/></g><path d="m4 12v1c0 .552285.4477153 1 1 1-.5522847 0-1 .447715-1 1v1h1v-1h1v1h1v-1c0-.552285-.4477153-1-1-1 .5522847 0 1-.447715 1-1v-1h-1v1h-1v-1zm5 0v4h1v-1h1v1h1v-1c-.000919-.175812-.04817-.348275-.137-.5.08883-.151725.136081-.324188.137-.5v-1c0-.552285-.447715-1-1-1zm1 1h1v1h-1z"/></g></svg>
diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
index e4503b9c6f..2ef2e3a666 100644
--- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
@@ -41,6 +41,7 @@ Camera3DGizmoPlugin::Camera3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8));
create_material("camera_material", gizmo_color);
+ create_icon_material("camera_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoCamera3D", "EditorIcons"));
create_handle_material("handles");
}
@@ -159,6 +160,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector<Vector3> handles;
Ref<Material> material = get_material("camera_material", p_gizmo);
+ Ref<Material> icon = get_material("camera_icon", p_gizmo);
const Size2i viewport_size = _get_viewport_size(camera);
const real_t viewport_aspect = viewport_size.x > 0 && viewport_size.y > 0 ? viewport_size.aspect() : 1.0;
@@ -256,6 +258,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
#undef ADD_QUAD
p_gizmo->add_lines(lines, material);
+ p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_collision_segments(lines);
if (!handles.is_empty()) {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index f212ddb34e..d05db7aa63 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2659,6 +2659,9 @@ void Node3DEditorViewport::_project_settings_changed() {
const bool transparent_background = GLOBAL_GET("rendering/viewport/transparent_background");
viewport->set_transparent_background(transparent_background);
+ const bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
+ viewport->set_use_hdr_2d(use_hdr_2d);
+
const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding");
viewport->set_use_debanding(use_debanding);
diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp
index 986f6bb87a..8f56267123 100644
--- a/editor/scene_create_dialog.cpp
+++ b/editor/scene_create_dialog.cpp
@@ -34,6 +34,7 @@
#include "editor/create_dialog.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
+#include "editor/gui/editor_validation_panel.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/node_3d.h"
#include "scene/gui/box_container.h"
@@ -41,7 +42,6 @@
#include "scene/gui/grid_container.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
-#include "scene/gui/panel_container.h"
#include "scene/resources/packed_scene.h"
void SceneCreateDialog::_notification(int p_what) {
@@ -53,7 +53,6 @@ void SceneCreateDialog::_notification(int p_what) {
node_type_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
node_type_gui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
node_type_other->add_theme_icon_override(SNAME("icon"), get_theme_icon(SNAME("Node"), SNAME("EditorIcons")));
- status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
} break;
}
}
@@ -63,7 +62,7 @@ void SceneCreateDialog::config(const String &p_dir) {
root_name_edit->set_text("");
scene_name_edit->set_text("");
scene_name_edit->call_deferred(SNAME("grab_focus"));
- update_dialog();
+ validation_panel->update();
}
void SceneCreateDialog::accept_create() {
@@ -82,40 +81,35 @@ void SceneCreateDialog::browse_types() {
void SceneCreateDialog::on_type_picked() {
other_type_display->set_text(select_node_dialog->get_selected_type().get_slice(" ", 0));
if (node_type_other->is_pressed()) {
- update_dialog();
+ validation_panel->update();
} else {
- node_type_other->set_pressed(true); // Calls update_dialog() via group.
+ node_type_other->set_pressed(true); // Calls validation_panel->update() via group.
}
}
void SceneCreateDialog::update_dialog() {
scene_name = scene_name_edit->get_text().strip_edges();
- update_error(file_error_label, MSG_OK, TTR("Scene name is valid."));
- bool is_valid = true;
if (scene_name.is_empty()) {
- update_error(file_error_label, MSG_ERROR, TTR("Scene name is empty."));
- is_valid = false;
+ validation_panel->set_message(MSG_ID_PATH, TTR("Scene name is empty."), EditorValidationPanel::MSG_ERROR);
}
- if (is_valid) {
+ if (validation_panel->is_valid()) {
if (!scene_name.ends_with(".")) {
scene_name += ".";
}
scene_name += scene_extension_picker->get_selected_metadata().operator String();
}
- if (is_valid && !scene_name.is_valid_filename()) {
- update_error(file_error_label, MSG_ERROR, TTR("File name invalid."));
- is_valid = false;
+ if (validation_panel->is_valid() && !scene_name.is_valid_filename()) {
+ validation_panel->set_message(MSG_ID_PATH, TTR("File name invalid."), EditorValidationPanel::MSG_ERROR);
}
- if (is_valid) {
+ if (validation_panel->is_valid()) {
scene_name = directory.path_join(scene_name);
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da->file_exists(scene_name)) {
- update_error(file_error_label, MSG_ERROR, TTR("File already exists."));
- is_valid = false;
+ validation_panel->set_message(MSG_ID_PATH, TTR("File already exists."), EditorValidationPanel::MSG_ERROR);
}
}
@@ -126,8 +120,6 @@ void SceneCreateDialog::update_dialog() {
node_type_other->set_icon(nullptr);
}
- update_error(node_error_label, MSG_OK, TTR("Root node valid."));
-
root_name = root_name_edit->get_text().strip_edges();
if (root_name.is_empty()) {
root_name = scene_name_edit->get_text().strip_edges();
@@ -135,39 +127,16 @@ void SceneCreateDialog::update_dialog() {
if (root_name.is_empty()) {
root_name_edit->set_placeholder(TTR("Leave empty to derive from scene name"));
} else {
- // Respect the desired root node casing from ProjectSettings and ensure it's a valid node name.
- String adjusted_root_name = Node::adjust_name_casing(root_name);
- root_name = adjusted_root_name.validate_node_name();
-
- bool has_invalid_characters = root_name != adjusted_root_name;
- if (has_invalid_characters) {
- update_error(node_error_label, MSG_WARNING, TTR("Invalid root node name characters have been replaced."));
- }
-
- root_name_edit->set_placeholder(root_name);
+ // Respect the desired root node casing from ProjectSettings.
+ root_name = Node::adjust_name_casing(root_name);
+ root_name_edit->set_placeholder(root_name.validate_node_name());
}
}
- if (root_name.is_empty() || root_name.validate_node_name() != root_name) {
- update_error(node_error_label, MSG_ERROR, TTR("Invalid root node name."));
- is_valid = false;
- }
-
- get_ok_button()->set_disabled(!is_valid);
-}
-
-void SceneCreateDialog::update_error(Label *p_label, MsgType p_type, const String &p_msg) {
- p_label->set_text(String::utf8("• ") + p_msg);
- switch (p_type) {
- case MSG_OK:
- p_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
- break;
- case MSG_ERROR:
- p_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- break;
- case MSG_WARNING:
- p_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- break;
+ if (root_name.is_empty()) {
+ validation_panel->set_message(MSG_ID_ROOT, TTR("Invalid root node name."), EditorValidationPanel::MSG_ERROR);
+ } else if (root_name != root_name.validate_node_name()) {
+ validation_panel->set_message(MSG_ID_ROOT, TTR("Invalid root node name characters have been replaced."), EditorValidationPanel::MSG_WARNING);
}
}
@@ -268,8 +237,6 @@ SceneCreateDialog::SceneCreateDialog() {
select_node_button = memnew(Button);
hb->add_child(select_node_button);
select_node_button->connect("pressed", callable_mp(this, &SceneCreateDialog::browse_types));
-
- node_type_group->connect("pressed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1));
}
{
@@ -282,7 +249,6 @@ SceneCreateDialog::SceneCreateDialog() {
scene_name_edit = memnew(LineEdit);
hb->add_child(scene_name_edit);
scene_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- scene_name_edit->connect("text_changed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1));
scene_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1));
List<String> extensions;
@@ -305,7 +271,6 @@ SceneCreateDialog::SceneCreateDialog() {
gc->add_child(root_name_edit);
root_name_edit->set_tooltip_text(TTR("When empty, the root node name is derived from the scene name based on the \"editor/naming/node_name_casing\" project setting."));
root_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- root_name_edit->connect("text_changed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1));
root_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1));
}
@@ -313,19 +278,16 @@ SceneCreateDialog::SceneCreateDialog() {
main_vb->add_child(spacing);
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
- status_panel = memnew(PanelContainer);
- main_vb->add_child(status_panel);
- status_panel->set_h_size_flags(Control::SIZE_FILL);
- status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-
- VBoxContainer *status_vb = memnew(VBoxContainer);
- status_panel->add_child(status_vb);
-
- file_error_label = memnew(Label);
- status_vb->add_child(file_error_label);
+ validation_panel = memnew(EditorValidationPanel);
+ main_vb->add_child(validation_panel);
+ validation_panel->add_line(MSG_ID_PATH, TTR("Scene name is valid."));
+ validation_panel->add_line(MSG_ID_ROOT, TTR("Root node valid."));
+ validation_panel->set_update_callback(callable_mp(this, &SceneCreateDialog::update_dialog));
+ validation_panel->set_accept_button(get_ok_button());
- node_error_label = memnew(Label);
- status_vb->add_child(node_error_label);
+ node_type_group->connect("pressed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
+ scene_name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
+ root_name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
set_title(TTR("Create New Scene"));
set_min_size(Size2i(400 * EDSCALE, 0));
diff --git a/editor/scene_create_dialog.h b/editor/scene_create_dialog.h
index c6f40b928e..c7a4a09850 100644
--- a/editor/scene_create_dialog.h
+++ b/editor/scene_create_dialog.h
@@ -37,18 +37,17 @@ class ButtonGroup;
class CheckBox;
class CreateDialog;
class EditorFileDialog;
+class EditorValidationPanel;
class Label;
class LineEdit;
class OptionButton;
-class PanelContainer;
class SceneCreateDialog : public ConfirmationDialog {
GDCLASS(SceneCreateDialog, ConfirmationDialog);
- enum MsgType {
- MSG_OK,
- MSG_ERROR,
- MSG_WARNING,
+ enum {
+ MSG_ID_PATH,
+ MSG_ID_ROOT,
};
const StringName type_meta = StringName("type");
@@ -80,15 +79,12 @@ private:
OptionButton *scene_extension_picker = nullptr;
LineEdit *root_name_edit = nullptr;
- PanelContainer *status_panel = nullptr;
- Label *file_error_label = nullptr;
- Label *node_error_label = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
void accept_create();
void browse_types();
void on_type_picked();
void update_dialog();
- void update_error(Label *p_label, MsgType p_type, const String &p_msg);
protected:
void _notification(int p_what);
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 058f4c8d91..daac755529 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -41,6 +41,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_validation_panel.h"
static String _get_parent_class_of_script(String p_path) {
if (!ResourceLoader::exists(p_path, "Script")) {
@@ -136,7 +137,6 @@ void ScriptCreateDialog::_notification(int p_what) {
path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
parent_browse_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
parent_search_button->set_icon(get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons")));
- status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
} break;
}
}
@@ -295,13 +295,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
}
// Let ScriptLanguage do custom validation.
- String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p);
- if (!path_error.is_empty()) {
- return path_error;
- }
-
- // All checks passed.
- return "";
+ return ScriptServer::get_language(language_menu->get_selected())->validate_path(p);
}
String ScriptCreateDialog::_get_class_name() const {
@@ -314,12 +308,12 @@ String ScriptCreateDialog::_get_class_name() const {
void ScriptCreateDialog::_class_name_changed(const String &p_name) {
is_class_name_valid = _validate_class(class_name->get_text());
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_parent_name_changed(const String &p_parent) {
is_parent_name_valid = _validate_parent(parent_name->get_text());
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_template_changed(int p_template) {
@@ -347,6 +341,7 @@ void ScriptCreateDialog::_template_changed(int p_template) {
}
}
}
+
// Update template label information.
String template_info = U"• ";
template_info += TTR("Template:");
@@ -354,8 +349,7 @@ void ScriptCreateDialog::_template_changed(int p_template) {
if (!sinfo.description.is_empty()) {
template_info += " - " + sinfo.description;
}
- template_info_label->set_text(template_info);
- template_info_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
+ validation_panel->set_message(MSG_ID_TEMPLATE, template_info, EditorValidationPanel::MSG_INFO, false);
}
void ScriptCreateDialog::ok_pressed() {
@@ -367,7 +361,7 @@ void ScriptCreateDialog::ok_pressed() {
EditorSettings::get_singleton()->save();
is_new_script_created = true;
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_create_new() {
@@ -471,7 +465,7 @@ void ScriptCreateDialog::_language_changed(int l) {
EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected()));
_parent_name_changed(parent_name->get_text());
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_built_in_pressed() {
@@ -482,13 +476,13 @@ void ScriptCreateDialog::_built_in_pressed() {
is_built_in = false;
_path_changed(file_path->get_text());
}
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_use_template_pressed() {
is_using_templates = use_templates->is_pressed();
EditorSettings::get_singleton()->set_meta("script_setup_use_script_templates", is_using_templates);
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) {
@@ -555,10 +549,9 @@ void ScriptCreateDialog::_path_changed(const String &p_path) {
is_path_valid = false;
is_new_script_created = true;
- String path_error = _validate_path(p_path, false);
+ path_error = _validate_path(p_path, false);
if (!path_error.is_empty()) {
- _msg_path_valid(false, path_error);
- _update_dialog();
+ validation_panel->update();
return;
}
@@ -567,32 +560,15 @@ void ScriptCreateDialog::_path_changed(const String &p_path) {
String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges());
if (da->file_exists(p)) {
is_new_script_created = false;
- _msg_path_valid(true, TTR("File exists, it will be reused."));
}
is_path_valid = true;
- _update_dialog();
+ validation_panel->update();
}
void ScriptCreateDialog::_path_submitted(const String &p_path) {
- ok_pressed();
-}
-
-void ScriptCreateDialog::_msg_script_valid(bool valid, const String &p_msg) {
- error_label->set_text(String::utf8("• ") + p_msg);
- if (valid) {
- error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
- } else {
- error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- }
-}
-
-void ScriptCreateDialog::_msg_path_valid(bool valid, const String &p_msg) {
- path_error_label->set_text(String::utf8("• ") + p_msg);
- if (valid) {
- path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
- } else {
- path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ if (!get_ok_button()->is_disabled()) {
+ ok_pressed();
}
}
@@ -688,25 +664,25 @@ void ScriptCreateDialog::_update_template_menu() {
void ScriptCreateDialog::_update_dialog() {
// "Add Script Dialog" GUI logic and script checks.
_update_template_menu();
- bool script_ok = true;
// Is script path/name valid (order from top to bottom)?
if (!is_built_in && !is_path_valid) {
- _msg_script_valid(false, TTR("Invalid path."));
- script_ok = false;
+ validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR);
}
if (has_named_classes && (is_new_script_created && !is_class_name_valid)) {
- _msg_script_valid(false, TTR("Invalid class name."));
- script_ok = false;
+ validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid class name."), EditorValidationPanel::MSG_ERROR);
}
if (!is_parent_name_valid && is_new_script_created) {
- _msg_script_valid(false, TTR("Invalid inherited parent name or path."));
- script_ok = false;
+ validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid inherited parent name or path."), EditorValidationPanel::MSG_ERROR);
}
- if (script_ok) {
- _msg_script_valid(true, TTR("Script path/name is valid."));
+ if (validation_panel->is_valid() && !is_new_script_created) {
+ validation_panel->set_message(MSG_ID_SCRIPT, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK);
+ }
+
+ if (!path_error.is_empty()) {
+ validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR);
}
// Does script have named classes?
@@ -752,7 +728,11 @@ void ScriptCreateDialog::_update_dialog() {
// Is Script created or loaded from existing file?
- builtin_warning_label->set_visible(is_built_in);
+ if (is_built_in) {
+ validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in scripts have some limitations and can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false);
+ } else if (_get_class_name() == parent_name->get_text()) {
+ validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Warning: Having the script name be the same as a built-in type is usually not desired."), EditorValidationPanel::MSG_WARNING, false);
+ }
path_controls[0]->set_visible(!is_built_in);
path_controls[1]->set_visible(!is_built_in);
@@ -761,7 +741,6 @@ void ScriptCreateDialog::_update_dialog() {
// Check if the script name is the same as the parent class.
// This warning isn't relevant if the script is built-in.
- script_name_warning_label->set_visible(!is_built_in && _get_class_name() == parent_name->get_text());
bool is_new_file = is_built_in || is_new_script_created;
@@ -774,21 +753,16 @@ void ScriptCreateDialog::_update_dialog() {
if (is_new_file) {
if (is_built_in) {
- _msg_path_valid(true, TTR("Built-in script (into scene file)."));
- }
- if (is_new_script_created && is_path_valid) {
- _msg_path_valid(true, TTR("Will create a new script file."));
+ validation_panel->set_message(MSG_ID_PATH, TTR("Built-in script (into scene file)."), EditorValidationPanel::MSG_OK);
}
} else {
+ template_inactive_message = TTR("Using existing script file.");
if (load_enabled) {
- template_inactive_message = TTR("Using existing script file.");
if (is_path_valid) {
- _msg_path_valid(true, TTR("Will load an existing script file."));
+ validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing script file."), EditorValidationPanel::MSG_OK);
}
} else {
- template_inactive_message = TTR("Using existing script file.");
- _msg_path_valid(false, TTR("Script file already exists."));
- script_ok = false;
+ validation_panel->set_message(MSG_ID_PATH, TTR("Script file already exists."), EditorValidationPanel::MSG_ERROR);
}
}
@@ -806,18 +780,7 @@ void ScriptCreateDialog::_update_dialog() {
template_menu->set_disabled(true);
template_menu->clear();
template_menu->add_item(template_inactive_message);
- }
- template_info_label->set_visible(!template_menu->is_disabled());
-
- get_ok_button()->set_disabled(!script_ok);
-
- Callable entered_call = callable_mp(this, &ScriptCreateDialog::_path_submitted);
- if (script_ok) {
- if (!file_path->is_connected("text_submitted", entered_call)) {
- file_path->connect("text_submitted", entered_call);
- }
- } else if (file_path->is_connected("text_submitted", entered_call)) {
- file_path->disconnect("text_submitted", entered_call);
+ validation_panel->set_message(MSG_ID_TEMPLATE, "", EditorValidationPanel::MSG_INFO);
}
}
@@ -967,47 +930,23 @@ ScriptCreateDialog::ScriptCreateDialog() {
/* Information Messages Field */
- VBoxContainer *vb = memnew(VBoxContainer);
-
- error_label = memnew(Label);
- vb->add_child(error_label);
-
- path_error_label = memnew(Label);
- vb->add_child(path_error_label);
-
- builtin_warning_label = memnew(Label);
- builtin_warning_label->set_text(
- TTR("Note: Built-in scripts have some limitations and can't be edited using an external editor."));
- vb->add_child(builtin_warning_label);
- builtin_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
- builtin_warning_label->hide();
-
- script_name_warning_label = memnew(Label);
- script_name_warning_label->set_text(
- TTR("Warning: Having the script name be the same as a built-in type is usually not desired."));
- vb->add_child(script_name_warning_label);
- script_name_warning_label->add_theme_color_override("font_color", Color(1, 0.85, 0.4));
- script_name_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
- script_name_warning_label->hide();
-
- template_info_label = memnew(Label);
- vb->add_child(template_info_label);
- template_info_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
-
- status_panel = memnew(PanelContainer);
- status_panel->set_h_size_flags(Control::SIZE_FILL);
- status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- status_panel->add_child(vb);
+ validation_panel = memnew(EditorValidationPanel);
+ validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script path/name is valid."));
+ validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new script file."));
+ validation_panel->add_line(MSG_ID_BUILT_IN);
+ validation_panel->add_line(MSG_ID_TEMPLATE);
+ validation_panel->set_update_callback(callable_mp(this, &ScriptCreateDialog::_update_dialog));
+ validation_panel->set_accept_button(get_ok_button());
/* Spacing */
Control *spacing = memnew(Control);
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
- vb = memnew(VBoxContainer);
+ VBoxContainer *vb = memnew(VBoxContainer);
vb->add_child(gc);
vb->add_child(spacing);
- vb->add_child(status_panel);
+ vb->add_child(validation_panel);
add_child(vb);
/* Language */
diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h
index fa42b96746..ea2f480c57 100644
--- a/editor/script_create_dialog.h
+++ b/editor/script_create_dialog.h
@@ -41,17 +41,20 @@
class CreateDialog;
class EditorFileDialog;
+class EditorValidationPanel;
class ScriptCreateDialog : public ConfirmationDialog {
GDCLASS(ScriptCreateDialog, ConfirmationDialog);
+ enum {
+ MSG_ID_SCRIPT,
+ MSG_ID_PATH,
+ MSG_ID_BUILT_IN,
+ MSG_ID_TEMPLATE,
+ };
+
LineEdit *class_name = nullptr;
- Label *error_label = nullptr;
- Label *path_error_label = nullptr;
- Label *builtin_warning_label = nullptr;
- Label *script_name_warning_label = nullptr;
- Label *template_info_label = nullptr;
- PanelContainer *status_panel = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
LineEdit *parent_name = nullptr;
Button *parent_browse_button = nullptr;
Button *parent_search_button = nullptr;
@@ -67,6 +70,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
AcceptDialog *alert = nullptr;
CreateDialog *select_class = nullptr;
bool is_browsing_parent = false;
+ String path_error;
String template_inactive_message;
String initial_bp;
bool is_new_script_created = true;
@@ -113,8 +117,6 @@ class ScriptCreateDialog : public ConfirmationDialog {
virtual void ok_pressed() override;
void _create_new();
void _load_exist();
- void _msg_script_valid(bool valid, const String &p_msg = String());
- void _msg_path_valid(bool valid, const String &p_msg = String());
void _update_template_menu();
void _update_dialog();
ScriptLanguage::ScriptTemplate _get_current_template() const;
diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp
index 607b53718b..9a7b9bc84d 100644
--- a/editor/shader_create_dialog.cpp
+++ b/editor/shader_create_dialog.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "editor/editor_scale.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_validation_panel.h"
#include "scene/resources/shader_include.h"
#include "scene/resources/visual_shader.h"
#include "servers/rendering/shader_types.h"
@@ -89,7 +90,6 @@ void ShaderCreateDialog::_update_theme() {
}
path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
- status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
}
void ShaderCreateDialog::_update_language_info() {
@@ -147,7 +147,7 @@ void ShaderCreateDialog::ok_pressed() {
}
is_new_shader_created = true;
- _update_dialog();
+ validation_panel->update();
}
void ShaderCreateDialog::_create_new() {
@@ -327,7 +327,7 @@ void ShaderCreateDialog::_type_changed(int p_language) {
}
EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_language", type_menu->get_item_text(type_menu->get_selected()));
- _update_dialog();
+ validation_panel->update();
}
void ShaderCreateDialog::_built_in_toggled(bool p_enabled) {
@@ -337,7 +337,7 @@ void ShaderCreateDialog::_built_in_toggled(bool p_enabled) {
} else {
_path_changed(file_path->get_text());
}
- _update_dialog();
+ validation_panel->update();
}
void ShaderCreateDialog::_browse_path() {
@@ -378,10 +378,9 @@ void ShaderCreateDialog::_path_changed(const String &p_path) {
is_path_valid = false;
is_new_shader_created = true;
- String path_error = _validate_path(p_path);
+ path_error = _validate_path(p_path);
if (!path_error.is_empty()) {
- _msg_path_valid(false, path_error);
- _update_dialog();
+ validation_panel->update();
return;
}
@@ -389,15 +388,16 @@ void ShaderCreateDialog::_path_changed(const String &p_path) {
String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges());
if (f->file_exists(p)) {
is_new_shader_created = false;
- _msg_path_valid(true, TTR("File exists, it will be reused."));
}
is_path_valid = true;
- _update_dialog();
+ validation_panel->update();
}
void ShaderCreateDialog::_path_submitted(const String &p_path) {
- ok_pressed();
+ if (!get_ok_button()->is_disabled()) {
+ ok_pressed();
+ }
}
void ShaderCreateDialog::config(const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled, int p_preferred_type, int p_preferred_mode) {
@@ -490,33 +490,14 @@ String ShaderCreateDialog::_validate_path(const String &p_path) {
return "";
}
-void ShaderCreateDialog::_msg_script_valid(bool valid, const String &p_msg) {
- error_label->set_text(String::utf8("• ") + p_msg);
- if (valid) {
- error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor")));
- } else {
- error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("error_color"), SNAME("Editor")));
- }
-}
-
-void ShaderCreateDialog::_msg_path_valid(bool valid, const String &p_msg) {
- path_error_label->set_text(String::utf8("• ") + p_msg);
- if (valid) {
- path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor")));
- } else {
- path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("error_color"), SNAME("Editor")));
- }
-}
-
void ShaderCreateDialog::_update_dialog() {
- bool shader_ok = true;
-
if (!is_built_in && !is_path_valid) {
- _msg_script_valid(false, TTR("Invalid path."));
- shader_ok = false;
+ validation_panel->set_message(MSG_ID_SHADER, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR);
}
- if (shader_ok) {
- _msg_script_valid(true, TTR("Shader path/name is valid."));
+ if (!path_error.is_empty()) {
+ validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR);
+ } else if (validation_panel->is_valid() && !is_new_shader_created) {
+ validation_panel->set_message(MSG_ID_SHADER, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK);
}
if (!built_in_enabled) {
internal->set_pressed(false);
@@ -537,37 +518,23 @@ void ShaderCreateDialog::_update_dialog() {
internal->set_disabled(!built_in_enabled);
- builtin_warning_label->set_visible(is_built_in);
+ if (is_built_in) {
+ validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in shaders can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false);
+ }
if (is_built_in) {
set_ok_button_text(TTR("Create"));
- _msg_path_valid(true, TTR("Built-in shader (into scene file)."));
+ validation_panel->set_message(MSG_ID_PATH, TTR("Built-in shader (into scene file)."), EditorValidationPanel::MSG_OK);
} else if (is_new_shader_created) {
set_ok_button_text(TTR("Create"));
- if (is_path_valid) {
- _msg_path_valid(true, TTR("Will create a new shader file."));
- }
} else if (load_enabled) {
set_ok_button_text(TTR("Load"));
if (is_path_valid) {
- _msg_path_valid(true, TTR("Will load an existing shader file."));
+ validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing shader file."), EditorValidationPanel::MSG_OK);
}
} else {
set_ok_button_text(TTR("Create"));
- _msg_path_valid(false, TTR("Shader file already exists."));
-
- shader_ok = false;
- }
-
- get_ok_button()->set_disabled(!shader_ok);
-
- Callable entered_call = callable_mp(this, &ShaderCreateDialog::_path_submitted);
- if (shader_ok) {
- if (!file_path->is_connected("text_submitted", entered_call)) {
- file_path->connect("text_submitted", entered_call);
- }
- } else if (file_path->is_connected("text_submitted", entered_call)) {
- file_path->disconnect("text_submitted", entered_call);
+ validation_panel->set_message(MSG_ID_PATH, TTR("Shader file already exists."), EditorValidationPanel::MSG_ERROR);
}
}
@@ -588,35 +555,22 @@ ShaderCreateDialog::ShaderCreateDialog() {
// Error Fields.
- VBoxContainer *vb = memnew(VBoxContainer);
-
- error_label = memnew(Label);
- vb->add_child(error_label);
-
- path_error_label = memnew(Label);
- vb->add_child(path_error_label);
-
- builtin_warning_label = memnew(Label);
- builtin_warning_label->set_text(
- TTR("Note: Built-in shaders can't be edited using an external editor."));
- vb->add_child(builtin_warning_label);
- builtin_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
- builtin_warning_label->hide();
-
- status_panel = memnew(PanelContainer);
- status_panel->set_h_size_flags(Control::SIZE_FILL);
- status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- status_panel->add_child(vb);
+ validation_panel = memnew(EditorValidationPanel);
+ validation_panel->add_line(MSG_ID_SHADER, TTR("Shader path/name is valid."));
+ validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new shader file."));
+ validation_panel->add_line(MSG_ID_BUILT_IN);
+ validation_panel->set_update_callback(callable_mp(this, &ShaderCreateDialog::_update_dialog));
+ validation_panel->set_accept_button(get_ok_button());
// Spacing.
Control *spacing = memnew(Control);
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
- vb = memnew(VBoxContainer);
+ VBoxContainer *vb = memnew(VBoxContainer);
vb->add_child(gc);
vb->add_child(spacing);
- vb->add_child(status_panel);
+ vb->add_child(validation_panel);
add_child(vb);
// Type.
diff --git a/editor/shader_create_dialog.h b/editor/shader_create_dialog.h
index 729a7b5bd4..d6d9f10020 100644
--- a/editor/shader_create_dialog.h
+++ b/editor/shader_create_dialog.h
@@ -40,10 +40,17 @@
#include "scene/gui/panel_container.h"
class EditorFileDialog;
+class EditorValidationPanel;
class ShaderCreateDialog : public ConfirmationDialog {
GDCLASS(ShaderCreateDialog, ConfirmationDialog);
+ enum {
+ MSG_ID_SHADER,
+ MSG_ID_PATH,
+ MSG_ID_BUILT_IN,
+ };
+
struct ShaderTypeData {
List<String> extensions;
String default_extension;
@@ -53,10 +60,7 @@ class ShaderCreateDialog : public ConfirmationDialog {
List<ShaderTypeData> type_data;
GridContainer *gc = nullptr;
- Label *error_label = nullptr;
- Label *path_error_label = nullptr;
- Label *builtin_warning_label = nullptr;
- PanelContainer *status_panel = nullptr;
+ EditorValidationPanel *validation_panel = nullptr;
OptionButton *type_menu = nullptr;
OptionButton *mode_menu = nullptr;
OptionButton *template_menu = nullptr;
@@ -67,6 +71,7 @@ class ShaderCreateDialog : public ConfirmationDialog {
AcceptDialog *alert = nullptr;
String initial_base_path;
+ String path_error;
bool is_new_shader_created = true;
bool is_path_valid = false;
bool is_built_in = false;
@@ -93,8 +98,6 @@ class ShaderCreateDialog : public ConfirmationDialog {
virtual void ok_pressed() override;
void _create_new();
void _load_exist();
- void _msg_script_valid(bool valid, const String &p_msg = String());
- void _msg_path_valid(bool valid, const String &p_msg = String());
void _update_dialog();
protected:
diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp
index ed242e7faa..33e257a5c2 100644
--- a/modules/noise/noise_texture_3d.cpp
+++ b/modules/noise/noise_texture_3d.cpp
@@ -110,6 +110,7 @@ void NoiseTexture3D::_set_texture_data(const TypedArray<Image> &p_data) {
} else {
texture = RS::get_singleton()->texture_3d_create(data[0]->get_format(), data[0]->get_width(), data[0]->get_height(), data.size(), false, data);
}
+ format = data[0]->get_format();
}
emit_changed();
}
@@ -346,6 +347,5 @@ Vector<Ref<Image>> NoiseTexture3D::get_data() const {
}
Image::Format NoiseTexture3D::get_format() const {
- ERR_FAIL_COND_V(!texture.is_valid(), Image::FORMAT_L8);
- return RS::get_singleton()->texture_3d_get(texture)[0]->get_format();
+ return format;
}
diff --git a/modules/noise/noise_texture_3d.h b/modules/noise/noise_texture_3d.h
index 397711ca98..13125efe7f 100644
--- a/modules/noise/noise_texture_3d.h
+++ b/modules/noise/noise_texture_3d.h
@@ -60,6 +60,8 @@ private:
Ref<Gradient> color_ramp;
Ref<Noise> noise;
+ Image::Format format = Image::FORMAT_L8;
+
void _thread_done(const TypedArray<Image> &p_data);
static void _thread_function(void *p_ud);
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 2ea7a8d6ae..3aa2a71a2c 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -510,7 +510,7 @@ StringName Area2D::get_audio_bus_name() const {
return audio_bus;
}
}
- return "Master";
+ return SceneStringNames::get_singleton()->Master;
}
void Area2D::_validate_property(PropertyInfo &p_property) const {
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 343dd82b87..dac31058bd 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -116,10 +116,10 @@ StringName AudioStreamPlayer2D::_get_actual_bus() {
//check if any area is diverting sound into a bus
Ref<World2D> world_2d = get_world_2d();
- ERR_FAIL_COND_V(world_2d.is_null(), SNAME("Master"));
+ ERR_FAIL_COND_V(world_2d.is_null(), SceneStringNames::get_singleton()->Master);
PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space());
- ERR_FAIL_COND_V(space_state == nullptr, SNAME("Master"));
+ ERR_FAIL_COND_V(space_state == nullptr, SceneStringNames::get_singleton()->Master);
PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS];
PhysicsDirectSpaceState2D::PointParameters point_params;
@@ -316,7 +316,7 @@ StringName AudioStreamPlayer2D::get_bus() const {
return default_bus;
}
}
- return SNAME("Master");
+ return SceneStringNames::get_singleton()->Master;
}
void AudioStreamPlayer2D::set_autoplay(bool p_enable) {
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 9b23fd3943..a4677bef36 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -32,6 +32,7 @@
#define AUDIO_STREAM_PLAYER_2D_H
#include "scene/2d/node_2d.h"
+#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
#include "servers/audio_server.h"
@@ -66,7 +67,7 @@ private:
float volume_db = 0.0;
float pitch_scale = 1.0;
bool autoplay = false;
- StringName default_bus = SNAME("Master");
+ StringName default_bus = SceneStringNames::get_singleton()->Master;
int max_polyphony = 1;
void _set_playing(bool p_enable);
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index 29d151b726..beb6892435 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -573,7 +573,7 @@ StringName Area3D::get_audio_bus_name() const {
return audio_bus;
}
}
- return "Master";
+ return SceneStringNames::get_singleton()->Master;
}
void Area3D::set_use_reverb_bus(bool p_enable) {
@@ -594,7 +594,7 @@ StringName Area3D::get_reverb_bus_name() const {
return reverb_bus;
}
}
- return "Master";
+ return SceneStringNames::get_singleton()->Master;
}
void Area3D::set_reverb_amount(float p_amount) {
diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h
index 454c8c2960..86602d3192 100644
--- a/scene/3d/area_3d.h
+++ b/scene/3d/area_3d.h
@@ -33,6 +33,7 @@
#include "core/templates/vset.h"
#include "scene/3d/collision_object_3d.h"
+#include "scene/scene_string_names.h"
class Area3D : public CollisionObject3D {
GDCLASS(Area3D, CollisionObject3D);
@@ -134,10 +135,10 @@ private:
void _clear_monitoring();
bool audio_bus_override = false;
- StringName audio_bus = "Master";
+ StringName audio_bus = SceneStringNames::get_singleton()->Master;
bool use_reverb_bus = false;
- StringName reverb_bus = "Master";
+ StringName reverb_bus = SceneStringNames::get_singleton()->Master;
float reverb_amount = 0.0;
float reverb_uniformity = 0.0;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index ac626d0d2a..8147521a01 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -35,6 +35,7 @@
#include "scene/3d/audio_listener_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/main/viewport.h"
+#include "scene/scene_string_names.h"
// Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004)
// Speaker-Placement Correction Amplitude Panning (SPCAP)
@@ -635,7 +636,7 @@ StringName AudioStreamPlayer3D::get_bus() const {
return bus;
}
}
- return SNAME("Master");
+ return SceneStringNames::get_singleton()->Master;
}
void AudioStreamPlayer3D::set_autoplay(bool p_enable) {
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 6c37d6f81d..ae68bd719b 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -200,7 +200,7 @@ StringName AudioStreamPlayer::get_bus() const {
return bus;
}
}
- return SNAME("Master");
+ return SceneStringNames::get_singleton()->Master;
}
void AudioStreamPlayer::set_autoplay(bool p_enable) {
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index d1f6fca2ee..c0c25aa983 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -33,6 +33,7 @@
#include "core/templates/safe_refcount.h"
#include "scene/main/node.h"
+#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
class AudioStreamPlayer : public Node {
@@ -54,7 +55,7 @@ private:
float pitch_scale = 1.0;
float volume_db = 0.0;
bool autoplay = false;
- StringName bus = SNAME("Master");
+ StringName bus = SceneStringNames::get_singleton()->Master;
int max_polyphony = 1;
MixTarget mix_target = MIX_TARGET_STEREO;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 430569432a..738778b516 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -118,6 +118,7 @@ void Button::_notification(int p_what) {
Ref<StyleBox> style = theme_cache.normal;
bool rtl = is_layout_rtl();
+ const bool is_clipped = clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING;
switch (get_draw_mode()) {
case DRAW_NORMAL: {
@@ -283,7 +284,7 @@ void Button::_notification(int p_what) {
Size2 _size = get_size() - style->get_offset() * 2;
int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation;
_size.width -= icon_text_separation + icon_ofs_region;
- if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
+ if (!is_clipped && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
_size.width -= text_buf->get_size().width;
}
if (vertical_icon_alignment != VERTICAL_ALIGNMENT_CENTER) {
@@ -335,9 +336,9 @@ void Button::_notification(int p_what) {
text_clip -= _internal_margin[SIDE_RIGHT] + theme_cache.h_separation;
}
- text_buf->set_width((clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? text_clip : -1);
+ text_buf->set_width(is_clipped ? text_clip : -1);
- int text_width = MAX(1, (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
+ int text_width = MAX(1, is_clipped ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 2a0f85a1be..3c19766ca7 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -71,16 +71,14 @@ void ColorPicker::_notification(int p_what) {
for (int i = 0; i < SLIDER_COUNT; i++) {
labels[i]->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
sliders[i]->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
- set_offset((Side)i, get_offset((Side)i) + theme_cache.content_margin);
}
alpha_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
alpha_label->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
- set_offset((Side)0, get_offset((Side)0) + theme_cache.content_margin);
for (int i = 0; i < MODE_BUTTON_COUNT; i++) {
- mode_btns[i]->add_theme_style_override("pressed", theme_cache.mode_button_pressed);
- mode_btns[i]->add_theme_style_override("normal", theme_cache.mode_button_normal);
- mode_btns[i]->add_theme_style_override("hover", theme_cache.mode_button_hover);
+ mode_btns[i]->add_theme_style_override(SNAME("pressed"), theme_cache.mode_button_pressed);
+ mode_btns[i]->add_theme_style_override(SNAME("normal"), theme_cache.mode_button_normal);
+ mode_btns[i]->add_theme_style_override(SNAME("hover"), theme_cache.mode_button_hover);
}
shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_HSV_RECTANGLE), theme_cache.shape_rect);
@@ -88,6 +86,11 @@ void ColorPicker::_notification(int p_what) {
shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_VHS_CIRCLE), theme_cache.shape_circle);
shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_OKHSL_CIRCLE), theme_cache.shape_circle);
+ internal_margin->add_theme_constant_override(SNAME("margin_bottom"), theme_cache.content_margin);
+ internal_margin->add_theme_constant_override(SNAME("margin_left"), theme_cache.content_margin);
+ internal_margin->add_theme_constant_override(SNAME("margin_right"), theme_cache.content_margin);
+ internal_margin->add_theme_constant_override(SNAME("margin_top"), theme_cache.content_margin);
+
_reset_sliders_theme();
if (Engine::get_singleton()->is_editor_hint()) {
@@ -100,13 +103,6 @@ void ColorPicker::_notification(int p_what) {
_update_controls();
} break;
- case NOTIFICATION_VISIBILITY_CHANGED: {
- Popup *p = Object::cast_to<Popup>(get_parent());
- if (p && is_visible_in_tree()) {
- p->set_size(Size2(get_combined_minimum_size().width + theme_cache.content_margin * 2, get_combined_minimum_size().height + theme_cache.content_margin * 2));
- }
- } break;
-
case NOTIFICATION_WM_CLOSE_REQUEST: {
if (picker_window != nullptr && picker_window->is_visible()) {
picker_window->hide();
@@ -1450,7 +1446,7 @@ void ColorPicker::_pick_button_pressed() {
picker_window = memnew(Popup);
picker_window->set_size(Vector2i(1, 1));
picker_window->connect("visibility_changed", callable_mp(this, &ColorPicker::_pick_finished));
- add_child(picker_window);
+ add_child(picker_window, false, INTERNAL_MODE_FRONT);
}
picker_window->popup();
}
@@ -1479,7 +1475,7 @@ void ColorPicker::_pick_button_pressed_legacy() {
picker_window = memnew(Popup);
picker_window->hide();
picker_window->set_transient(true);
- add_child(picker_window);
+ add_child(picker_window, false, INTERNAL_MODE_FRONT);
picker_texture_rect = memnew(TextureRect);
picker_texture_rect->set_anchors_preset(Control::PRESET_FULL_RECT);
@@ -1715,8 +1711,14 @@ void ColorPicker::_bind_methods() {
}
ColorPicker::ColorPicker() {
+ internal_margin = memnew(MarginContainer);
+ add_child(internal_margin, false, INTERNAL_MODE_FRONT);
+
+ VBoxContainer *real_vbox = memnew(VBoxContainer);
+ internal_margin->add_child(real_vbox);
+
HBoxContainer *hb_edit = memnew(HBoxContainer);
- add_child(hb_edit, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(hb_edit);
hb_edit->set_v_size_flags(SIZE_SHRINK_BEGIN);
uv_edit = memnew(Control);
@@ -1728,7 +1730,7 @@ ColorPicker::ColorPicker() {
uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, uv_edit));
sample_hbc = memnew(HBoxContainer);
- add_child(sample_hbc, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(sample_hbc);
btn_pick = memnew(Button);
sample_hbc->add_child(btn_pick);
@@ -1771,7 +1773,7 @@ ColorPicker::ColorPicker() {
add_mode(new ColorModeOKHSL(this));
mode_hbc = memnew(HBoxContainer);
- add_child(mode_hbc, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(mode_hbc);
mode_group.instantiate();
@@ -1806,11 +1808,11 @@ ColorPicker::ColorPicker() {
mode_popup->set_item_checked(MODE_MAX + 1, true);
mode_popup->connect("id_pressed", callable_mp(this, &ColorPicker::_set_mode_popup_value));
VBoxContainer *vbl = memnew(VBoxContainer);
- add_child(vbl, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(vbl);
VBoxContainer *vbr = memnew(VBoxContainer);
- add_child(vbr, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(vbr);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
slider_gc = memnew(GridContainer);
@@ -1900,9 +1902,9 @@ ColorPicker::ColorPicker() {
btn_preset->set_focus_mode(FOCUS_NONE);
btn_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
btn_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_preset, preset_container));
- add_child(btn_preset, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(btn_preset);
- add_child(preset_container, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(preset_container);
recent_preset_hbc = memnew(HBoxContainer);
recent_preset_hbc->set_v_size_flags(SIZE_SHRINK_BEGIN);
@@ -1917,9 +1919,9 @@ ColorPicker::ColorPicker() {
btn_recent_preset->set_focus_mode(FOCUS_NONE);
btn_recent_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
btn_recent_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_recent_preset, recent_preset_hbc));
- add_child(btn_recent_preset, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(btn_recent_preset);
- add_child(recent_preset_hbc, false, INTERNAL_MODE_FRONT);
+ real_vbox->add_child(recent_preset_hbc);
set_pick_color(Color(1, 1, 1));
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 1a4b1b6ee1..680b3f7d96 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -130,6 +130,7 @@ private:
Ref<StyleBoxFlat> picker_preview_style_box;
Color picker_color;
+ MarginContainer *internal_margin = nullptr;
Control *uv_edit = nullptr;
Control *w_edit = nullptr;
AspectRatioContainer *wheel_edit = nullptr;
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 8138a66f64..2b8c85c823 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -78,6 +78,7 @@ void OptionButton::_update_theme_item_cache() {
void OptionButton::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
+ _refresh_size_cache();
if (has_theme_icon(SNAME("arrow"))) {
if (is_layout_rtl()) {
_set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width());
@@ -446,7 +447,7 @@ void OptionButton::_refresh_size_cache() {
cache_refresh_pending = false;
if (fit_to_longest_item) {
- _cached_size = Vector2();
+ _cached_size = theme_cache.normal->get_minimum_size();
for (int i = 0; i < get_item_count(); i++) {
_cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(popup->get_item_xl_text(i), get_item_icon(i)));
}
@@ -595,7 +596,6 @@ OptionButton::OptionButton(const String &p_text) :
popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected));
popup->connect("id_focused", callable_mp(this, &OptionButton::_focused));
popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed).bind(false));
- _refresh_size_cache();
}
OptionButton::~OptionButton() {
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index f6e558fe57..ac09844128 100644
--- a/scene/gui/video_stream_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -451,7 +451,7 @@ StringName VideoStreamPlayer::get_bus() const {
return bus;
}
}
- return "Master";
+ return SceneStringNames::get_singleton()->Master;
}
void VideoStreamPlayer::_validate_property(PropertyInfo &p_property) const {
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index db9c1efa68..d8c5e9c007 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1733,6 +1733,9 @@ SceneTree::SceneTree() {
const bool transparent_background = GLOBAL_DEF("rendering/viewport/transparent_background", false);
root->set_transparent_background(transparent_background);
+ const bool use_hdr_2d = GLOBAL_DEF_RST_BASIC("rendering/viewport/hdr_2d", false);
+ root->set_use_hdr_2d(use_hdr_2d);
+
const int ssaa_mode = GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), 0);
root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode));
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 54444b6828..2d3aa66f2c 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1177,6 +1177,17 @@ bool Viewport::has_transparent_background() const {
return transparent_bg;
}
+void Viewport::set_use_hdr_2d(bool p_enable) {
+ ERR_MAIN_THREAD_GUARD;
+ use_hdr_2d = p_enable;
+ RS::get_singleton()->viewport_set_use_hdr_2d(viewport, p_enable);
+}
+
+bool Viewport::is_using_hdr_2d() const {
+ ERR_READ_THREAD_GUARD_V(false);
+ return use_hdr_2d;
+}
+
void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
ERR_MAIN_THREAD_GUARD;
if (world_2d == p_world_2d) {
@@ -2184,6 +2195,18 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
return;
}
+ if (p_event->is_action_pressed("ui_cancel")) {
+ // Cancel tooltip timer or hide tooltip when pressing Escape (this is standard behavior in most applications).
+ _gui_cancel_tooltip();
+ if (gui.tooltip_popup) {
+ // If a tooltip was hidden, prevent other actions associated with `ui_cancel` from occurring.
+ // For instance, this prevents the node from being deselected when pressing Escape
+ // to hide a documentation tooltip in the inspector.
+ set_input_as_handled();
+ return;
+ }
+ }
+
if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) {
gui.key_focus->release_focus();
}
@@ -4271,6 +4294,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_visible_rect"), &Viewport::get_visible_rect);
ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background);
ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background);
+ ClassDB::bind_method(D_METHOD("set_use_hdr_2d", "enable"), &Viewport::set_use_hdr_2d);
+ ClassDB::bind_method(D_METHOD("is_using_hdr_2d"), &Viewport::is_using_hdr_2d);
ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d);
ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d);
@@ -4435,6 +4460,8 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Lighting,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr_2d"), "set_use_hdr_2d", "is_using_hdr_2d");
+
#ifndef _3D_DISABLED
ADD_GROUP("Scaling 3D", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index a8cb2ec24a..331ce98cdd 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -243,6 +243,7 @@ private:
Rect2 last_vp_rect;
bool transparent_bg = false;
+ bool use_hdr_2d = false;
bool gen_mipmaps = false;
bool snap_controls_to_pixels = true;
@@ -526,6 +527,9 @@ public:
void set_transparent_background(bool p_enable);
bool has_transparent_background() const;
+ void set_use_hdr_2d(bool p_enable);
+ bool is_using_hdr_2d() const;
+
Ref<ViewportTexture> get_texture() const;
void set_positional_shadow_atlas_size(int p_size);
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index d0658c489d..875b53203a 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -670,9 +670,14 @@ void Window::_propagate_window_notification(Node *p_node, int p_notification) {
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);
Window *root = get_tree()->get_root();
- DEV_ASSERT(!root->gui.windowmanager_window_over); // Entering a window while a window is hovered should never happen.
+ if (root->gui.windowmanager_window_over) {
+#ifdef DEV_ENABLED
+ WARN_PRINT_ONCE("Entering a window while a window is hovered should never happen in DisplayServer.");
+#endif // DEV_ENABLED
+ root->gui.windowmanager_window_over->_event_callback(DisplayServer::WINDOW_EVENT_MOUSE_EXIT);
+ }
+ _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
root->gui.windowmanager_window_over = this;
notification(NOTIFICATION_VP_MOUSE_ENTER);
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
@@ -681,7 +686,12 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
} break;
case DisplayServer::WINDOW_EVENT_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.
+ if (!root->gui.windowmanager_window_over) {
+#ifdef DEV_ENABLED
+ WARN_PRINT_ONCE("Exiting a window while no window is hovered should never happen in DisplayServer.");
+#endif // DEV_ENABLED
+ return;
+ }
root->gui.windowmanager_window_over->_mouse_leave_viewport();
root->gui.windowmanager_window_over = nullptr;
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index f0971f1a34..cf8ece0d4c 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -193,6 +193,9 @@ SceneStringNames::SceneStringNames() {
path_pp = NodePath("..");
+ // Audio bus name.
+ Master = StaticCString::create("Master");
+
_default = StaticCString::create("default");
_window_group = StaticCString::create("_window_group");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index f31cf7b881..10b71e2a2a 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -204,6 +204,8 @@ public:
StringName output;
+ StringName Master;
+
StringName parameters_base_path;
StringName _window_group;
diff --git a/scu_builders.py b/scu_builders.py
index 5f7821655b..e76a58bd88 100644
--- a/scu_builders.py
+++ b/scu_builders.py
@@ -8,8 +8,8 @@ from os.path import normpath, basename
base_folder_path = str(Path(__file__).parent) + "/"
base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
_verbose = False
-_is_release_build = False
_scu_folders = set()
+_max_includes_per_scu = 1024
def clear_out_existing_files(output_folder, extension):
@@ -197,13 +197,14 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension=
# adjust number of output files according to whether DEV or release
num_output_files = 1
- if _is_release_build:
- # always have a maximum in release
- includes_per_scu = 8
- num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1)
+
+ if includes_per_scu == 0:
+ includes_per_scu = _max_includes_per_scu
else:
- if includes_per_scu > 0:
- num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1)
+ if includes_per_scu > _max_includes_per_scu:
+ includes_per_scu = _max_includes_per_scu
+
+ num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1)
lines_per_file = math.ceil(total_lines / float(num_output_files))
lines_per_file = max(lines_per_file, 1)
@@ -241,15 +242,15 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension=
)
-def generate_scu_files(verbose, is_release_build):
+def generate_scu_files(verbose, max_includes_per_scu):
print("=============================")
print("Single Compilation Unit Build")
print("=============================")
- print("Generating SCU build files")
global _verbose
_verbose = verbose
- global _is_release_build
- _is_release_build = is_release_build
+ global _max_includes_per_scu
+ _max_includes_per_scu = max_includes_per_scu
+ print("Generating SCU build files... (max includes per scu " + str(_max_includes_per_scu) + ")")
curr_folder = os.path.abspath("./")
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 49991e41d3..2308fcca9e 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -40,6 +40,7 @@
#include "core/string/string_name.h"
#include "core/templates/pair.h"
#include "scene/resources/audio_stream_wav.h"
+#include "scene/scene_string_names.h"
#include "servers/audio/audio_driver_dummy.h"
#include "servers/audio/effects/audio_effect_compressor.h"
@@ -747,7 +748,7 @@ void AudioServer::set_bus_count(int p_count) {
buses[i]->bypass = false;
buses[i]->volume_db = 0;
if (i > 0) {
- buses[i]->send = "Master";
+ buses[i]->send = SceneStringNames::get_singleton()->Master;
}
bus_map[attempt] = buses[i];
@@ -1582,7 +1583,7 @@ void AudioServer::set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout) {
for (int i = 0; i < p_bus_layout->buses.size(); i++) {
Bus *bus = memnew(Bus);
if (i == 0) {
- bus->name = "Master";
+ bus->name = SceneStringNames::get_singleton()->Master;
} else {
bus->name = p_bus_layout->buses[i].name;
bus->send = p_bus_layout->buses[i].send;
@@ -1891,5 +1892,5 @@ void AudioBusLayout::_get_property_list(List<PropertyInfo> *p_list) const {
AudioBusLayout::AudioBusLayout() {
buses.resize(1);
- buses.write[0].name = "Master";
+ buses.write[0].name = SceneStringNames::get_singleton()->Master;
}
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index 71a1801de9..d9bde5903c 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -177,6 +177,8 @@ public:
virtual void render_target_set_as_unused(RID p_render_target) override {}
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override {}
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override { return RS::VIEWPORT_MSAA_DISABLED; }
+ virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {}
+ virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; }
virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override {}
virtual bool render_target_is_clear_requested(RID p_render_target) override { return false; }
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
index 38a86a6cdc..2c537c3a1c 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
@@ -89,12 +89,12 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant));
- tonemap.push_constant.use_bcs = p_settings.use_bcs;
+ tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0;
tonemap.push_constant.bcs[0] = p_settings.brightness;
tonemap.push_constant.bcs[1] = p_settings.contrast;
tonemap.push_constant.bcs[2] = p_settings.saturation;
- tonemap.push_constant.use_glow = p_settings.use_glow;
+ tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0;
tonemap.push_constant.glow_intensity = p_settings.glow_intensity;
tonemap.push_constant.glow_map_strength = p_settings.glow_map_strength;
tonemap.push_constant.glow_levels[0] = p_settings.glow_levels[0]; // clean this up to just pass by pointer or something
@@ -114,19 +114,21 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
}
tonemap.push_constant.tonemapper = p_settings.tonemap_mode;
- tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure;
+ tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0;
tonemap.push_constant.exposure = p_settings.exposure;
tonemap.push_constant.white = p_settings.white;
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
- tonemap.push_constant.use_color_correction = p_settings.use_color_correction;
+ tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
- tonemap.push_constant.use_fxaa = p_settings.use_fxaa;
- tonemap.push_constant.use_debanding = p_settings.use_debanding;
+ tonemap.push_constant.flags |= p_settings.use_fxaa ? TONEMAP_FLAG_USE_FXAA : 0;
+ tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0;
tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x;
tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y;
+ tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0;
+
if (p_settings.view_count > 1) {
// Use MULTIVIEW versions
mode += 6;
@@ -184,13 +186,13 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant));
- tonemap.push_constant.use_bcs = p_settings.use_bcs;
+ tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0;
tonemap.push_constant.bcs[0] = p_settings.brightness;
tonemap.push_constant.bcs[1] = p_settings.contrast;
tonemap.push_constant.bcs[2] = p_settings.saturation;
ERR_FAIL_COND_MSG(p_settings.use_glow, "Glow is not supported when using subpasses.");
- tonemap.push_constant.use_glow = p_settings.use_glow;
+ tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0;
int mode = p_settings.use_1d_color_correction ? TONEMAP_MODE_SUBPASS_1D_LUT : TONEMAP_MODE_SUBPASS;
if (p_settings.view_count > 1) {
@@ -199,16 +201,18 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
}
tonemap.push_constant.tonemapper = p_settings.tonemap_mode;
- tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure;
+ tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0;
tonemap.push_constant.exposure = p_settings.exposure;
tonemap.push_constant.white = p_settings.white;
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
- tonemap.push_constant.use_color_correction = p_settings.use_color_correction;
+ tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
- tonemap.push_constant.use_debanding = p_settings.use_debanding;
+ tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0;
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
+ tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0;
+
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h
index afd2f8e401..a1a99f931f 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.h
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.h
@@ -59,14 +59,23 @@ private:
TONEMAP_MODE_MAX
};
+ enum {
+ TONEMAP_FLAG_USE_BCS = (1 << 0),
+ TONEMAP_FLAG_USE_GLOW = (1 << 1),
+ TONEMAP_FLAG_USE_AUTO_EXPOSURE = (1 << 2),
+ TONEMAP_FLAG_USE_COLOR_CORRECTION = (1 << 3),
+ TONEMAP_FLAG_USE_FXAA = (1 << 4),
+ TONEMAP_FLAG_USE_DEBANDING = (1 << 5),
+ TONEMAP_FLAG_CONVERT_TO_SRGB = (1 << 6),
+ };
+
struct TonemapPushConstant {
float bcs[3]; // 12 - 12
- uint32_t use_bcs; // 4 - 16
+ uint32_t flags; // 4 - 16
- uint32_t use_glow; // 4 - 20
- uint32_t use_auto_exposure; // 4 - 24
- uint32_t use_color_correction; // 4 - 28
- uint32_t tonemapper; // 4 - 32
+ float pixel_size[2]; // 8 - 24
+ uint32_t tonemapper; // 4 - 28
+ uint32_t pad; // 4 - 32
uint32_t glow_texture_size[2]; // 8 - 40
float glow_intensity; // 4 - 44
@@ -79,10 +88,6 @@ private:
float white; // 4 - 88
float auto_exposure_scale; // 4 - 92
float luminance_multiplier; // 4 - 96
-
- float pixel_size[2]; // 8 - 104
- uint32_t use_fxaa; // 4 - 108
- uint32_t use_debanding; // 4 - 112
};
/* tonemap actually writes to a framebuffer, which is
@@ -141,6 +146,8 @@ public:
bool use_debanding = false;
Vector2i texture_size;
uint32_t view_count = 1;
+
+ bool convert_to_srgb = false;
};
void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp
index 98d826b1f9..8402cc7444 100644
--- a/servers/rendering/renderer_rd/environment/fog.cpp
+++ b/servers/rendering/renderer_rd/environment/fog.cpp
@@ -143,7 +143,7 @@ Vector3 Fog::fog_volume_get_size(RID p_fog_volume) const {
bool Fog::FogMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
uniform_set_updated = true;
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, Fog::get_singleton()->volumetric_fog.shader.version_get_shader(shader_data->version, 0), VolumetricFogShader::FogSet::FOG_SET_MATERIAL, true);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, Fog::get_singleton()->volumetric_fog.shader.version_get_shader(shader_data->version, 0), VolumetricFogShader::FogSet::FOG_SET_MATERIAL, true, true);
}
Fog::FogMaterialData::~FogMaterialData() {
diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp
index 5fc0393080..f94c701ada 100644
--- a/servers/rendering/renderer_rd/environment/sky.cpp
+++ b/servers/rendering/renderer_rd/environment/sky.cpp
@@ -185,7 +185,7 @@ bool SkyRD::SkyMaterialData::update_parameters(const HashMap<StringName, Variant
uniform_set_updated = true;
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, scene_singleton->sky.sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL, true);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, scene_singleton->sky.sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL, true, true);
}
SkyRD::SkyMaterialData::~SkyMaterialData() {
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 4381ac3f36..9c7990d679 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1769,7 +1769,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
case RS::ENV_BG_CANVAS: {
if (!is_reflection_probe) {
RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target());
- copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true);
+ bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target());
+ copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear);
}
keep_color = true;
} break;
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 298a6c0752..12f8f6a366 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
@@ -436,7 +436,7 @@ void SceneShaderForwardClustered::MaterialData::set_next_pass(RID p_pass) {
bool SceneShaderForwardClustered::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, RD::BARRIER_MASK_RASTER);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, true, RD::BARRIER_MASK_RASTER);
}
SceneShaderForwardClustered::MaterialData::~MaterialData() {
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 2053c3a9a6..befb2c5504 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -722,16 +722,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
using_subpass_post_process = false;
}
- // We do this last because our get_color_fbs creates and caches the framebuffer if we need it.
- RID four_subpasses = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES);
- if (using_subpass_post_process && four_subpasses.is_null()) {
- // can't do blit subpass because we don't have all subpasses
- using_subpass_post_process = false;
- }
-
if (using_subpass_post_process) {
// all as subpasses
- framebuffer = four_subpasses;
+ framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES);
} else if (using_subpass_transparent) {
// our tonemap pass is separate
framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_THREE_SUBPASSES);
@@ -803,7 +796,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
if (rb_data.is_valid()) {
RID dest_framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_ONE_PASS);
RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target());
- copy_effects->copy_to_fb_rect(texture, dest_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true);
+ bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target());
+ copy_effects->copy_to_fb_rect(texture, dest_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear);
}
keep_color = true;
} break;
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index e4498ac533..32d2289f75 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -388,7 +388,7 @@ void SceneShaderForwardMobile::MaterialData::set_next_pass(RID p_pass) {
bool SceneShaderForwardMobile::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, RD::BARRIER_MASK_RASTER);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, true, RD::BARRIER_MASK_RASTER);
}
SceneShaderForwardMobile::MaterialData::~MaterialData() {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index eb33f296d0..773ea9098a 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -363,7 +363,7 @@ void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RI
bool use_normal;
bool use_specular;
- bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, uniform_set, size, specular_shininess, use_normal, use_specular);
+ bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular);
//something odd happened
if (!success) {
_bind_canvas_texture(p_draw_list, default_canvas_texture, p_base_filter, p_base_repeat, r_last_texture, push_constant, r_texpixel_size);
@@ -421,6 +421,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
_update_transform_2d_to_mat2x3(base_transform, push_constant.world);
Color base_color = p_item->final_modulate;
+ bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_render_target);
for (int i = 0; i < 4; i++) {
push_constant.modulation[i] = 0;
@@ -441,6 +442,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.lights[3] = 0;
uint32_t base_flags = 0;
+ base_flags |= use_linear_colors ? FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR : 0;
uint16_t light_count = 0;
PipelineLightMode light_mode;
@@ -566,10 +568,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.flags |= FLAGS_USE_LCD;
}
- push_constant.modulation[0] = rect->modulate.r * base_color.r;
- push_constant.modulation[1] = rect->modulate.g * base_color.g;
- push_constant.modulation[2] = rect->modulate.b * base_color.b;
- push_constant.modulation[3] = rect->modulate.a * base_color.a;
+ Color modulated = rect->modulate * base_color;
+ if (use_linear_colors) {
+ modulated = modulated.srgb_to_linear();
+ }
+
+ push_constant.modulation[0] = modulated.r;
+ push_constant.modulation[1] = modulated.g;
+ push_constant.modulation[2] = modulated.b;
+ push_constant.modulation[3] = modulated.a;
push_constant.src_rect[0] = src_rect.position.x;
push_constant.src_rect[1] = src_rect.position.y;
@@ -618,10 +625,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
}
}
- push_constant.modulation[0] = np->color.r * base_color.r;
- push_constant.modulation[1] = np->color.g * base_color.g;
- push_constant.modulation[2] = np->color.b * base_color.b;
- push_constant.modulation[3] = np->color.a * base_color.a;
+ Color modulated = np->color * base_color;
+ if (use_linear_colors) {
+ modulated = modulated.srgb_to_linear();
+ }
+
+ push_constant.modulation[0] = modulated.r;
+ push_constant.modulation[1] = modulated.g;
+ push_constant.modulation[2] = modulated.b;
+ push_constant.modulation[3] = modulated.a;
push_constant.src_rect[0] = src_rect.position.x;
push_constant.src_rect[1] = src_rect.position.y;
@@ -676,10 +688,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
_bind_canvas_texture(p_draw_list, polygon->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size);
- push_constant.modulation[0] = base_color.r;
- push_constant.modulation[1] = base_color.g;
- push_constant.modulation[2] = base_color.b;
- push_constant.modulation[3] = base_color.a;
+ Color color = base_color;
+ if (use_linear_colors) {
+ color = color.srgb_to_linear();
+ }
+
+ push_constant.modulation[0] = color.r;
+ push_constant.modulation[1] = color.g;
+ push_constant.modulation[2] = color.b;
+ push_constant.modulation[3] = color.a;
for (int j = 0; j < 4; j++) {
push_constant.src_rect[j] = 0;
@@ -718,6 +735,9 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.uvs[j * 2 + 0] = primitive->uvs[j].x;
push_constant.uvs[j * 2 + 1] = primitive->uvs[j].y;
Color col = primitive->colors[j] * base_color;
+ if (use_linear_colors) {
+ col = col.srgb_to_linear();
+ }
push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
@@ -732,6 +752,9 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.uvs[j * 2 + 0] = primitive->uvs[j + 1].x;
push_constant.uvs[j * 2 + 1] = primitive->uvs[j + 1].y;
Color col = primitive->colors[j + 1] * base_color;
+ if (use_linear_colors) {
+ col = col.srgb_to_linear();
+ }
push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
@@ -849,10 +872,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh);
static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP };
- push_constant.modulation[0] = base_color.r * modulate.r;
- push_constant.modulation[1] = base_color.g * modulate.g;
- push_constant.modulation[2] = base_color.b * modulate.b;
- push_constant.modulation[3] = base_color.a * modulate.a;
+ Color modulated = modulate * base_color;
+ if (use_linear_colors) {
+ modulated = modulated.srgb_to_linear();
+ }
+
+ push_constant.modulation[0] = modulated.r;
+ push_constant.modulation[1] = modulated.g;
+ push_constant.modulation[2] = modulated.b;
+ push_constant.modulation[3] = modulated.a;
for (int j = 0; j < 4; j++) {
push_constant.src_rect[j] = 0;
@@ -1113,8 +1141,9 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) {
pipeline_variants = &material_data->shader_data->pipeline_variants;
// Update uniform set.
- if (material_data->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set.
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_data->uniform_set, MATERIAL_UNIFORM_SET);
+ RID uniform_set = texture_storage->render_target_is_using_hdr(p_to_render_target) ? material_data->uniform_set : material_data->uniform_set_srgb;
+ if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { // Material may not have a uniform set.
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set, MATERIAL_UNIFORM_SET);
material_data->set_as_used();
}
} else {
@@ -2235,12 +2264,14 @@ RendererRD::MaterialStorage::ShaderData *RendererCanvasRenderRD::_create_shader_
bool RendererCanvasRenderRD::CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
-
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false);
+ bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, true, false, RD::BARRIER_MASK_ALL_BARRIERS);
+ bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false, false, RD::BARRIER_MASK_ALL_BARRIERS);
+ return uniform_set_changed || uniform_set_srgb_changed;
}
RendererCanvasRenderRD::CanvasMaterialData::~CanvasMaterialData() {
free_parameters_uniform_set(uniform_set);
+ free_parameters_uniform_set(uniform_set_srgb);
}
RendererRD::MaterialStorage::MaterialData *RendererCanvasRenderRD::_create_material_func(CanvasShaderData *p_shader) {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 1116bed6e4..af8736a445 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -75,6 +75,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
FLAGS_CLIP_RECT_UV = (1 << 9),
FLAGS_TRANSPOSE_RECT = (1 << 10),
+ FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR = (1 << 11),
+
FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
FLAGS_USING_PARTICLES = (1 << 13),
@@ -195,6 +197,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
struct CanvasMaterialData : public RendererRD::MaterialStorage::MaterialData {
CanvasShaderData *shader_data = nullptr;
RID uniform_set;
+ RID uniform_set_srgb;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
index 870da3c321..4ccd2aa322 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
@@ -81,6 +81,7 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
blit.push_constant.k2 = p_render_targets[i].lens_distortion.k2;
blit.push_constant.upscale = p_render_targets[i].lens_distortion.upscale;
blit.push_constant.aspect_ratio = p_render_targets[i].lens_distortion.aspect_ratio;
+ blit.push_constant.convert_to_srgb = texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
@@ -171,7 +172,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
RID texture = texture_storage->texture_allocate();
texture_storage->texture_2d_initialize(texture, p_image);
- RID rd_texture = texture_storage->texture_get_rd_texture(texture);
+ RID rd_texture = texture_storage->texture_get_rd_texture(texture, false);
RD::SamplerState sampler_state;
sampler_state.min_filter = p_use_filter ? RD::SAMPLER_FILTER_LINEAR : RD::SAMPLER_FILTER_NEAREST;
@@ -237,6 +238,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
blit.push_constant.k2 = 0;
blit.push_constant.upscale = 1.0;
blit.push_constant.aspect_ratio = 1.0;
+ blit.push_constant.convert_to_srgb = false;
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h
index 14fb4e6340..705fb9e8e5 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.h
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h
@@ -80,7 +80,7 @@ protected:
float upscale;
float aspect_ratio;
uint32_t layer;
- uint32_t pad1;
+ uint32_t convert_to_srgb;
};
struct Blit {
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 326f619689..9d4d266a7a 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -552,6 +552,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier();
tonemap.view_count = rb->get_view_count();
+ tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(render_target);
+
RID dest_fb;
if (fsr && can_use_effects && rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR) {
// If we use FSR to upscale we need to write our result into an intermediate buffer.
@@ -647,6 +649,8 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr
tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier();
tonemap.view_count = rb->get_view_count();
+ tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(rb->get_render_target());
+
tone_mapper->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap);
RD::get_singleton()->draw_command_end_label();
diff --git a/servers/rendering/renderer_rd/shaders/blit.glsl b/servers/rendering/renderer_rd/shaders/blit.glsl
index 14f190a49f..d451647bec 100644
--- a/servers/rendering/renderer_rd/shaders/blit.glsl
+++ b/servers/rendering/renderer_rd/shaders/blit.glsl
@@ -45,7 +45,7 @@ layout(push_constant, std140) uniform Pos {
float upscale;
float aspect_ratio;
uint layer;
- uint pad1;
+ bool convert_to_srgb;
}
data;
@@ -59,6 +59,13 @@ layout(binding = 0) uniform sampler2DArray src_rt;
layout(binding = 0) uniform sampler2D src_rt;
#endif
+vec3 linear_to_srgb(vec3 color) {
+ // If going to srgb, clamp from 0 to 1.
+ color = clamp(color, vec3(0.0), vec3(1.0));
+ const vec3 a = vec3(0.055f);
+ return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
void main() {
#ifdef APPLY_LENS_DISTORTION
vec2 coords = uv * 2.0 - 1.0;
@@ -94,4 +101,8 @@ void main() {
#else
color = texture(src_rt, uv);
#endif
+
+ if (data.convert_to_srgb) {
+ color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
+ }
}
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 6270450c15..31c5aadc88 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -36,6 +36,12 @@ layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
#GLOBALS
+#ifdef USE_ATTRIBUTES
+vec3 srgb_to_linear(vec3 color) {
+ return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
+}
+#endif
+
void main() {
vec4 instance_custom = vec4(0.0);
#ifdef USE_PRIMITIVE
@@ -65,7 +71,11 @@ void main() {
#elif defined(USE_ATTRIBUTES)
vec2 vertex = vertex_attrib;
- vec4 color = color_attrib * draw_data.modulation;
+ vec4 color = color_attrib;
+ if (bool(draw_data.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR)) {
+ color.rgb = srgb_to_linear(color.rgb);
+ }
+ color *= draw_data.modulation;
vec2 uv = uv_attrib;
uvec4 bones = bone_attrib;
@@ -563,9 +573,6 @@ void main() {
}
vec4 base_color = color;
- if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) {
- color = vec4(0.0); //invisible by default due to using light mask
- }
#ifdef MODE_LIGHT_ONLY
float light_only_alpha = 0.0;
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index 2dd5abb75f..7ac7cf9c07 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -12,7 +12,7 @@
#define FLAGS_CLIP_RECT_UV (1 << 9)
#define FLAGS_TRANSPOSE_RECT (1 << 10)
-#define FLAGS_USING_LIGHT_MASK (1 << 11)
+#define FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR (1 << 11)
#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12)
#define FLAGS_USING_PARTICLES (1 << 13)
diff --git a/servers/rendering/renderer_rd/shaders/effects/copy.glsl b/servers/rendering/renderer_rd/shaders/effects/copy.glsl
index 3a82861057..2b3d27b000 100644
--- a/servers/rendering/renderer_rd/shaders/effects/copy.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/copy.glsl
@@ -57,7 +57,7 @@ layout(r32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffe
#elif defined(DST_IMAGE_8BIT)
layout(rgba8, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
#else
-layout(rgba32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
+layout(rgba16f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
#endif
#ifdef MODE_GAUSSIAN_BLUR
diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
index 0a1ae9e7b6..33ec991107 100644
--- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
@@ -57,14 +57,21 @@ layout(set = 3, binding = 0) uniform sampler2D source_color_correction;
layout(set = 3, binding = 0) uniform sampler3D source_color_correction;
#endif
+#define FLAG_USE_BCS (1 << 0)
+#define FLAG_USE_GLOW (1 << 1)
+#define FLAG_USE_AUTO_EXPOSURE (1 << 2)
+#define FLAG_USE_COLOR_CORRECTION (1 << 3)
+#define FLAG_USE_FXAA (1 << 4)
+#define FLAG_USE_DEBANDING (1 << 5)
+#define FLAG_CONVERT_TO_SRGB (1 << 6)
+
layout(push_constant, std430) uniform Params {
vec3 bcs;
- bool use_bcs;
+ uint flags;
- bool use_glow;
- bool use_auto_exposure;
- bool use_color_correction;
+ vec2 pixel_size;
uint tonemapper;
+ uint pad;
uvec2 glow_texture_size;
float glow_intensity;
@@ -77,10 +84,6 @@ layout(push_constant, std430) uniform Params {
float white;
float auto_exposure_scale;
float luminance_multiplier;
-
- vec2 pixel_size;
- bool use_fxaa;
- bool use_debanding;
}
params;
@@ -318,10 +321,12 @@ vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blendi
if (params.glow_mode == GLOW_MODE_ADD) {
return color + glow;
} else if (params.glow_mode == GLOW_MODE_SCREEN) {
- //need color clamping
+ // Needs color clamping.
+ glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
return max((color + glow) - (color * glow), vec3(0.0));
} else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) {
- //need color clamping
+ // Needs color clamping.
+ glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
glow = glow * vec3(0.5f) + vec3(0.5f);
color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r)));
@@ -439,7 +444,7 @@ void main() {
float exposure = params.exposure;
#ifndef SUBPASS
- if (params.use_auto_exposure) {
+ if (bool(params.flags & FLAG_USE_AUTO_EXPOSURE)) {
exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_scale);
}
#endif
@@ -448,12 +453,12 @@ void main() {
// Early Tonemap & SRGB Conversion
#ifndef SUBPASS
- if (params.use_fxaa) {
+ if (bool(params.flags & FLAG_USE_FXAA)) {
// FXAA must be performed before glow to preserve the "bleed" effect of glow.
color.rgb = do_fxaa(color.rgb, exposure, uv_interp);
}
- if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) {
+ if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode == GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier;
if (params.glow_map_strength > 0.001) {
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
@@ -464,11 +469,12 @@ void main() {
color.rgb = apply_tonemapping(color.rgb, params.white);
- color.rgb = linear_to_srgb(color.rgb); // regular linear -> SRGB conversion
-
+ if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
+ color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
+ }
#ifndef SUBPASS
// Glow
- if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) {
+ if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode != GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity * params.luminance_multiplier;
if (params.glow_map_strength > 0.001) {
glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
@@ -476,7 +482,9 @@ void main() {
// high dynamic range -> SRGB
glow = apply_tonemapping(glow, params.white);
- glow = linear_to_srgb(glow);
+ if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
+ glow = linear_to_srgb(glow);
+ }
color.rgb = apply_glow(color.rgb, glow);
}
@@ -484,15 +492,15 @@ void main() {
// Additional effects
- if (params.use_bcs) {
+ if (bool(params.flags & FLAG_USE_BCS)) {
color.rgb = apply_bcs(color.rgb, params.bcs);
}
- if (params.use_color_correction) {
+ if (bool(params.flags & FLAG_USE_COLOR_CORRECTION)) {
color.rgb = apply_color_correction(color.rgb);
}
- if (params.use_debanding) {
+ if (bool(params.flags & FLAG_USE_DEBANDING)) {
// Debanding should be done at the end of tonemapping, but before writing to the LDR buffer.
// Otherwise, we're adding noise to an already-quantized image.
color.rgb += screen_space_dither(gl_FragCoord.xy);
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index d055f01009..a96893570e 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -751,7 +751,7 @@ MaterialStorage::MaterialData::~MaterialData() {
}
}
-void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color) {
+void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color, bool p_3d_material) {
TextureStorage *texture_storage = TextureStorage::get_singleton();
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -917,7 +917,7 @@ void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Va
if (tex) {
rd_texture = (srgb && tex->rd_texture_srgb.is_valid()) ? tex->rd_texture_srgb : tex->rd_texture;
#ifdef TOOLS_ENABLED
- if (tex->detect_3d_callback && p_use_linear_color) {
+ if (tex->detect_3d_callback && p_3d_material) {
tex->detect_3d_callback(tex->detect_3d_callback_ud);
}
if (tex->detect_normal_callback && (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL || p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL)) {
@@ -986,7 +986,7 @@ void MaterialStorage::MaterialData::free_parameters_uniform_set(RID p_uniform_se
}
}
-bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, uint32_t p_barrier) {
+bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material, uint32_t p_barrier) {
if ((uint32_t)ubo_data.size() != p_ubo_size) {
p_uniform_dirty = true;
if (uniform_buffer.is_valid()) {
@@ -1033,7 +1033,7 @@ bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<
}
if (p_textures_dirty && tex_uniform_count) {
- update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), p_use_linear_color);
+ update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), p_use_linear_color, p_3d_material);
}
if (p_ubo_size == 0 && (p_texture_uniforms.size() == 0)) {
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h
index b6da3df783..ae97f43a3c 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h
@@ -78,7 +78,7 @@ public:
struct MaterialData {
Vector<RendererRD::TextureStorage::RenderTarget *> render_target_cache;
void update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color);
- void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color);
+ void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color, bool p_3d_material);
void set_as_used();
virtual void set_render_priority(int p_priority) = 0;
@@ -87,7 +87,7 @@ public:
virtual ~MaterialData();
//to be used internally by update_parameters, in the most common configuration of material parameters
- bool update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS);
+ bool update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &r_uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS);
void free_parameters_uniform_set(RID p_uniform_set);
private:
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index 8f647be5c9..0a91672544 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -1629,7 +1629,7 @@ MaterialStorage::ShaderData *ParticlesStorage::_create_particles_shader_func() {
}
bool ParticlesStorage::ParticleProcessMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, ParticlesStorage::get_singleton()->particles_shader.shader.version_get_shader(shader_data->version, 0), 3, true);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, ParticlesStorage::get_singleton()->particles_shader.shader.version_get_shader(shader_data->version, 0), 3, true, false);
}
ParticlesStorage::ParticleProcessMaterialData::~ParticleProcessMaterialData() {
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index d84f6e6850..f009318d24 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -46,9 +46,11 @@ void TextureStorage::CanvasTexture::clear_sets() {
}
for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) {
for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) {
- if (RD::get_singleton()->uniform_set_is_valid(uniform_sets[i][j])) {
- RD::get_singleton()->free(uniform_sets[i][j]);
- uniform_sets[i][j] = RID();
+ for (int k = 0; k < 2; k++) {
+ if (RD::get_singleton()->uniform_set_is_valid(uniform_sets[i][j][k])) {
+ RD::get_singleton()->free(uniform_sets[i][j][k]);
+ uniform_sets[i][j][k] = RID();
+ }
}
}
}
@@ -641,7 +643,7 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS:
ct->clear_sets();
}
-bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular) {
+bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular) {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
CanvasTexture *ct = nullptr;
@@ -674,7 +676,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte
RS::CanvasItemTextureRepeat repeat = ct->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? ct->texture_repeat : p_base_repeat;
ERR_FAIL_COND_V(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, false);
- RID uniform_set = ct->uniform_sets[filter][repeat];
+ RID uniform_set = ct->uniform_sets[filter][repeat][int(p_use_srgb)];
if (!RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
//create and update
Vector<RD::Uniform> uniforms;
@@ -688,7 +690,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte
u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE));
ct->size_cache = Size2i(1, 1);
} else {
- u.append_id(t->rd_texture);
+ u.append_id(t->rd_texture_srgb.is_valid() && (p_use_srgb) ? t->rd_texture_srgb : t->rd_texture);
ct->size_cache = Size2i(t->width_2d, t->height_2d);
if (t->render_target) {
t->render_target->was_used = true;
@@ -741,7 +743,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte
}
uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_base_shader, p_base_set);
- ct->uniform_sets[filter][repeat] = uniform_set;
+ ct->uniform_sets[filter][repeat][int(p_use_srgb)] = uniform_set;
ct->cleared_cache = false;
}
@@ -1268,7 +1270,35 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
#endif
Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0);
ERR_FAIL_COND_V(data.size() == 0, Ref<Image>());
- Ref<Image> image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data);
+ Ref<Image> image;
+
+ // Expand RGB10_A2 into RGBAH. This is needed for capturing viewport data
+ // when using the mobile renderer with HDR mode on.
+ if (tex->rd_format == RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32) {
+ Vector<uint8_t> new_data;
+ new_data.resize(data.size() * 2);
+ uint16_t *ndp = (uint16_t *)new_data.ptr();
+
+ uint32_t *ptr = (uint32_t *)data.ptr();
+ uint32_t num_pixels = data.size() / 4;
+
+ for (uint32_t ofs = 0; ofs < num_pixels; ofs++) {
+ uint32_t px = ptr[ofs];
+ uint32_t r = (px & 0x3FF);
+ uint32_t g = ((px >> 10) & 0x3FF);
+ uint32_t b = ((px >> 20) & 0x3FF);
+ uint32_t a = ((px >> 30) & 0x3);
+
+ ndp[ofs * 4 + 0] = Math::make_half_float(float(r) / 1023.0);
+ ndp[ofs * 4 + 1] = Math::make_half_float(float(g) / 1023.0);
+ ndp[ofs * 4 + 2] = Math::make_half_float(float(b) / 1023.0);
+ ndp[ofs * 4 + 3] = Math::make_half_float(float(a) / 3.0);
+ }
+ image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, new_data);
+ } else {
+ image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data);
+ }
+
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
if (tex->format != tex->validated_format) {
image->convert(tex->format);
@@ -3020,10 +3050,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
if (rt->size.width == 0 || rt->size.height == 0) {
return;
}
- //until we implement support for HDR monitors (and render target is attached to screen), this is enough.
- rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
- rt->image_format = rt->is_transparent ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8;
+ if (rt->use_hdr) {
+ rt->color_format = RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format();
+ rt->color_format_srgb = rt->color_format;
+ rt->image_format = rt->is_transparent ? Image::FORMAT_RGBAH : Image::FORMAT_RGBH;
+ } else {
+ rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+ rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
+ rt->image_format = rt->is_transparent ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8;
+ }
RD::TextureFormat rd_color_attachment_format;
RD::TextureView rd_view;
@@ -3106,6 +3141,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
tex->rd_format = rt->color_format;
tex->rd_format_srgb = rt->color_format_srgb;
tex->format = rt->image_format;
+ tex->validated_format = rt->use_hdr ? Image::FORMAT_RGBAH : Image::FORMAT_RGBA8;
Vector<RID> proxies = tex->proxies; //make a copy, since update may change it
for (int i = 0; i < proxies.size(); i++) {
@@ -3328,6 +3364,25 @@ RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) con
return rt->msaa;
}
+void TextureStorage::render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND(!rt);
+
+ if (p_use_hdr == rt->use_hdr) {
+ return;
+ }
+
+ rt->use_hdr = p_use_hdr;
+ _update_render_target(rt);
+}
+
+bool TextureStorage::render_target_is_using_hdr(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, false);
+
+ return rt->use_hdr;
+}
+
RID TextureStorage::render_target_get_rd_framebuffer(RID p_render_target) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND_V(!rt, RID());
@@ -3404,7 +3459,7 @@ bool TextureStorage::render_target_is_clear_requested(RID p_render_target) {
Color TextureStorage::render_target_get_clear_request_color(RID p_render_target) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND_V(!rt, Color());
- return rt->clear_color;
+ return rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color;
}
void TextureStorage::render_target_disable_clear_request(RID p_render_target) {
@@ -3420,7 +3475,7 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) {
return;
}
Vector<Color> clear_colors;
- clear_colors.push_back(rt->clear_color);
+ clear_colors.push_back(rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color);
RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors);
RD::get_singleton()->draw_list_end();
rt->clear_requested = false;
@@ -3735,7 +3790,7 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
// TODO figure out stereo support here
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
- copy_effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true);
+ copy_effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, !rt->use_hdr, true);
} else {
copy_effects->copy_to_fb_rect(rt->color, rt->backbuffer_fb, region, false, false, false, false, RID(), false, true);
}
@@ -3759,7 +3814,7 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
RID mipmap = rt->backbuffer_mipmaps[i];
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
- copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true);
+ copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, !rt->use_hdr);
} else {
copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size);
}
@@ -3789,9 +3844,9 @@ void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const
}
}
- //single texture copy for backbuffer
+ // Single texture copy for backbuffer.
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
- copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, true);
+ copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, !rt->use_hdr);
} else {
copy_effects->set_color_raster(rt->backbuffer_mipmap0, p_color, region);
}
@@ -3833,7 +3888,7 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
RID mipmap = rt->backbuffer_mipmaps[i];
if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
- copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true);
+ copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, !rt->use_hdr);
} else {
copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size);
}
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 6df6faa40a..2137c5f383 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -96,7 +96,7 @@ private:
RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
- RID uniform_sets[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX];
+ RID uniform_sets[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX][2];
Size2i size_cache = Size2i(1, 1);
bool use_normal_cache = false;
@@ -341,6 +341,7 @@ private:
Image::Format image_format = Image::FORMAT_L8;
bool is_transparent = false;
+ bool use_hdr = false;
bool sdf_enabled = false;
@@ -474,7 +475,7 @@ public:
virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override;
virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override;
- bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular);
+ bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular);
/* Texture API */
@@ -717,6 +718,8 @@ public:
virtual void render_target_set_as_unused(RID p_render_target) override;
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override;
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override;
+ virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) override;
+ virtual bool render_target_is_using_hdr(RID p_render_target) const override;
void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color);
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 7a829826d2..75371de814 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -1163,6 +1163,17 @@ void RendererViewport::viewport_set_msaa_3d(RID p_viewport, RS::ViewportMSAA p_m
_configure_3d_render_buffers(viewport);
}
+void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d) {
+ Viewport *viewport = viewport_owner.get_or_null(p_viewport);
+ ERR_FAIL_COND(!viewport);
+
+ if (viewport->use_hdr_2d == p_use_hdr_2d) {
+ return;
+ }
+ viewport->use_hdr_2d = p_use_hdr_2d;
+ RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d);
+}
+
void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_COND(!viewport);
diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h
index c004d05b23..92c5929796 100644
--- a/servers/rendering/renderer_viewport.h
+++ b/servers/rendering/renderer_viewport.h
@@ -116,6 +116,7 @@ public:
RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
bool transparent_bg = false;
+ bool use_hdr_2d = false;
uint32_t canvas_cull_mask = 0xffffffff;
@@ -157,6 +158,7 @@ public:
update_mode = RS::VIEWPORT_UPDATE_WHEN_VISIBLE;
clear_mode = RS::VIEWPORT_CLEAR_ALWAYS;
transparent_bg = false;
+ use_hdr_2d = false;
viewport_to_screen = DisplayServer::INVALID_WINDOW_ID;
shadow_atlas_size = 0;
@@ -250,6 +252,7 @@ public:
void viewport_remove_canvas(RID p_viewport, RID p_canvas);
void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset);
void viewport_set_transparent_background(RID p_viewport, bool p_enabled);
+ void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d);
void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform);
void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer);
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index e9b40eb082..9ad2175332 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -629,6 +629,7 @@ public:
FUNC2(viewport_remove_canvas, RID, RID)
FUNC3(viewport_set_canvas_transform, RID, RID, const Transform2D &)
FUNC2(viewport_set_transparent_background, RID, bool)
+ FUNC2(viewport_set_use_hdr_2d, RID, bool)
FUNC2(viewport_set_snap_2d_transforms_to_pixel, RID, bool)
FUNC2(viewport_set_snap_2d_vertices_to_pixel, RID, bool)
diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp
index 7af326f779..8d6ddf9121 100644
--- a/servers/rendering/shader_preprocessor.cpp
+++ b/servers/rendering/shader_preprocessor.cpp
@@ -420,11 +420,11 @@ void ShaderPreprocessor::process_define(Tokenizer *p_tokenizer) {
return;
}
+ Vector<String> args;
if (p_tokenizer->peek() == '(') {
// Macro has arguments.
p_tokenizer->get_token();
- Vector<String> args;
while (true) {
String name = p_tokenizer->get_identifier();
if (name.is_empty()) {
@@ -442,17 +442,24 @@ void ShaderPreprocessor::process_define(Tokenizer *p_tokenizer) {
return;
}
}
+ }
- Define *define = memnew(Define);
+ String body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
+ if (body.begins_with("##")) {
+ set_error(RTR("'##' must not appear at beginning of macro expansion."), line);
+ return;
+ }
+ if (body.ends_with("##")) {
+ set_error(RTR("'##' must not appear at end of macro expansion."), line);
+ return;
+ }
+
+ Define *define = memnew(Define);
+ if (!args.is_empty()) {
define->arguments = args;
- define->body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
- state->defines[label] = define;
- } else {
- // Simple substitution macro.
- Define *define = memnew(Define);
- define->body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
- state->defines[label] = define;
}
+ define->body = body;
+ state->defines[label] = define;
}
void ShaderPreprocessor::process_elif(Tokenizer *p_tokenizer) {
@@ -1074,9 +1081,13 @@ bool ShaderPreprocessor::expand_macros_once(const String &p_line, int p_line_num
}
}
+ concatenate_macro_body(body);
+
result = result.substr(0, index) + " " + body + " " + result.substr(args_end + 1, result.length());
} else {
- result = result.substr(0, index) + body + result.substr(index + key.length(), result.length() - (index + key.length()));
+ concatenate_macro_body(body);
+
+ result = result.substr(0, index) + " " + body + " " + result.substr(index + key.length(), result.length() - (index + key.length()));
}
r_expanded = result;
@@ -1115,6 +1126,40 @@ bool ShaderPreprocessor::find_match(const String &p_string, const String &p_valu
return false;
}
+void ShaderPreprocessor::concatenate_macro_body(String &r_body) {
+ int index_start = r_body.find("##");
+ while (index_start > -1) {
+ int index_end = index_start + 2; // First character after ##.
+ // The macro was checked during creation so this should never happen.
+ ERR_FAIL_INDEX(index_end, r_body.size());
+
+ // If there more than two # in a row, then it's not a concatenation.
+ bool is_concat = true;
+ while (index_end <= r_body.length() && r_body[index_end] == '#') {
+ index_end++;
+ is_concat = false;
+ }
+ if (!is_concat) {
+ index_start = r_body.find("##", index_end);
+ continue;
+ }
+
+ // Skip whitespace after ##.
+ while (index_end < r_body.length() && is_char_space(r_body[index_end])) {
+ index_end++;
+ }
+
+ // Skip whitespace before ##.
+ while (index_start >= 1 && is_char_space(r_body[index_start - 1])) {
+ index_start--;
+ }
+
+ r_body = r_body.substr(0, index_start) + r_body.substr(index_end, r_body.length() - index_end);
+
+ index_start = r_body.find("##", index_start);
+ }
+}
+
String ShaderPreprocessor::next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives) {
const int line = p_tokenizer->get_line();
int nesting = 0;
diff --git a/servers/rendering/shader_preprocessor.h b/servers/rendering/shader_preprocessor.h
index 406b663228..9448a97d68 100644
--- a/servers/rendering/shader_preprocessor.h
+++ b/servers/rendering/shader_preprocessor.h
@@ -205,6 +205,7 @@ private:
Error expand_macros(const String &p_string, int p_line, String &r_result);
bool expand_macros_once(const String &p_line, int p_line_number, const RBMap<String, Define *>::Element *p_define_pair, String &r_expanded);
bool find_match(const String &p_string, const String &p_value, int &r_index, int &r_index_start);
+ void concatenate_macro_body(String &r_body);
String next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives);
void add_to_output(const String &p_str);
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index c3a257595c..6332dd578e 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -151,6 +151,8 @@ public:
virtual void render_target_set_as_unused(RID p_render_target) = 0;
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) = 0;
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const = 0;
+ virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) = 0;
+ virtual bool render_target_is_using_hdr(RID p_render_target) const = 0;
virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) = 0;
virtual bool render_target_is_clear_requested(RID p_render_target) = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 48b38cf2b7..45ba0b3c08 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2231,6 +2231,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_set_positional_shadow_atlas_quadrant_subdivision", "viewport", "quadrant", "subdivision"), &RenderingServer::viewport_set_positional_shadow_atlas_quadrant_subdivision);
ClassDB::bind_method(D_METHOD("viewport_set_msaa_3d", "viewport", "msaa"), &RenderingServer::viewport_set_msaa_3d);
ClassDB::bind_method(D_METHOD("viewport_set_msaa_2d", "viewport", "msaa"), &RenderingServer::viewport_set_msaa_2d);
+ ClassDB::bind_method(D_METHOD("viewport_set_use_hdr_2d", "viewport", "enabled"), &RenderingServer::viewport_set_use_hdr_2d);
ClassDB::bind_method(D_METHOD("viewport_set_screen_space_aa", "viewport", "mode"), &RenderingServer::viewport_set_screen_space_aa);
ClassDB::bind_method(D_METHOD("viewport_set_use_taa", "viewport", "enable"), &RenderingServer::viewport_set_use_taa);
ClassDB::bind_method(D_METHOD("viewport_set_use_debanding", "viewport", "enable"), &RenderingServer::viewport_set_use_debanding);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 618ceb3091..1528a957ce 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -863,6 +863,7 @@ public:
virtual void viewport_remove_canvas(RID p_viewport, RID p_canvas) = 0;
virtual void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) = 0;
virtual void viewport_set_transparent_background(RID p_viewport, bool p_enabled) = 0;
+ virtual void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr) = 0;
virtual void viewport_set_snap_2d_transforms_to_pixel(RID p_viewport, bool p_enabled) = 0;
virtual void viewport_set_snap_2d_vertices_to_pixel(RID p_viewport, bool p_enabled) = 0;
diff --git a/tests/servers/rendering/test_shader_preprocessor.h b/tests/servers/rendering/test_shader_preprocessor.h
new file mode 100644
index 0000000000..41a009ccf5
--- /dev/null
+++ b/tests/servers/rendering/test_shader_preprocessor.h
@@ -0,0 +1,334 @@
+/**************************************************************************/
+/* test_shader_preprocessor.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_SHADER_PREPROCESSOR_H
+#define TEST_SHADER_PREPROCESSOR_H
+
+#include "servers/rendering/shader_preprocessor.h"
+
+#include "tests/test_macros.h"
+
+#include <cctype>
+
+namespace TestShaderPreprocessor {
+
+void erase_all_empty(Vector<String> &p_vec) {
+ int idx = p_vec.find(" ");
+ while (idx >= 0) {
+ p_vec.remove_at(idx);
+ idx = p_vec.find(" ");
+ }
+}
+
+bool is_variable_char(unsigned char c) {
+ return std::isalnum(c) || c == '_';
+}
+
+bool is_operator_char(unsigned char c) {
+ static const std::string operators = "<>=+-*/";
+ return operators.find(c) != std::string::npos;
+}
+
+// Remove unnecessary spaces from a line.
+String remove_spaces(String &p_str) {
+ String res;
+ // Result is guaranteed to not be longer than the input.
+ res.resize(p_str.size());
+ int wp = 0;
+ char32_t last = 0;
+ bool has_removed = false;
+
+ for (int n = 0; n < p_str.size(); n++) {
+ // These test cases only use ASCII.
+ auto c = static_cast<unsigned char>(p_str[n]);
+ if (std::isblank(c)) {
+ has_removed = true;
+ } else {
+ if (has_removed) {
+ // Insert a space to avoid joining things that could potentially form a new token.
+ // E.g. "float x" or "- -".
+ if ((is_variable_char(c) && is_variable_char(last)) ||
+ (is_operator_char(c) && is_operator_char(last))) {
+ res[wp++] = ' ';
+ }
+ has_removed = false;
+ }
+ res[wp++] = c;
+ last = c;
+ }
+ }
+ res.resize(wp);
+ return res;
+}
+
+// The pre-processor changes indentation and inserts spaces when inserting macros.
+// Re-format the code, without changing its meaning, to make it easier to compare.
+String compact_spaces(String &p_str) {
+ Vector<String> lines = p_str.split("\n", false);
+ erase_all_empty(lines);
+ for (auto &line : lines) {
+ line = remove_spaces(line);
+ }
+ return String("\n").join(lines);
+}
+
+#define CHECK_SHADER_EQ(a, b) CHECK_EQ(compact_spaces(a), compact_spaces(b))
+#define CHECK_SHADER_NE(a, b) CHECK_NE(compact_spaces(a), compact_spaces(b))
+
+TEST_CASE("[ShaderPreprocessor] Simple defines") {
+ String code(
+ "#define X 1.0 // comment\n"
+ "#define Y mix\n"
+ "#define Z X\n"
+ "\n"
+ "#define func0 \\\n"
+ " vec3 my_fun(vec3 arg) {\\\n"
+ " return pow(arg, 2.2);\\\n"
+ " }\n"
+ "\n"
+ "func0\n"
+ "\n"
+ "fragment() {\n"
+ " ALBEDO = vec3(X);\n"
+ " float x = Y(0., Z, X);\n"
+ " #undef X\n"
+ " float X = x;\n"
+ " x = -Z;\n"
+ "}\n");
+ String expected(
+ "vec3 my_fun(vec3 arg) { return pow(arg, 2.2); }\n"
+ "\n"
+ "fragment() {\n"
+ " ALBEDO = vec3( 1.0 );\n"
+ " float x = mix(0., 1.0 , 1.0 );\n"
+ " float X = x;\n"
+ " x = -X;\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Avoid merging adjacent tokens") {
+ String code(
+ "#define X -10\n"
+ "#define Y(s) s\n"
+ "\n"
+ "fragment() {\n"
+ " float v = 1.0-X-Y(-2);\n"
+ "}\n");
+ String expected(
+ "fragment() {\n"
+ " float v = 1.0 - -10 - -2;\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Complex defines") {
+ String code(
+ "const float X = 2.0;\n"
+ "#define A(X) X*2.\n"
+ "#define X 1.0\n"
+ "#define Y Z(X, W)\n"
+ "#define Z max\n"
+ "#define C(X, Y) Z(A(Y), B(X))\n"
+ "#define W -X\n"
+ "#define B(X) X*3.\n"
+ "\n"
+ "fragment() {\n"
+ " float x = Y;\n"
+ " float y = C(5., 7.0);\n"
+ "}\n");
+ String expected(
+ "const float X = 2.0;\n"
+ "fragment() {\n"
+ " float x = max(1.0, - 1.0);\n"
+ " float y = max(7.0*2. , 5.*3.);\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Concatenation") {
+ String code(
+ "fragment() {\n"
+ " #define X 1 // this is fine ##\n"
+ " #define y 2\n"
+ " #define z 3##.## 1## 4 ## 59\n"
+ " #define Z(y) X ## y\n"
+ " #define Z2(y) y##X\n"
+ " #define W(y) X, y\n"
+ " #define A(x) fl## oat a = 1##x ##.3 ## x\n"
+ " #define C(x, y) x##.##y\n"
+ " #define J(x) x##=\n"
+ " float Z(y) = 1.2;\n"
+ " float Z(z) = 2.3;\n"
+ " float Z2(y) = z;\n"
+ " float Z2(z) = 2.3;\n"
+ " int b = max(W(3));\n"
+ " Xy J(+) b J(=) 3 ? 0.1 : 0.2;\n"
+ " A(9);\n"
+ " Xy = C(X, y);\n"
+ "}\n");
+ String expected(
+ "fragment() {\n"
+ " float Xy = 1.2;\n"
+ " float Xz = 2.3;\n"
+ " float yX = 3.1459;\n"
+ " float zX = 2.3;\n"
+ " int b = max(1, 3);\n"
+ " Xy += b == 3 ? 0.1 : 0.2;\n"
+ " float a = 19.39;\n"
+ " Xy = 1.2;\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Nested concatenation") {
+ // Concatenation ## should not expand adjacent tokens if they are macros,
+ // but this is currently not implemented in Godot's shader preprocessor.
+ // To force expanding, an extra macro should be required (B in this case).
+
+ String code(
+ "fragment() {\n"
+ " vec2 X = vec2(0);\n"
+ " #define X 1\n"
+ " #define y 2\n"
+ " #define B(x, y) C(x, y)\n"
+ " #define C(x, y) x##.##y\n"
+ " C(X, y) = B(X, y);\n"
+ "}\n");
+ String expected(
+ "fragment() {\n"
+ " vec2 X = vec2(0);\n"
+ " X.y = 1.2;\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ // TODO: Reverse the check when/if this is changed.
+ CHECK_SHADER_NE(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Concatenation sorting network") {
+ String code(
+ "fragment() {\n"
+ " #define ARR(X) test##X\n"
+ " #define ACMP(a, b) ARR(a) > ARR(b)\n"
+ " #define ASWAP(a, b) tmp = ARR(b); ARR(b) = ARR(a); ARR(a) = tmp;\n"
+ " #define ACSWAP(a, b) if(ACMP(a, b)) { ASWAP(a, b) }\n"
+ " float test0 = 1.2;\n"
+ " float test1 = 0.34;\n"
+ " float test3 = 0.8;\n"
+ " float test4 = 2.9;\n"
+ " float tmp;\n"
+ " ACSWAP(0,2)\n"
+ " ACSWAP(1,3)\n"
+ " ACSWAP(0,1)\n"
+ " ACSWAP(2,3)\n"
+ " ACSWAP(1,2)\n"
+ "}\n");
+ String expected(
+ "fragment() {\n"
+ " float test0 = 1.2;\n"
+ " float test1 = 0.34;\n"
+ " float test3 = 0.8;\n"
+ " float test4 = 2.9;\n"
+ " float tmp;\n"
+ " if(test0 > test2) { tmp = test2; test2 = test0; test0 = tmp; }\n"
+ " if(test1 > test3) { tmp = test3; test3 = test1; test1 = tmp; }\n"
+ " if(test0 > test1) { tmp = test1; test1 = test0; test0 = tmp; }\n"
+ " if(test2 > test3) { tmp = test3; test3 = test2; test2 = tmp; }\n"
+ " if(test1 > test2) { tmp = test2; test2 = test1; test1 = tmp; }\n"
+ "}\n");
+ String result;
+
+ ShaderPreprocessor preprocessor;
+ CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK);
+
+ CHECK_SHADER_EQ(result, expected);
+}
+
+TEST_CASE("[ShaderPreprocessor] Undefined behaviour") {
+ // None of these are valid concatenation, nor valid shader code.
+ // Don't care about results, just make sure there's no crash.
+ const String filename("somefile.gdshader");
+ String result;
+ ShaderPreprocessor preprocessor;
+
+ preprocessor.preprocess("#define X ###\nX\n", filename, result);
+ preprocessor.preprocess("#define X ####\nX\n", filename, result);
+ preprocessor.preprocess("#define X #####\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 ### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 #### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 ##### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X ### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X #### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X ##### 2\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 ###\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 ####\nX\n", filename, result);
+ preprocessor.preprocess("#define X 1 #####\nX\n", filename, result);
+}
+
+TEST_CASE("[ShaderPreprocessor] Invalid concatenations") {
+ const String filename("somefile.gdshader");
+ String result;
+ ShaderPreprocessor preprocessor;
+
+ CHECK_NE(preprocessor.preprocess("#define X ##", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X 1 ##", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X ## 1", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X(y) ## ", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X(y) y ## ", filename, result), Error::OK);
+ CHECK_NE(preprocessor.preprocess("#define X(y) ## y", filename, result), Error::OK);
+}
+
+} // namespace TestShaderPreprocessor
+
+#endif // TEST_SHADER_PREPROCESSOR_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index d775136e90..5ca03a20af 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -115,6 +115,7 @@
#include "tests/scene/test_theme.h"
#include "tests/scene/test_viewport.h"
#include "tests/scene/test_visual_shader.h"
+#include "tests/servers/rendering/test_shader_preprocessor.h"
#include "tests/servers/test_navigation_server_2d.h"
#include "tests/servers/test_navigation_server_3d.h"
#include "tests/servers/test_text_server.h"
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 4bb1e8e1d1..27705de997 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -356,7 +356,7 @@ Files extracted from upstream source:
## libwebp
- Upstream: https://chromium.googlesource.com/webm/libwebp/
-- Version: 1.3.0 (b557776962a3dcc985d83bd4ed94e1e2e50d0fa2, 2022)
+- Version: 1.3.1 (fd7bb21c0cb56e8a82e9bfa376164b842f433f3b, 2023)
- License: BSD-3-Clause
Files extracted from upstream source:
@@ -422,7 +422,7 @@ to solve some MSVC warnings. See the patches in the `patches` directory.
## miniupnpc
- Upstream: https://github.com/miniupnp/miniupnp
-- Version: 2.2.4 (7d1d8bc3868b08ad003bad235eee57562b95b76d, 2022)
+- Version: 2.2.5 (58837ef586278d18cbebee50be758835ed4be79a, 2023)
- License: BSD-3-Clause
Files extracted from upstream source:
diff --git a/thirdparty/libwebp/AUTHORS b/thirdparty/libwebp/AUTHORS
index 2f0c537d1c..8359b20da9 100644
--- a/thirdparty/libwebp/AUTHORS
+++ b/thirdparty/libwebp/AUTHORS
@@ -32,6 +32,7 @@ Contributors:
- Mislav Bradac (mislavm at google dot com)
- Nico Weber (thakis at chromium dot org)
- Noel Chromium (noel at chromium dot org)
+- Nozomi Isozaki (nontan at pixiv dot co dot jp)
- Oliver Wolff (oliver dot wolff at qt dot io)
- Owen Rodley (orodley at google dot com)
- Parag Salasakar (img dot mips1 at gmail dot com)
@@ -47,6 +48,7 @@ Contributors:
- Somnath Banerjee (somnath dot banerjee at gmail dot com)
- Sriraman Tallam (tmsriram at google dot com)
- Tamar Levy (tamar dot levy at intel dot com)
+- Thiago Perrotta (tperrotta at google dot com)
- Timothy Gu (timothygu99 at gmail dot com)
- Urvang Joshi (urvang at google dot com)
- Vikas Arora (vikasa at google dot com)
diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv.c b/thirdparty/libwebp/sharpyuv/sharpyuv.c
index 7de34fb0b2..a074564888 100644
--- a/thirdparty/libwebp/sharpyuv/sharpyuv.c
+++ b/thirdparty/libwebp/sharpyuv/sharpyuv.c
@@ -440,6 +440,7 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
// By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed,
// users can declare it as extern and call it with an alternate VP8CPUInfo
// function.
+extern VP8CPUInfo SharpYuvGetCPUInfo;
SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func);
void SharpYuvInit(VP8CPUInfo cpu_info_func) {
static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used =
diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv.h b/thirdparty/libwebp/sharpyuv/sharpyuv.h
index 181b20a0bc..7b9904d6f9 100644
--- a/thirdparty/libwebp/sharpyuv/sharpyuv.h
+++ b/thirdparty/libwebp/sharpyuv/sharpyuv.h
@@ -37,7 +37,7 @@ extern "C" {
// SharpYUV API version following the convention from semver.org
#define SHARPYUV_VERSION_MAJOR 0
#define SHARPYUV_VERSION_MINOR 2
-#define SHARPYUV_VERSION_PATCH 0
+#define SHARPYUV_VERSION_PATCH 1
// Version as a uint32_t. The major number is the high 8 bits.
// The minor number is the middle 8 bits. The patch number is the low 16 bits.
#define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \
diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c b/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c
index 31c272c408..0da3efc0b8 100644
--- a/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c
+++ b/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c
@@ -72,6 +72,7 @@ void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
const uint16_t* best_y, uint16_t* out,
int bit_depth);
+extern VP8CPUInfo SharpYuvGetCPUInfo;
extern void InitSharpYuvSSE2(void);
extern void InitSharpYuvNEON(void);
diff --git a/thirdparty/libwebp/src/dec/tree_dec.c b/thirdparty/libwebp/src/dec/tree_dec.c
index 1c6fdea27c..2434605953 100644
--- a/thirdparty/libwebp/src/dec/tree_dec.c
+++ b/thirdparty/libwebp/src/dec/tree_dec.c
@@ -12,10 +12,11 @@
// Author: Skal (pascal.massimino@gmail.com)
#include "src/dec/vp8i_dec.h"
+#include "src/dsp/cpu.h"
#include "src/utils/bit_reader_inl_utils.h"
#if !defined(USE_GENERIC_TREE)
-#if !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__)
+#if !defined(__arm__) && !defined(_M_ARM) && !WEBP_AARCH64
// using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then.
#define USE_GENERIC_TREE 1 // ALTERNATE_CODE
#else
diff --git a/thirdparty/libwebp/src/dec/vp8_dec.c b/thirdparty/libwebp/src/dec/vp8_dec.c
index 2003935ec4..20b92e84c4 100644
--- a/thirdparty/libwebp/src/dec/vp8_dec.c
+++ b/thirdparty/libwebp/src/dec/vp8_dec.c
@@ -494,6 +494,8 @@ static int GetCoeffsAlt(VP8BitReader* const br,
return 16;
}
+extern VP8CPUInfo VP8GetCPUInfo;
+
WEBP_DSP_INIT_FUNC(InitGetCoeffs) {
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
GetCoeffs = GetCoeffsAlt;
diff --git a/thirdparty/libwebp/src/dec/vp8i_dec.h b/thirdparty/libwebp/src/dec/vp8i_dec.h
index 83791ecd25..1ae4ff62f2 100644
--- a/thirdparty/libwebp/src/dec/vp8i_dec.h
+++ b/thirdparty/libwebp/src/dec/vp8i_dec.h
@@ -32,7 +32,7 @@ extern "C" {
// version numbers
#define DEC_MAJ_VERSION 1
#define DEC_MIN_VERSION 3
-#define DEC_REV_VERSION 0
+#define DEC_REV_VERSION 1
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
// Constraints are: We need to store one 16x16 block of luma samples (y),
diff --git a/thirdparty/libwebp/src/dec/webp_dec.c b/thirdparty/libwebp/src/dec/webp_dec.c
index 3f4f7bb659..f557868b99 100644
--- a/thirdparty/libwebp/src/dec/webp_dec.c
+++ b/thirdparty/libwebp/src/dec/webp_dec.c
@@ -658,19 +658,26 @@ uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
int* width, int* height, uint8_t** u, uint8_t** v,
int* stride, int* uv_stride) {
- WebPDecBuffer output; // only to preserve the side-infos
- uint8_t* const out = Decode(MODE_YUV, data, data_size,
- width, height, &output);
-
- if (out != NULL) {
- const WebPYUVABuffer* const buf = &output.u.YUVA;
- *u = buf->u;
- *v = buf->v;
- *stride = buf->y_stride;
- *uv_stride = buf->u_stride;
- assert(buf->u_stride == buf->v_stride);
- }
- return out;
+ // data, width and height are checked by Decode().
+ if (u == NULL || v == NULL || stride == NULL || uv_stride == NULL) {
+ return NULL;
+ }
+
+ {
+ WebPDecBuffer output; // only to preserve the side-infos
+ uint8_t* const out = Decode(MODE_YUV, data, data_size,
+ width, height, &output);
+
+ if (out != NULL) {
+ const WebPYUVABuffer* const buf = &output.u.YUVA;
+ *u = buf->u;
+ *v = buf->v;
+ *stride = buf->y_stride;
+ *uv_stride = buf->u_stride;
+ assert(buf->u_stride == buf->v_stride);
+ }
+ return out;
+ }
}
static void DefaultFeatures(WebPBitstreamFeatures* const features) {
diff --git a/thirdparty/libwebp/src/demux/demux.c b/thirdparty/libwebp/src/demux/demux.c
index 324e5eb993..fd45a2500e 100644
--- a/thirdparty/libwebp/src/demux/demux.c
+++ b/thirdparty/libwebp/src/demux/demux.c
@@ -25,7 +25,7 @@
#define DMUX_MAJ_VERSION 1
#define DMUX_MIN_VERSION 3
-#define DMUX_REV_VERSION 0
+#define DMUX_REV_VERSION 1
typedef struct {
size_t start_; // start location of the data
diff --git a/thirdparty/libwebp/src/dsp/alpha_processing.c b/thirdparty/libwebp/src/dsp/alpha_processing.c
index 1892929a43..1d152f24da 100644
--- a/thirdparty/libwebp/src/dsp/alpha_processing.c
+++ b/thirdparty/libwebp/src/dsp/alpha_processing.c
@@ -425,6 +425,7 @@ void (*WebPAlphaReplace)(uint32_t* src, int length, uint32_t color);
//------------------------------------------------------------------------------
// Init function
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitAlphaProcessingMIPSdspR2(void);
extern void WebPInitAlphaProcessingSSE2(void);
extern void WebPInitAlphaProcessingSSE41(void);
diff --git a/thirdparty/libwebp/src/dsp/cost.c b/thirdparty/libwebp/src/dsp/cost.c
index 460ec4f2a7..73d2140177 100644
--- a/thirdparty/libwebp/src/dsp/cost.c
+++ b/thirdparty/libwebp/src/dsp/cost.c
@@ -374,6 +374,7 @@ static void SetResidualCoeffs_C(const int16_t* const coeffs,
VP8GetResidualCostFunc VP8GetResidualCost;
VP8SetResidualCoeffsFunc VP8SetResidualCoeffs;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8EncDspCostInitMIPS32(void);
extern void VP8EncDspCostInitMIPSdspR2(void);
extern void VP8EncDspCostInitSSE2(void);
diff --git a/thirdparty/libwebp/src/dsp/cost_neon.c b/thirdparty/libwebp/src/dsp/cost_neon.c
index 8cc8ce58aa..6582669cb3 100644
--- a/thirdparty/libwebp/src/dsp/cost_neon.c
+++ b/thirdparty/libwebp/src/dsp/cost_neon.c
@@ -29,7 +29,7 @@ static void SetResidualCoeffs_NEON(const int16_t* const coeffs,
const uint8x16_t eob = vcombine_u8(vqmovn_u16(eob_0), vqmovn_u16(eob_1));
const uint8x16_t masked = vandq_u8(eob, vld1q_u8(position));
-#ifdef __aarch64__
+#if WEBP_AARCH64
res->last = vmaxvq_u8(masked) - 1;
#else
const uint8x8_t eob_8x8 = vmax_u8(vget_low_u8(masked), vget_high_u8(masked));
@@ -43,7 +43,7 @@ static void SetResidualCoeffs_NEON(const int16_t* const coeffs,
vst1_lane_s32(&res->last, vreinterpret_s32_u32(eob_32x2), 0);
--res->last;
-#endif // __aarch64__
+#endif // WEBP_AARCH64
res->coeffs = coeffs;
}
diff --git a/thirdparty/libwebp/src/dsp/cpu.c b/thirdparty/libwebp/src/dsp/cpu.c
index 62de73f750..2234c77b35 100644
--- a/thirdparty/libwebp/src/dsp/cpu.c
+++ b/thirdparty/libwebp/src/dsp/cpu.c
@@ -173,6 +173,7 @@ static int x86CPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
#elif defined(WEBP_ANDROID_NEON) // NB: needs to be before generic NEON test.
static int AndroidCPUInfo(CPUFeature feature) {
@@ -184,6 +185,7 @@ static int AndroidCPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
#elif defined(EMSCRIPTEN) // also needs to be before generic NEON test
// Use compile flags as an indicator of SIMD support instead of a runtime check.
@@ -208,6 +210,7 @@ static int wasmCPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = wasmCPUInfo;
#elif defined(WEBP_HAVE_NEON)
// In most cases this function doesn't check for NEON support (it's assumed by
@@ -236,6 +239,7 @@ static int armCPUInfo(CPUFeature feature) {
return 1;
#endif
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
#elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) || \
defined(WEBP_USE_MSA)
@@ -247,7 +251,9 @@ static int mipsCPUInfo(CPUFeature feature) {
}
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo;
#else
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = NULL;
#endif
diff --git a/thirdparty/libwebp/src/dsp/cpu.h b/thirdparty/libwebp/src/dsp/cpu.h
index be80727c0d..c86540f280 100644
--- a/thirdparty/libwebp/src/dsp/cpu.h
+++ b/thirdparty/libwebp/src/dsp/cpu.h
@@ -43,6 +43,9 @@
#define __has_builtin(x) 0
#endif
+//------------------------------------------------------------------------------
+// x86 defines.
+
#if !defined(HAVE_CONFIG_H)
#if defined(_MSC_VER) && _MSC_VER > 1310 && \
(defined(_M_X64) || defined(_M_IX86))
@@ -80,6 +83,9 @@
#undef WEBP_MSC_SSE41
#undef WEBP_MSC_SSE2
+//------------------------------------------------------------------------------
+// Arm defines.
+
// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
// inline assembly would need to be modified for use with Native Client.
#if ((defined(__ARM_NEON__) || defined(__aarch64__)) && \
@@ -98,16 +104,26 @@
// inclusion of arm64_neon.h; Visual Studio 2019 includes this file in
// arm_neon.h. Compile errors were seen with Visual Studio 2019 16.4 with
// vtbl4_u8(); a fix was made in 16.6.
-#if defined(_MSC_VER) && ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
- (_MSC_VER >= 1926 && defined(_M_ARM64)))
+#if defined(_MSC_VER) && \
+ ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
+ (_MSC_VER >= 1926 && (defined(_M_ARM64) || defined(_M_ARM64EC))))
#define WEBP_USE_NEON
#define WEBP_USE_INTRINSICS
#endif
+#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
+#define WEBP_AARCH64 1
+#else
+#define WEBP_AARCH64 0
+#endif
+
#if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON)
#define WEBP_HAVE_NEON
#endif
+//------------------------------------------------------------------------------
+// MIPS defines.
+
#if defined(__mips__) && !defined(__mips64) && defined(__mips_isa_rev) && \
(__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
#define WEBP_USE_MIPS32
@@ -123,6 +139,8 @@
#define WEBP_USE_MSA
#endif
+//------------------------------------------------------------------------------
+
#ifndef WEBP_DSP_OMIT_C_CODE
#define WEBP_DSP_OMIT_C_CODE 1
#endif
@@ -133,13 +151,14 @@
#define WEBP_NEON_OMIT_C_CODE 0
#endif
-#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || \
- defined(__aarch64__))
+#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || WEBP_AARCH64)
#define WEBP_NEON_WORK_AROUND_GCC 1
#else
#define WEBP_NEON_WORK_AROUND_GCC 0
#endif
+//------------------------------------------------------------------------------
+
// This macro prevents thread_sanitizer from reporting known concurrent writes.
#define WEBP_TSAN_IGNORE_FUNCTION
#if defined(__has_feature)
@@ -241,16 +260,7 @@ typedef enum {
kMSA
} CPUFeature;
-#ifdef __cplusplus
-extern "C" {
-#endif
-
// returns true if the CPU supports the feature.
typedef int (*VP8CPUInfo)(CPUFeature feature);
-WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
#endif // WEBP_DSP_CPU_H_
diff --git a/thirdparty/libwebp/src/dsp/dec.c b/thirdparty/libwebp/src/dsp/dec.c
index 537c701282..33d8df8a62 100644
--- a/thirdparty/libwebp/src/dsp/dec.c
+++ b/thirdparty/libwebp/src/dsp/dec.c
@@ -734,6 +734,7 @@ VP8SimpleFilterFunc VP8SimpleHFilter16i;
void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst,
int dst_stride);
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8DspInitSSE2(void);
extern void VP8DspInitSSE41(void);
extern void VP8DspInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/dec_neon.c b/thirdparty/libwebp/src/dsp/dec_neon.c
index fa851707e2..22784cf15a 100644
--- a/thirdparty/libwebp/src/dsp/dec_neon.c
+++ b/thirdparty/libwebp/src/dsp/dec_neon.c
@@ -1428,7 +1428,7 @@ static WEBP_INLINE void DC8_NEON(uint8_t* dst, int do_top, int do_left) {
if (do_top) {
const uint8x8_t A = vld1_u8(dst - BPS); // top row
-#if defined(__aarch64__)
+#if WEBP_AARCH64
const uint16_t p2 = vaddlv_u8(A);
sum_top = vdupq_n_u16(p2);
#else
@@ -1511,7 +1511,7 @@ static WEBP_INLINE void DC16_NEON(uint8_t* dst, int do_top, int do_left) {
if (do_top) {
const uint8x16_t A = vld1q_u8(dst - BPS); // top row
-#if defined(__aarch64__)
+#if WEBP_AARCH64
const uint16_t p3 = vaddlvq_u8(A);
sum_top = vdupq_n_u16(p3);
#else
diff --git a/thirdparty/libwebp/src/dsp/enc.c b/thirdparty/libwebp/src/dsp/enc.c
index ea47a3fd95..2ba97ba8d6 100644
--- a/thirdparty/libwebp/src/dsp/enc.c
+++ b/thirdparty/libwebp/src/dsp/enc.c
@@ -732,6 +732,7 @@ VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT;
VP8BlockCopy VP8Copy4x4;
VP8BlockCopy VP8Copy16x8;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8EncDspInitSSE2(void);
extern void VP8EncDspInitSSE41(void);
extern void VP8EncDspInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/enc_neon.c b/thirdparty/libwebp/src/dsp/enc_neon.c
index 3a04111c55..714800367b 100644
--- a/thirdparty/libwebp/src/dsp/enc_neon.c
+++ b/thirdparty/libwebp/src/dsp/enc_neon.c
@@ -764,7 +764,7 @@ static WEBP_INLINE void AccumulateSSE16_NEON(const uint8_t* const a,
// Horizontal sum of all four uint32_t values in 'sum'.
static int SumToInt_NEON(uint32x4_t sum) {
-#if defined(__aarch64__)
+#if WEBP_AARCH64
return (int)vaddvq_u32(sum);
#else
const uint64x2_t sum2 = vpaddlq_u32(sum);
@@ -865,7 +865,7 @@ static int QuantizeBlock_NEON(int16_t in[16], int16_t out[16],
uint8x8x4_t shuffles;
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
uint8x16x2_t all_out;
INIT_VECTOR2(all_out, vreinterpretq_u8_s16(out0), vreinterpretq_u8_s16(out1));
diff --git a/thirdparty/libwebp/src/dsp/enc_sse2.c b/thirdparty/libwebp/src/dsp/enc_sse2.c
index 1d1055668f..010624a2f7 100644
--- a/thirdparty/libwebp/src/dsp/enc_sse2.c
+++ b/thirdparty/libwebp/src/dsp/enc_sse2.c
@@ -25,9 +25,160 @@
//------------------------------------------------------------------------------
// Transforms (Paragraph 14.4)
-// Does one or two inverse transforms.
-static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
- int do_two) {
+// Does one inverse transform.
+static void ITransform_One_SSE2(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst) {
+ // This implementation makes use of 16-bit fixed point versions of two
+ // multiply constants:
+ // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
+ // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
+ //
+ // To be able to use signed 16-bit integers, we use the following trick to
+ // have constants within range:
+ // - Associated constants are obtained by subtracting the 16-bit fixed point
+ // version of one:
+ // k = K - (1 << 16) => K = k + (1 << 16)
+ // K1 = 85267 => k1 = 20091
+ // K2 = 35468 => k2 = -30068
+ // - The multiplication of a variable by a constant become the sum of the
+ // variable and the multiplication of that variable by the associated
+ // constant:
+ // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
+ const __m128i k1k2 = _mm_set_epi16(-30068, -30068, -30068, -30068,
+ 20091, 20091, 20091, 20091);
+ const __m128i k2k1 = _mm_set_epi16(20091, 20091, 20091, 20091,
+ -30068, -30068, -30068, -30068);
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i zero_four = _mm_set_epi16(0, 0, 0, 0, 4, 4, 4, 4);
+ __m128i T01, T23;
+
+ // Load and concatenate the transform coefficients.
+ const __m128i in01 = _mm_loadu_si128((const __m128i*)&in[0]);
+ const __m128i in23 = _mm_loadu_si128((const __m128i*)&in[8]);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+
+ // Vertical pass and subsequent transpose.
+ {
+ const __m128i in1 = _mm_unpackhi_epi64(in01, in01);
+ const __m128i in3 = _mm_unpackhi_epi64(in23, in23);
+
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
+ // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
+ const __m128i a_d3 = _mm_add_epi16(in01, in23);
+ const __m128i b_c3 = _mm_sub_epi16(in01, in23);
+ const __m128i c1d1 = _mm_mulhi_epi16(in1, k2k1);
+ const __m128i c2d2 = _mm_mulhi_epi16(in3, k1k2);
+ const __m128i c3 = _mm_unpackhi_epi64(b_c3, b_c3);
+ const __m128i c4 = _mm_sub_epi16(c1d1, c2d2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ const __m128i d4u = _mm_add_epi16(c1d1, c2d2);
+ const __m128i du = _mm_add_epi16(a_d3, d4u);
+ const __m128i d = _mm_unpackhi_epi64(du, du);
+
+ // Second pass.
+ const __m128i comb_ab = _mm_unpacklo_epi64(a_d3, b_c3);
+ const __m128i comb_dc = _mm_unpacklo_epi64(d, c);
+
+ const __m128i tmp01 = _mm_add_epi16(comb_ab, comb_dc);
+ const __m128i tmp32 = _mm_sub_epi16(comb_ab, comb_dc);
+ const __m128i tmp23 = _mm_shuffle_epi32(tmp32, _MM_SHUFFLE(1, 0, 3, 2));
+
+ const __m128i transpose_0 = _mm_unpacklo_epi16(tmp01, tmp23);
+ const __m128i transpose_1 = _mm_unpackhi_epi16(tmp01, tmp23);
+ // a00 a20 a01 a21 a02 a22 a03 a23
+ // a10 a30 a11 a31 a12 a32 a13 a33
+
+ T01 = _mm_unpacklo_epi16(transpose_0, transpose_1);
+ T23 = _mm_unpackhi_epi16(transpose_0, transpose_1);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+ }
+
+ // Horizontal pass and subsequent transpose.
+ {
+ const __m128i T1 = _mm_unpackhi_epi64(T01, T01);
+ const __m128i T3 = _mm_unpackhi_epi64(T23, T23);
+
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ const __m128i dc = _mm_add_epi16(T01, zero_four);
+
+ // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
+ // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
+ const __m128i a_d3 = _mm_add_epi16(dc, T23);
+ const __m128i b_c3 = _mm_sub_epi16(dc, T23);
+ const __m128i c1d1 = _mm_mulhi_epi16(T1, k2k1);
+ const __m128i c2d2 = _mm_mulhi_epi16(T3, k1k2);
+ const __m128i c3 = _mm_unpackhi_epi64(b_c3, b_c3);
+ const __m128i c4 = _mm_sub_epi16(c1d1, c2d2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ const __m128i d4u = _mm_add_epi16(c1d1, c2d2);
+ const __m128i du = _mm_add_epi16(a_d3, d4u);
+ const __m128i d = _mm_unpackhi_epi64(du, du);
+
+ // Second pass.
+ const __m128i comb_ab = _mm_unpacklo_epi64(a_d3, b_c3);
+ const __m128i comb_dc = _mm_unpacklo_epi64(d, c);
+
+ const __m128i tmp01 = _mm_add_epi16(comb_ab, comb_dc);
+ const __m128i tmp32 = _mm_sub_epi16(comb_ab, comb_dc);
+ const __m128i tmp23 = _mm_shuffle_epi32(tmp32, _MM_SHUFFLE(1, 0, 3, 2));
+
+ const __m128i shifted01 = _mm_srai_epi16(tmp01, 3);
+ const __m128i shifted23 = _mm_srai_epi16(tmp23, 3);
+ // a00 a01 a02 a03 a10 a11 a12 a13
+ // a20 a21 a22 a23 a30 a31 a32 a33
+
+ const __m128i transpose_0 = _mm_unpacklo_epi16(shifted01, shifted23);
+ const __m128i transpose_1 = _mm_unpackhi_epi16(shifted01, shifted23);
+ // a00 a20 a01 a21 a02 a22 a03 a23
+ // a10 a30 a11 a31 a12 a32 a13 a33
+
+ T01 = _mm_unpacklo_epi16(transpose_0, transpose_1);
+ T23 = _mm_unpackhi_epi16(transpose_0, transpose_1);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+ }
+
+ // Add inverse transform to 'ref' and store.
+ {
+ // Load the reference(s).
+ __m128i ref01, ref23, ref0123;
+ int32_t buf[4];
+
+ // Load four bytes/pixels per line.
+ const __m128i ref0 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[0 * BPS]));
+ const __m128i ref1 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[1 * BPS]));
+ const __m128i ref2 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[2 * BPS]));
+ const __m128i ref3 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[3 * BPS]));
+ ref01 = _mm_unpacklo_epi32(ref0, ref1);
+ ref23 = _mm_unpacklo_epi32(ref2, ref3);
+
+ // Convert to 16b.
+ ref01 = _mm_unpacklo_epi8(ref01, zero);
+ ref23 = _mm_unpacklo_epi8(ref23, zero);
+ // Add the inverse transform(s).
+ ref01 = _mm_add_epi16(ref01, T01);
+ ref23 = _mm_add_epi16(ref23, T23);
+ // Unsigned saturate to 8b.
+ ref0123 = _mm_packus_epi16(ref01, ref23);
+
+ _mm_storeu_si128((__m128i *)buf, ref0123);
+
+ // Store four bytes/pixels per line.
+ WebPInt32ToMem(&dst[0 * BPS], buf[0]);
+ WebPInt32ToMem(&dst[1 * BPS], buf[1]);
+ WebPInt32ToMem(&dst[2 * BPS], buf[2]);
+ WebPInt32ToMem(&dst[3 * BPS], buf[3]);
+ }
+}
+
+// Does two inverse transforms.
+static void ITransform_Two_SSE2(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst) {
// This implementation makes use of 16-bit fixed point versions of two
// multiply constants:
// K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
@@ -49,33 +200,21 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
__m128i T0, T1, T2, T3;
// Load and concatenate the transform coefficients (we'll do two inverse
- // transforms in parallel). In the case of only one inverse transform, the
- // second half of the vectors will just contain random value we'll never
- // use nor store.
+ // transforms in parallel).
__m128i in0, in1, in2, in3;
{
- in0 = _mm_loadl_epi64((const __m128i*)&in[0]);
- in1 = _mm_loadl_epi64((const __m128i*)&in[4]);
- in2 = _mm_loadl_epi64((const __m128i*)&in[8]);
- in3 = _mm_loadl_epi64((const __m128i*)&in[12]);
- // a00 a10 a20 a30 x x x x
- // a01 a11 a21 a31 x x x x
- // a02 a12 a22 a32 x x x x
- // a03 a13 a23 a33 x x x x
- if (do_two) {
- const __m128i inB0 = _mm_loadl_epi64((const __m128i*)&in[16]);
- const __m128i inB1 = _mm_loadl_epi64((const __m128i*)&in[20]);
- const __m128i inB2 = _mm_loadl_epi64((const __m128i*)&in[24]);
- const __m128i inB3 = _mm_loadl_epi64((const __m128i*)&in[28]);
- in0 = _mm_unpacklo_epi64(in0, inB0);
- in1 = _mm_unpacklo_epi64(in1, inB1);
- in2 = _mm_unpacklo_epi64(in2, inB2);
- in3 = _mm_unpacklo_epi64(in3, inB3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
+ const __m128i tmp0 = _mm_loadu_si128((const __m128i*)&in[0]);
+ const __m128i tmp1 = _mm_loadu_si128((const __m128i*)&in[8]);
+ const __m128i tmp2 = _mm_loadu_si128((const __m128i*)&in[16]);
+ const __m128i tmp3 = _mm_loadu_si128((const __m128i*)&in[24]);
+ in0 = _mm_unpacklo_epi64(tmp0, tmp2);
+ in1 = _mm_unpackhi_epi64(tmp0, tmp2);
+ in2 = _mm_unpacklo_epi64(tmp1, tmp3);
+ in3 = _mm_unpackhi_epi64(tmp1, tmp3);
+ // a00 a10 a20 a30 b00 b10 b20 b30
+ // a01 a11 a21 a31 b01 b11 b21 b31
+ // a02 a12 a22 a32 b02 b12 b22 b32
+ // a03 a13 a23 a33 b03 b13 b23 b33
}
// Vertical pass and subsequent transpose.
@@ -148,19 +287,11 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
const __m128i zero = _mm_setzero_si128();
// Load the reference(s).
__m128i ref0, ref1, ref2, ref3;
- if (do_two) {
- // Load eight bytes/pixels per line.
- ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
- ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
- ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
- ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
- } else {
- // Load four bytes/pixels per line.
- ref0 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[0 * BPS]));
- ref1 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[1 * BPS]));
- ref2 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[2 * BPS]));
- ref3 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[3 * BPS]));
- }
+ // Load eight bytes/pixels per line.
+ ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
+ ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
+ ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
+ ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
// Convert to 16b.
ref0 = _mm_unpacklo_epi8(ref0, zero);
ref1 = _mm_unpacklo_epi8(ref1, zero);
@@ -176,20 +307,21 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
ref1 = _mm_packus_epi16(ref1, ref1);
ref2 = _mm_packus_epi16(ref2, ref2);
ref3 = _mm_packus_epi16(ref3, ref3);
- // Store the results.
- if (do_two) {
- // Store eight bytes/pixels per line.
- _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
- _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
- _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
- _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
- } else {
- // Store four bytes/pixels per line.
- WebPInt32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0));
- WebPInt32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1));
- WebPInt32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2));
- WebPInt32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3));
- }
+ // Store eight bytes/pixels per line.
+ _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
+ _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
+ _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
+ _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
+ }
+}
+
+// Does one or two inverse transforms.
+static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
+ int do_two) {
+ if (do_two) {
+ ITransform_Two_SSE2(ref, in, dst);
+ } else {
+ ITransform_One_SSE2(ref, in, dst);
}
}
diff --git a/thirdparty/libwebp/src/dsp/filters.c b/thirdparty/libwebp/src/dsp/filters.c
index 4506567ba3..85eee5098f 100644
--- a/thirdparty/libwebp/src/dsp/filters.c
+++ b/thirdparty/libwebp/src/dsp/filters.c
@@ -233,6 +233,7 @@ static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in,
WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8FiltersInitMIPSdspR2(void);
extern void VP8FiltersInitMSA(void);
extern void VP8FiltersInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/lossless.c b/thirdparty/libwebp/src/dsp/lossless.c
index fb86e58d4a..9f81209453 100644
--- a/thirdparty/libwebp/src/dsp/lossless.c
+++ b/thirdparty/libwebp/src/dsp/lossless.c
@@ -588,6 +588,7 @@ VP8LConvertFunc VP8LConvertBGRAToBGR;
VP8LMapARGBFunc VP8LMapColor32b;
VP8LMapAlphaFunc VP8LMapColor8b;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8LDspInitSSE2(void);
extern void VP8LDspInitSSE41(void);
extern void VP8LDspInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/lossless_enc.c b/thirdparty/libwebp/src/dsp/lossless_enc.c
index b1f9f26d72..cde1280617 100644
--- a/thirdparty/libwebp/src/dsp/lossless_enc.c
+++ b/thirdparty/libwebp/src/dsp/lossless_enc.c
@@ -791,6 +791,7 @@ VP8LBundleColorMapFunc VP8LBundleColorMap;
VP8LPredictorAddSubFunc VP8LPredictorsSub[16];
VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8LEncDspInitSSE2(void);
extern void VP8LEncDspInitSSE41(void);
extern void VP8LEncDspInitNEON(void);
diff --git a/thirdparty/libwebp/src/dsp/lossless_enc_neon.c b/thirdparty/libwebp/src/dsp/lossless_enc_neon.c
index 7c7b73f8b6..e32c7961a2 100644
--- a/thirdparty/libwebp/src/dsp/lossless_enc_neon.c
+++ b/thirdparty/libwebp/src/dsp/lossless_enc_neon.c
@@ -25,7 +25,7 @@
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
#define USE_VTBLQ
#endif
diff --git a/thirdparty/libwebp/src/dsp/lossless_neon.c b/thirdparty/libwebp/src/dsp/lossless_neon.c
index 89e3e013a0..ddc9b61711 100644
--- a/thirdparty/libwebp/src/dsp/lossless_neon.c
+++ b/thirdparty/libwebp/src/dsp/lossless_neon.c
@@ -498,7 +498,7 @@ static void PredictorAdd13_NEON(const uint32_t* in, const uint32_t* upper,
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
#define USE_VTBLQ
#endif
diff --git a/thirdparty/libwebp/src/dsp/neon.h b/thirdparty/libwebp/src/dsp/neon.h
index c591f9b9a7..14acb4044b 100644
--- a/thirdparty/libwebp/src/dsp/neon.h
+++ b/thirdparty/libwebp/src/dsp/neon.h
@@ -21,7 +21,7 @@
// Right now, some intrinsics functions seem slower, so we disable them
// everywhere except newer clang/gcc or aarch64 where the inline assembly is
// incompatible.
-#if LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,9) || defined(__aarch64__)
+#if LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 9) || WEBP_AARCH64
#define WEBP_USE_INTRINSICS // use intrinsics when possible
#endif
@@ -46,7 +46,7 @@
// if using intrinsics, this flag avoids some functions that make gcc-4.6.3
// crash ("internal compiler error: in immed_double_const, at emit-rtl.").
// (probably similar to gcc.gnu.org/bugzilla/show_bug.cgi?id=48183)
-#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__))
+#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || WEBP_AARCH64)
#define WORK_AROUND_GCC
#endif
diff --git a/thirdparty/libwebp/src/dsp/quant.h b/thirdparty/libwebp/src/dsp/quant.h
index fc099bf9d6..bf7734cb11 100644
--- a/thirdparty/libwebp/src/dsp/quant.h
+++ b/thirdparty/libwebp/src/dsp/quant.h
@@ -22,7 +22,7 @@
#define IsFlat IsFlat_NEON
static uint32_t horizontal_add_uint32x4(const uint32x4_t a) {
-#if defined(__aarch64__)
+#if WEBP_AARCH64
return vaddvq_u32(a);
#else
const uint64x2_t b = vpaddlq_u32(a);
diff --git a/thirdparty/libwebp/src/dsp/rescaler.c b/thirdparty/libwebp/src/dsp/rescaler.c
index 14620ce4f1..325d8be180 100644
--- a/thirdparty/libwebp/src/dsp/rescaler.c
+++ b/thirdparty/libwebp/src/dsp/rescaler.c
@@ -197,6 +197,7 @@ WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPRescalerDspInitSSE2(void);
extern void WebPRescalerDspInitMIPS32(void);
extern void WebPRescalerDspInitMIPSdspR2(void);
diff --git a/thirdparty/libwebp/src/dsp/ssim.c b/thirdparty/libwebp/src/dsp/ssim.c
index f85c2e6e5b..9a1341ed95 100644
--- a/thirdparty/libwebp/src/dsp/ssim.c
+++ b/thirdparty/libwebp/src/dsp/ssim.c
@@ -137,6 +137,7 @@ VP8SSIMGetClippedFunc VP8SSIMGetClipped;
VP8AccumulateSSEFunc VP8AccumulateSSE;
#endif
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8SSIMDspInitSSE2(void);
WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) {
diff --git a/thirdparty/libwebp/src/dsp/upsampling.c b/thirdparty/libwebp/src/dsp/upsampling.c
index 87f771f3eb..983b9c42d3 100644
--- a/thirdparty/libwebp/src/dsp/upsampling.c
+++ b/thirdparty/libwebp/src/dsp/upsampling.c
@@ -215,6 +215,7 @@ static void EmptyYuv444Func(const uint8_t* y,
WebPYUV444Converter WebPYUV444Converters[MODE_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitYUV444ConvertersMIPSdspR2(void);
extern void WebPInitYUV444ConvertersSSE2(void);
extern void WebPInitYUV444ConvertersSSE41(void);
diff --git a/thirdparty/libwebp/src/dsp/upsampling_neon.c b/thirdparty/libwebp/src/dsp/upsampling_neon.c
index 6ba71a7de5..bbc000ca2d 100644
--- a/thirdparty/libwebp/src/dsp/upsampling_neon.c
+++ b/thirdparty/libwebp/src/dsp/upsampling_neon.c
@@ -111,7 +111,7 @@ static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 };
vst4_u8(out, v255_r_g_b); \
} while (0)
-#if !defined(WEBP_SWAP_16BIT_CSP)
+#if (WEBP_SWAP_16BIT_CSP == 0)
#define ZIP_U8(lo, hi) vzip_u8((lo), (hi))
#else
#define ZIP_U8(lo, hi) vzip_u8((hi), (lo))
diff --git a/thirdparty/libwebp/src/dsp/yuv.c b/thirdparty/libwebp/src/dsp/yuv.c
index d16c13d3ca..8a04b85d82 100644
--- a/thirdparty/libwebp/src/dsp/yuv.c
+++ b/thirdparty/libwebp/src/dsp/yuv.c
@@ -70,6 +70,7 @@ void WebPSamplerProcessPlane(const uint8_t* y, int y_stride,
WebPSamplerRowFunc WebPSamplers[MODE_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitSamplersSSE2(void);
extern void WebPInitSamplersSSE41(void);
extern void WebPInitSamplersMIPS32(void);
diff --git a/thirdparty/libwebp/src/enc/alpha_enc.c b/thirdparty/libwebp/src/enc/alpha_enc.c
index f7c02690e3..26f003485a 100644
--- a/thirdparty/libwebp/src/enc/alpha_enc.c
+++ b/thirdparty/libwebp/src/enc/alpha_enc.c
@@ -13,6 +13,7 @@
#include <assert.h>
#include <stdlib.h>
+#include <string.h>
#include "src/enc/vp8i_enc.h"
#include "src/dsp/dsp.h"
@@ -140,6 +141,11 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
!reduce_levels, &tmp_bw, &result->stats);
if (ok) {
output = VP8LBitWriterFinish(&tmp_bw);
+ if (tmp_bw.error_) {
+ VP8LBitWriterWipeOut(&tmp_bw);
+ memset(&result->bw, 0, sizeof(result->bw));
+ return 0;
+ }
output_size = VP8LBitWriterNumBytes(&tmp_bw);
if (output_size > data_size) {
// compressed size is larger than source! Revert to uncompressed mode.
@@ -148,6 +154,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
}
} else {
VP8LBitWriterWipeOut(&tmp_bw);
+ memset(&result->bw, 0, sizeof(result->bw));
return 0;
}
}
@@ -162,7 +169,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
header = method | (filter << 2);
if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
- VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size);
+ if (!VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size)) ok = 0;
ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
ok = ok && VP8BitWriterAppend(&result->bw, output, output_size);
@@ -312,11 +319,11 @@ static int EncodeAlpha(VP8Encoder* const enc,
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
if (quality < 0 || quality > 100) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
}
if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
}
if (method == ALPHA_NO_COMPRESSION) {
@@ -326,7 +333,7 @@ static int EncodeAlpha(VP8Encoder* const enc,
quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
if (quant_alpha == NULL) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// Extract alpha data (width x height) from raw_data (stride x height).
@@ -346,6 +353,9 @@ static int EncodeAlpha(VP8Encoder* const enc,
ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
filter, reduce_levels, effort_level, output,
output_size, pic->stats);
+ if (!ok) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise
+ }
#if !defined(WEBP_DISABLE_STATS)
if (pic->stats != NULL) { // need stats?
pic->stats->coded_size += (int)(*output_size);
@@ -405,7 +415,7 @@ int VP8EncStartAlpha(VP8Encoder* const enc) {
WebPWorker* const worker = &enc->alpha_worker_;
// Makes sure worker is good to go.
if (!WebPGetWorkerInterface()->Reset(worker)) {
- return 0;
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
WebPGetWorkerInterface()->Launch(worker);
return 1;
diff --git a/thirdparty/libwebp/src/enc/analysis_enc.c b/thirdparty/libwebp/src/enc/analysis_enc.c
index a0001ac034..962eaa998f 100644
--- a/thirdparty/libwebp/src/enc/analysis_enc.c
+++ b/thirdparty/libwebp/src/enc/analysis_enc.c
@@ -474,6 +474,10 @@ int VP8EncAnalyze(VP8Encoder* const enc) {
} else { // Use only one default segment.
ResetAllMBInfo(enc);
}
+ if (!ok) {
+ return WebPEncodingSetError(enc->pic_,
+ VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise
+ }
return ok;
}
diff --git a/thirdparty/libwebp/src/enc/backward_references_enc.c b/thirdparty/libwebp/src/enc/backward_references_enc.c
index 49a0fac034..dc98bf1719 100644
--- a/thirdparty/libwebp/src/enc/backward_references_enc.c
+++ b/thirdparty/libwebp/src/enc/backward_references_enc.c
@@ -283,8 +283,7 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality,
hash_to_first_index =
(int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
if (hash_to_first_index == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
percent_range = remaining_percent / 2;
@@ -1050,8 +1049,7 @@ int VP8LGetBackwardReferences(
refs_best = GetBackwardReferencesLowEffort(
width, height, argb, cache_bits_best, hash_chain, refs);
if (refs_best == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// Set it in first position.
BackwardRefsSwap(refs_best, &refs[0]);
@@ -1059,8 +1057,7 @@ int VP8LGetBackwardReferences(
if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try,
cache_bits_max, do_no_cache, hash_chain, refs,
cache_bits_best)) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
}
diff --git a/thirdparty/libwebp/src/enc/frame_enc.c b/thirdparty/libwebp/src/enc/frame_enc.c
index b93d9e5b99..9a98dc1f3e 100644
--- a/thirdparty/libwebp/src/enc/frame_enc.c
+++ b/thirdparty/libwebp/src/enc/frame_enc.c
@@ -689,7 +689,7 @@ static int PreLoopInitialize(VP8Encoder* const enc) {
}
if (!ok) {
VP8EncFreeBitWriters(enc); // malloc error occurred
- WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
return ok;
}
@@ -719,6 +719,7 @@ static int PostLoopFinalize(VP8EncIterator* const it, int ok) {
} else {
// Something bad happened -> need to do some memory cleanup.
VP8EncFreeBitWriters(enc);
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
return ok;
}
@@ -754,6 +755,11 @@ int VP8EncLoop(VP8Encoder* const enc) {
// *then* decide how to code the skip decision if there's one.
if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) {
CodeResiduals(it.bw_, &it, &info);
+ if (it.bw_->error_) {
+ // enc->pic_->error_code is set in PostLoopFinalize().
+ ok = 0;
+ break;
+ }
} else { // reset predictors after a skip
ResetAfterSkip(&it);
}
diff --git a/thirdparty/libwebp/src/enc/picture_csp_enc.c b/thirdparty/libwebp/src/enc/picture_csp_enc.c
index 78c8ca479b..a9280e6c30 100644
--- a/thirdparty/libwebp/src/enc/picture_csp_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_csp_enc.c
@@ -98,6 +98,7 @@ static int kLinearToGammaTab[GAMMA_TAB_SIZE + 1];
static uint16_t kGammaToLinearTab[256];
static volatile int kGammaTablesOk = 0;
static void InitGammaTables(void);
+extern VP8CPUInfo VP8GetCPUInfo;
WEBP_DSP_INIT_FUNC(InitGammaTables) {
if (!kGammaTablesOk) {
@@ -534,7 +535,9 @@ static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
WebPInitConvertARGBToYUV();
InitGammaTables();
- if (tmp_rgb == NULL) return 0; // malloc error
+ if (tmp_rgb == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
// Downsample Y/U/V planes, two rows at a time
for (y = 0; y < (height >> 1); ++y) {
diff --git a/thirdparty/libwebp/src/enc/picture_rescale_enc.c b/thirdparty/libwebp/src/enc/picture_rescale_enc.c
index 839f91cacc..ea90d82548 100644
--- a/thirdparty/libwebp/src/enc/picture_rescale_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_rescale_enc.c
@@ -137,7 +137,9 @@ int WebPPictureCrop(WebPPicture* pic,
PictureGrabSpecs(pic, &tmp);
tmp.width = width;
tmp.height = height;
- if (!WebPPictureAlloc(&tmp)) return 0;
+ if (!WebPPictureAlloc(&tmp)) {
+ return WebPEncodingSetError(pic, tmp.error_code);
+ }
if (!pic->use_argb) {
const int y_offset = top * pic->y_stride + left;
@@ -212,26 +214,28 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) {
prev_height = picture->height;
if (!WebPRescalerGetScaledDimensions(
prev_width, prev_height, &width, &height)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
PictureGrabSpecs(picture, &tmp);
tmp.width = width;
tmp.height = height;
- if (!WebPPictureAlloc(&tmp)) return 0;
+ if (!WebPPictureAlloc(&tmp)) {
+ return WebPEncodingSetError(picture, tmp.error_code);
+ }
if (!picture->use_argb) {
work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
if (work == NULL) {
WebPPictureFree(&tmp);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// If present, we need to rescale alpha first (for AlphaMultiplyY).
if (picture->a != NULL) {
WebPInitAlphaProcessing();
if (!RescalePlane(picture->a, prev_width, prev_height, picture->a_stride,
tmp.a, width, height, tmp.a_stride, work, 1)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
}
@@ -246,14 +250,14 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) {
!RescalePlane(picture->v, HALVE(prev_width), HALVE(prev_height),
picture->uv_stride, tmp.v, HALVE(width), HALVE(height),
tmp.uv_stride, work, 1)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
AlphaMultiplyY(&tmp, 1);
} else {
work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
if (work == NULL) {
WebPPictureFree(&tmp);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// In order to correctly interpolate colors, we need to apply the alpha
// weighting first (black-matting), scale the RGB values, and remove
@@ -263,7 +267,7 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) {
if (!RescalePlane((const uint8_t*)picture->argb, prev_width, prev_height,
picture->argb_stride * 4, (uint8_t*)tmp.argb, width,
height, tmp.argb_stride * 4, work, 4)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
AlphaMultiplyARGB(&tmp, 1);
}
diff --git a/thirdparty/libwebp/src/enc/syntax_enc.c b/thirdparty/libwebp/src/enc/syntax_enc.c
index e18cf650ca..9b8f524d69 100644
--- a/thirdparty/libwebp/src/enc/syntax_enc.c
+++ b/thirdparty/libwebp/src/enc/syntax_enc.c
@@ -258,7 +258,10 @@ static int EmitPartitionsSize(const VP8Encoder* const enc,
buf[3 * p + 1] = (part_size >> 8) & 0xff;
buf[3 * p + 2] = (part_size >> 16) & 0xff;
}
- return p ? pic->writer(buf, 3 * p, pic) : 1;
+ if (p && !pic->writer(buf, 3 * p, pic)) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
+ }
+ return 1;
}
//------------------------------------------------------------------------------
@@ -381,6 +384,7 @@ int VP8EncWrite(VP8Encoder* const enc) {
enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_);
+ if (!ok) WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
return ok;
}
diff --git a/thirdparty/libwebp/src/enc/vp8i_enc.h b/thirdparty/libwebp/src/enc/vp8i_enc.h
index c9927c47d8..19d9a6edb7 100644
--- a/thirdparty/libwebp/src/enc/vp8i_enc.h
+++ b/thirdparty/libwebp/src/enc/vp8i_enc.h
@@ -32,7 +32,7 @@ extern "C" {
// version numbers
#define ENC_MAJ_VERSION 1
#define ENC_MIN_VERSION 3
-#define ENC_REV_VERSION 0
+#define ENC_REV_VERSION 1
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
diff --git a/thirdparty/libwebp/src/enc/vp8l_enc.c b/thirdparty/libwebp/src/enc/vp8l_enc.c
index 0b07e529a9..3a8ec3dd1e 100644
--- a/thirdparty/libwebp/src/enc/vp8l_enc.c
+++ b/thirdparty/libwebp/src/enc/vp8l_enc.c
@@ -196,8 +196,7 @@ static int CoOccurrenceBuild(const WebPPicture* const pic,
uint32_t palette_sorted[MAX_PALETTE_SIZE];
lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
if (lines == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
line_top = &lines[0];
line_current = &lines[pic->width];
@@ -255,10 +254,10 @@ static int PaletteSortModifiedZeng(
cooccurrence =
(uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
if (cooccurrence == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
if (!CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence)) {
+ WebPSafeFree(cooccurrence);
return 0;
}
@@ -1012,8 +1011,7 @@ static int StoreImageToBitMask(
VP8LRefsCursorNext(&c);
}
if (bw->error_) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
return 1;
}
@@ -1297,7 +1295,10 @@ static int EncodeImageInternal(
}
}
tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
- if (tokens == NULL) goto Error;
+ if (tokens == NULL) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ goto Error;
+ }
for (i = 0; i < 5 * histogram_image_size; ++i) {
HuffmanTreeCode* const codes = &huffman_codes[i];
StoreHuffmanCode(bw, huff_tree, tokens, codes);
@@ -1448,18 +1449,21 @@ static int WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw,
const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
const size_t pad = vp8l_size & 1;
const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
+ *coded_size = 0;
+
+ if (bw->error_) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
if (!WriteRiffHeader(pic, riff_size, vp8l_size) ||
!pic->writer(webpll_data, webpll_size, pic)) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
}
if (pad) {
const uint8_t pad_byte[1] = { 0 };
if (!pic->writer(pad_byte, 1, pic)) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
}
}
*coded_size = CHUNK_HEADER_SIZE + riff_size;
@@ -1504,8 +1508,7 @@ static int AllocateTransformBuffer(VP8LEncoder* const enc, int width,
ClearTransformBuffer(enc);
mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem));
if (mem == NULL) {
- WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
enc->transform_mem_ = mem;
enc->transform_mem_size_ = (size_t)mem_size;
@@ -1613,8 +1616,7 @@ static int ApplyPalette(const uint32_t* src, uint32_t src_stride, uint32_t* dst,
int x, y;
if (tmp_row == NULL) {
- WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
if (palette_size < APPLY_PALETTE_GREEDY_MAX) {
@@ -1968,9 +1970,8 @@ int VP8LEncodeStream(const WebPConfig* const config,
int ok_main;
if (enc_main == NULL || !VP8LBitWriterInit(&bw_side, 0)) {
- WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
VP8LEncoderDelete(enc_main);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// Avoid "garbage value" error from Clang's static analysis tool.
@@ -2117,8 +2118,7 @@ int VP8LEncodeImage(const WebPConfig* const config,
if (picture == NULL) return 0;
if (config == NULL || picture->argb == NULL) {
- WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
}
width = picture->width;
diff --git a/thirdparty/libwebp/src/enc/webp_enc.c b/thirdparty/libwebp/src/enc/webp_enc.c
index 9620e05070..583fe6a8bb 100644
--- a/thirdparty/libwebp/src/enc/webp_enc.c
+++ b/thirdparty/libwebp/src/enc/webp_enc.c
@@ -307,7 +307,10 @@ int WebPEncodingSetError(const WebPPicture* const pic,
WebPEncodingError error) {
assert((int)error < VP8_ENC_ERROR_LAST);
assert((int)error >= VP8_ENC_OK);
- ((WebPPicture*)pic)->error_code = error;
+ // The oldest error reported takes precedence over the new one.
+ if (pic->error_code == VP8_ENC_OK) {
+ ((WebPPicture*)pic)->error_code = error;
+ }
return 0;
}
@@ -317,8 +320,7 @@ int WebPReportProgress(const WebPPicture* const pic,
*percent_store = percent;
if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
// user abort requested
- WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
}
}
return 1; // ok
@@ -329,7 +331,7 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
int ok = 0;
if (pic == NULL) return 0;
- WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far
+ pic->error_code = VP8_ENC_OK; // all ok so far
if (config == NULL) { // bad params
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
}
diff --git a/thirdparty/libwebp/src/mux/muxi.h b/thirdparty/libwebp/src/mux/muxi.h
index 7929138c44..fc44d6f2fe 100644
--- a/thirdparty/libwebp/src/mux/muxi.h
+++ b/thirdparty/libwebp/src/mux/muxi.h
@@ -29,7 +29,7 @@ extern "C" {
#define MUX_MAJ_VERSION 1
#define MUX_MIN_VERSION 3
-#define MUX_REV_VERSION 0
+#define MUX_REV_VERSION 1
// Chunk object.
typedef struct WebPChunk WebPChunk;
diff --git a/thirdparty/libwebp/src/mux/muxread.c b/thirdparty/libwebp/src/mux/muxread.c
index 80050396e1..9862ec68ee 100644
--- a/thirdparty/libwebp/src/mux/muxread.c
+++ b/thirdparty/libwebp/src/mux/muxread.c
@@ -116,9 +116,12 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
// Each of ANMF chunk contain a header at the beginning. So, its size should
// be at least 'hdr_size'.
if (size < hdr_size) goto Fail;
- ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
+ if (ChunkAssignData(&subchunk, &temp, copy_data,
+ chunk->tag_) != WEBP_MUX_OK) {
+ goto Fail;
+ }
}
- ChunkSetHead(&subchunk, &wpi->header_);
+ if (ChunkSetHead(&subchunk, &wpi->header_) != WEBP_MUX_OK) goto Fail;
wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
// Rest of the chunks.
diff --git a/thirdparty/libwebp/src/utils/bit_reader_utils.c b/thirdparty/libwebp/src/utils/bit_reader_utils.c
index 857cd60988..a26557aa49 100644
--- a/thirdparty/libwebp/src/utils/bit_reader_utils.c
+++ b/thirdparty/libwebp/src/utils/bit_reader_utils.c
@@ -15,6 +15,7 @@
#include "src/webp/config.h"
#endif
+#include "src/dsp/cpu.h"
#include "src/utils/bit_reader_inl_utils.h"
#include "src/utils/utils.h"
@@ -121,7 +122,7 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int bits,
#define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits.
-#if defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \
+#if defined(__arm__) || defined(_M_ARM) || WEBP_AARCH64 || \
defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64__) || defined(_M_X64)
#define VP8L_USE_FAST_LOAD
diff --git a/thirdparty/libwebp/src/utils/bit_reader_utils.h b/thirdparty/libwebp/src/utils/bit_reader_utils.h
index e64156e318..25ff31e5d9 100644
--- a/thirdparty/libwebp/src/utils/bit_reader_utils.h
+++ b/thirdparty/libwebp/src/utils/bit_reader_utils.h
@@ -19,6 +19,7 @@
#ifdef _MSC_VER
#include <stdlib.h> // _byteswap_ulong
#endif
+#include "src/dsp/cpu.h"
#include "src/webp/types.h"
// Warning! This macro triggers quite some MACRO wizardry around func signature!
@@ -64,7 +65,7 @@ extern "C" {
#define BITS 56
#elif defined(__arm__) || defined(_M_ARM) // ARM
#define BITS 24
-#elif defined(__aarch64__) // ARM 64bit
+#elif WEBP_AARCH64 // ARM 64bit
#define BITS 56
#elif defined(__mips__) // MIPS
#define BITS 24
diff --git a/thirdparty/libwebp/src/webp/decode.h b/thirdparty/libwebp/src/webp/decode.h
index d98247509a..0177b12089 100644
--- a/thirdparty/libwebp/src/webp/decode.h
+++ b/thirdparty/libwebp/src/webp/decode.h
@@ -81,10 +81,11 @@ WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
// returned is the Y samples buffer. Upon return, *u and *v will point to
// the U and V chroma data. These U and V buffers need NOT be passed to
// WebPFree(), unlike the returned Y luma one. The dimension of the U and V
-// planes are both (*width + 1) / 2 and (*height + 1)/ 2.
+// planes are both (*width + 1) / 2 and (*height + 1) / 2.
// Upon return, the Y buffer has a stride returned as '*stride', while U and V
// have a common stride returned as '*uv_stride'.
-// Return NULL in case of error.
+// 'width' and 'height' may be NULL, the other pointers must not be.
+// Returns NULL in case of error.
// (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr
WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
int* width, int* height,
diff --git a/thirdparty/miniupnpc/LICENSE b/thirdparty/miniupnpc/LICENSE
index 67ff3bb627..6eff8d2683 100644
--- a/thirdparty/miniupnpc/LICENSE
+++ b/thirdparty/miniupnpc/LICENSE
@@ -1,6 +1,6 @@
BSD 3-Clause License
-Copyright (c) 2005-2022, Thomas BERNARD
+Copyright (c) 2005-2023, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/thirdparty/miniupnpc/include/miniupnpc.h b/thirdparty/miniupnpc/include/miniupnpc.h
index 75fb8b702d..721819583f 100644
--- a/thirdparty/miniupnpc/include/miniupnpc.h
+++ b/thirdparty/miniupnpc/include/miniupnpc.h
@@ -1,4 +1,4 @@
-/* $Id: miniupnpc.h,v 1.61 2022/10/21 21:15:02 nanard Exp $ */
+/* $Id: miniupnpc.h,v 1.62 2023/06/11 23:25:46 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project: miniupnp
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
@@ -20,7 +20,7 @@
#define UPNPDISCOVER_MEMORY_ERROR (-102)
/* versions : */
-#define MINIUPNPC_VERSION "2.2.4"
+#define MINIUPNPC_VERSION "2.2.5"
#define MINIUPNPC_API_VERSION 17
/* Source port:
diff --git a/thirdparty/miniupnpc/src/miniupnpcstrings.h b/thirdparty/miniupnpc/src/miniupnpcstrings.h
index 25abc1016e..d89d9e443b 100644
--- a/thirdparty/miniupnpc/src/miniupnpcstrings.h
+++ b/thirdparty/miniupnpc/src/miniupnpcstrings.h
@@ -4,7 +4,7 @@
#include "core/version.h"
#define OS_STRING VERSION_NAME "/1.0"
-#define MINIUPNPC_VERSION_STRING "2.2.4"
+#define MINIUPNPC_VERSION_STRING "2.2.5"
#if 0
/* according to "UPnP Device Architecture 1.0" */
diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h
index d08e97f6cd..e5ac54a506 100644
--- a/thirdparty/tinyexr/tinyexr.h
+++ b/thirdparty/tinyexr/tinyexr.h
@@ -114,6 +114,11 @@ extern "C" {
#define TINYEXR_USE_STB_ZLIB (0)
#endif
+// Use nanozlib.
+#ifndef TINYEXR_USE_NANOZLIB
+#define TINYEXR_USE_NANOZLIB (0)
+#endif
+
// Disable PIZ compression when applying cpplint.
#ifndef TINYEXR_USE_PIZ
#define TINYEXR_USE_PIZ (1)
@@ -608,7 +613,10 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
#define NOMINMAX
#endif
#include <windows.h> // for UTF-8 and memory-mapping
+
+#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
#define TINYEXR_USE_WIN32_MMAP (1)
+#endif
#elif defined(__linux__) || defined(__unix__)
#include <fcntl.h> // for open()
@@ -650,7 +658,7 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
#include <omp.h>
#endif
-#if TINYEXR_USE_MINIZ
+#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
#include <miniz.h>
#else
// Issue #46. Please include your own zlib-compatible API header before
@@ -658,6 +666,11 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
//#include "zlib.h"
#endif
+#if defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
+#define NANOZLIB_IMPLEMENTATION
+#include "nanozlib.h"
+#endif
+
#if TINYEXR_USE_STB_ZLIB
// Since we don't know where a project has stb_image.h and stb_image_write.h
// and whether they are in the include path, we don't include them here, and
@@ -668,6 +681,7 @@ extern "C" int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuf
extern "C" unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality);
#endif
+
#if TINYEXR_USE_ZFP
#ifdef __clang__
@@ -1343,7 +1357,7 @@ static bool CompressZip(unsigned char *dst,
}
}
-#if TINYEXR_USE_MINIZ
+#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
//
// Compress the data using miniz
//
@@ -1357,7 +1371,7 @@ static bool CompressZip(unsigned char *dst,
}
compressedSize = outSize;
-#elif TINYEXR_USE_STB_ZLIB
+#elif defined(TINYEXR_USE_STB_ZLIB) && (TINYEXR_USE_STB_ZLIB==1)
int outSize;
unsigned char* ret = stbi_zlib_compress(const_cast<unsigned char*>(&tmpBuf.at(0)), src_size, &outSize, 8);
if (!ret) {
@@ -1367,6 +1381,18 @@ static bool CompressZip(unsigned char *dst,
free(ret);
compressedSize = outSize;
+#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
+ uint64_t dstSize = nanoz_compressBound(static_cast<uint64_t>(src_size));
+ int outSize{0};
+ unsigned char *ret = nanoz_compress(&tmpBuf.at(0), src_size, &outSize, /* quality */8);
+ if (!ret) {
+ return false;
+ }
+
+ memcpy(dst, ret, outSize);
+ free(ret);
+
+ compressedSize = outSize;
#else
uLong outSize = compressBound(static_cast<uLong>(src_size));
int ret = compress(dst, &outSize, static_cast<const Bytef *>(&tmpBuf.at(0)),
@@ -1398,7 +1424,7 @@ static bool DecompressZip(unsigned char *dst,
}
std::vector<unsigned char> tmpBuf(*uncompressed_size);
-#if TINYEXR_USE_MINIZ
+#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
int ret =
mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
if (MZ_OK != ret) {
@@ -1410,6 +1436,17 @@ static bool DecompressZip(unsigned char *dst,
if (ret < 0) {
return false;
}
+#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1)
+ uint64_t dest_size = (*uncompressed_size);
+ uint64_t uncomp_size{0};
+ nanoz_status_t ret =
+ nanoz_uncompress(src, src_size, dest_size, &tmpBuf.at(0), &uncomp_size);
+ if (NANOZ_SUCCESS != ret) {
+ return false;
+ }
+ if ((*uncompressed_size) != uncomp_size) {
+ return false;
+ }
#else
int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size);
if (Z_OK != ret) {
@@ -5715,7 +5752,7 @@ static bool isValidTile(const EXRHeader* exr_header,
static bool ReconstructTileOffsets(OffsetData& offset_data,
const EXRHeader* exr_header,
- const unsigned char* head, const unsigned char* marker, const size_t /*size*/,
+ const unsigned char* head, const unsigned char* marker, const size_t size,
bool isMultiPartFile,
bool isDeep) {
int numXLevels = offset_data.num_x_levels;
@@ -5724,11 +5761,20 @@ static bool ReconstructTileOffsets(OffsetData& offset_data,
for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) {
tinyexr::tinyexr_uint64 tileOffset = tinyexr::tinyexr_uint64(marker - head);
+
if (isMultiPartFile) {
+ if ((marker + sizeof(int)) >= (head + size)) {
+ return false;
+ }
+
//int partNumber;
marker += sizeof(int);
}
+ if ((marker + 4 * sizeof(int)) >= (head + size)) {
+ return false;
+ }
+
int tileX;
memcpy(&tileX, marker, sizeof(int));
tinyexr::swap4(&tileX);
@@ -5750,6 +5796,9 @@ static bool ReconstructTileOffsets(OffsetData& offset_data,
marker += sizeof(int);
if (isDeep) {
+ if ((marker + 2 * sizeof(tinyexr::tinyexr_int64)) >= (head + size)) {
+ return false;
+ }
tinyexr::tinyexr_int64 packed_offset_table_size;
memcpy(&packed_offset_table_size, marker, sizeof(tinyexr::tinyexr_int64));
tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64*>(&packed_offset_table_size));
@@ -5763,13 +5812,26 @@ static bool ReconstructTileOffsets(OffsetData& offset_data,
// next Int64 is unpacked sample size - skip that too
marker += packed_offset_table_size + packed_sample_size + 8;
+ if (marker >= (head + size)) {
+ return false;
+ }
+
} else {
- int dataSize;
- memcpy(&dataSize, marker, sizeof(int));
+ if ((marker + sizeof(uint32_t)) >= (head + size)) {
+ return false;
+ }
+
+ uint32_t dataSize;
+ memcpy(&dataSize, marker, sizeof(uint32_t));
tinyexr::swap4(&dataSize);
- marker += sizeof(int);
+ marker += sizeof(uint32_t);
+
marker += dataSize;
+
+ if (marker >= (head + size)) {
+ return false;
+ }
}
if (!isValidTile(exr_header, offset_data,
@@ -5781,6 +5843,19 @@ static bool ReconstructTileOffsets(OffsetData& offset_data,
if (level_idx < 0) {
return false;
}
+
+ if (size_t(level_idx) >= offset_data.offsets.size()) {
+ return false;
+ }
+
+ if (size_t(tileY) >= offset_data.offsets[size_t(level_idx)].size()) {
+ return false;
+ }
+
+ if (size_t(tileX) >= offset_data.offsets[size_t(level_idx)][size_t(tileY)].size()) {
+ return false;
+ }
+
offset_data.offsets[size_t(level_idx)][size_t(tileY)][size_t(tileX)] = tileOffset;
}
}
@@ -7043,13 +7118,16 @@ static bool EncodePixelData(/* out */ std::vector<unsigned char>& out_data,
} else if ((compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) ||
(compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) {
-#if TINYEXR_USE_MINIZ
+#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1)
std::vector<unsigned char> block(mz_compressBound(
static_cast<unsigned long>(buf.size())));
#elif TINYEXR_USE_STB_ZLIB
// there is no compressBound() function, so we use a value that
// is grossly overestimated, but should always work
std::vector<unsigned char> block(256 + 2 * buf.size());
+#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB == 1)
+ std::vector<unsigned char> block(nanoz_compressBound(
+ static_cast<unsigned long>(buf.size())));
#else
std::vector<unsigned char> block(
compressBound(static_cast<uLong>(buf.size())));