summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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--misc/error_suppressions/tsan.txt1
-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/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
87 files changed, 1231 insertions, 526 deletions
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/misc/error_suppressions/tsan.txt b/misc/error_suppressions/tsan.txt
index a67a22810b..7c3d836f6c 100644
--- a/misc/error_suppressions/tsan.txt
+++ b/misc/error_suppressions/tsan.txt
@@ -4,4 +4,5 @@
deadlock:tests/core/templates/test_command_queue.h
deadlock:modules/text_server_adv/text_server_adv.cpp
deadlock:modules/text_server_fb/text_server_fb.cpp
+race:modules/navigation/nav_map.cpp
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/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"