summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/doc_data.h44
-rw-r--r--core/io/image.cpp10
-rw-r--r--core/io/image.h2
-rw-r--r--core/io/resource_loader.cpp4
-rw-r--r--core/object/message_queue.cpp51
-rw-r--r--core/string/locales.h6
-rw-r--r--doc/classes/Image.xml7
-rw-r--r--doc/classes/NavigationServer2D.xml10
-rw-r--r--doc/classes/NavigationServer3D.xml12
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/RenderingDevice.xml41
-rw-r--r--doc/classes/RenderingServer.xml7
-rw-r--r--doc/classes/Tween.xml2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp7
-rw-r--r--drivers/gles3/storage/render_scene_buffers_gles3.cpp6
-rw-r--r--drivers/gles3/storage/render_scene_buffers_gles3.h2
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp15
-rw-r--r--drivers/vulkan/rendering_device_vulkan.h3
-rw-r--r--editor/code_editor.cpp2
-rw-r--r--editor/editor_help.cpp29
-rw-r--r--editor/editor_node.cpp27
-rw-r--r--editor/import/resource_importer_scene.cpp22
-rw-r--r--editor/plugins/script_text_editor.cpp2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp5
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp1
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp57
-rw-r--r--editor/plugins/tiles/tile_set_editor.h21
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp2
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp27
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h5
-rw-r--r--editor/project_manager.cpp84
-rw-r--r--editor/project_manager.h2
-rw-r--r--misc/extension_api_validation/4.0-stable.expected21
-rw-r--r--modules/dds/image_loader_dds.cpp422
-rw-r--r--modules/dds/image_loader_dds.h (renamed from modules/mono/mono_gd/android_mono_config.h)22
-rw-r--r--modules/dds/register_types.cpp8
-rw-r--r--modules/dds/texture_loader_dds.cpp362
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml1
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp44
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp19
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp14
-rw-r--r--modules/gdscript/gdscript_compiler.cpp336
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp3
-rw-r--r--modules/gdscript/gdscript_editor.cpp123
-rw-r--r--modules/gdscript/gdscript_function.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp172
-rw-r--r--modules/gdscript/gdscript_parser.h57
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp32
-rw-r--r--modules/gdscript/gdscript_vm.cpp74
-rw-r--r--modules/gdscript/gdscript_warning.cpp8
-rw-r--r--modules/gdscript/gdscript_warning.h4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.out7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out15
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.gd17
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out5
-rw-r--r--modules/mono/SCsub2
-rw-r--r--modules/mono/config.py2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs64
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs584
-rw-r--r--modules/mono/godotsharp_dirs.cpp55
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp8
-rw-r--r--modules/mono/mono_gd/support/android_support.cpp720
-rw-r--r--modules/mono/mono_gd/support/android_support.h54
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt24
-rw-r--r--scene/main/node.cpp10
-rw-r--r--scene/resources/placeholder_textures.cpp32
-rw-r--r--scene/resources/placeholder_textures.h8
-rw-r--r--scene/resources/video_stream.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp18
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp96
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h14
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp26
-rw-r--r--servers/rendering/renderer_rd/pipeline_cache_rd.cpp2
-rw-r--r--servers/rendering/renderer_rd/pipeline_cache_rd.h6
-rw-r--r--servers/rendering/renderer_rd/shader_rd.cpp273
-rw-r--r--servers/rendering/renderer_rd/shader_rd.h53
-rw-r--r--servers/rendering/rendering_device.cpp4
-rw-r--r--servers/rendering/rendering_device.h15
-rw-r--r--servers/rendering/rendering_server_default.cpp6
-rw-r--r--servers/rendering/rendering_server_default.h11
-rw-r--r--servers/rendering_server.cpp2
-rw-r--r--servers/rendering_server.h2
-rw-r--r--tests/core/io/test_image.h10
-rw-r--r--tests/data/images/icon.ddsbin0 -> 87536 bytes
94 files changed, 2322 insertions, 2130 deletions
diff --git a/core/doc_data.h b/core/doc_data.h
index 0fe7414b98..b8c92a4b67 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -532,6 +532,42 @@ public:
}
};
+ struct EnumDoc {
+ String description;
+ bool is_deprecated = false;
+ bool is_experimental = false;
+ static EnumDoc from_dict(const Dictionary &p_dict) {
+ EnumDoc doc;
+
+ if (p_dict.has("description")) {
+ doc.description = p_dict["description"];
+ }
+
+ if (p_dict.has("is_deprecated")) {
+ doc.is_deprecated = p_dict["is_deprecated"];
+ }
+
+ if (p_dict.has("is_experimental")) {
+ doc.is_experimental = p_dict["is_experimental"];
+ }
+
+ return doc;
+ }
+ static Dictionary to_dict(const EnumDoc &p_doc) {
+ Dictionary dict;
+
+ if (!p_doc.description.is_empty()) {
+ dict["description"] = p_doc.description;
+ }
+
+ dict["is_deprecated"] = p_doc.is_deprecated;
+
+ dict["is_experimental"] = p_doc.is_experimental;
+
+ return dict;
+ }
+ };
+
struct ClassDoc {
String name;
String inherits;
@@ -543,7 +579,7 @@ public:
Vector<MethodDoc> operators;
Vector<MethodDoc> signals;
Vector<ConstantDoc> constants;
- HashMap<String, String> enums;
+ HashMap<String, EnumDoc> enums;
Vector<PropertyDoc> properties;
Vector<MethodDoc> annotations;
Vector<ThemeItemDoc> theme_properties;
@@ -626,7 +662,7 @@ public:
enums = p_dict["enums"];
}
for (int i = 0; i < enums.size(); i++) {
- doc.enums[enums.get_key_at_index(i)] = enums.get_value_at_index(i);
+ doc.enums[enums.get_key_at_index(i)] = EnumDoc::from_dict(enums.get_value_at_index(i));
}
Array properties;
@@ -740,8 +776,8 @@ public:
if (!p_doc.enums.is_empty()) {
Dictionary enums;
- for (const KeyValue<String, String> &E : p_doc.enums) {
- enums[E.key] = E.value;
+ for (const KeyValue<String, EnumDoc> &E : p_doc.enums) {
+ enums[E.key] = EnumDoc::to_dict(E.value);
}
dict["enums"] = enums;
}
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 7326563f18..a5fea09113 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -3017,6 +3017,7 @@ ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_dds_mem_loader_func = nullptr;
void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
@@ -3488,6 +3489,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer);
ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer);
ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer);
+ ClassDB::bind_method(D_METHOD("load_dds_from_buffer", "buffer"), &Image::load_dds_from_buffer);
ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0));
@@ -3863,6 +3865,14 @@ Error Image::load_svg_from_string(const String &p_svg_str, float scale) {
return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale);
}
+Error Image::load_dds_from_buffer(const Vector<uint8_t> &p_array) {
+ ERR_FAIL_NULL_V_MSG(
+ _dds_mem_loader_func,
+ ERR_UNAVAILABLE,
+ "The DDS module isn't enabled. Recompile the Godot editor or export template binary with the `module_dds_enabled=yes` SCons option.");
+ return _load_from_buffer(p_array, _dds_mem_loader_func);
+}
+
void Image::convert_rg_to_ra_rgba8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
ERR_FAIL_COND(!data.size());
diff --git a/core/io/image.h b/core/io/image.h
index f877b00ee6..f68543ba24 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -150,6 +150,7 @@ public:
static ImageMemLoadFunc _tga_mem_loader_func;
static ImageMemLoadFunc _bmp_mem_loader_func;
static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
+ static ImageMemLoadFunc _dds_mem_loader_func;
static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
@@ -402,6 +403,7 @@ public:
Error load_webp_from_buffer(const Vector<uint8_t> &p_array);
Error load_tga_from_buffer(const Vector<uint8_t> &p_array);
Error load_bmp_from_buffer(const Vector<uint8_t> &p_array);
+ Error load_dds_from_buffer(const Vector<uint8_t> &p_array);
Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0);
Error load_svg_from_string(const String &p_svg_str, float scale = 1.0);
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 1fe662b1fa..df0253349c 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -275,10 +275,10 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
#ifdef TOOLS_ENABLED
Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES);
- ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), "Resource file not found: " + p_path + ".");
+ ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint));
#endif
- ERR_FAIL_V_MSG(Ref<Resource>(), "No loader found for resource: " + p_path + ".");
+ ERR_FAIL_V_MSG(Ref<Resource>(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint));
}
void ResourceLoader::_thread_load_function(void *p_userdata) {
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index dd7aba1384..506f8291eb 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -35,10 +35,6 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
-#ifdef DEBUG_ENABLED
-#include "core/config/engine.h"
-#endif
-
#ifdef DEV_ENABLED
// Includes sanity checks to ensure that a queue set as a thread singleton override
// is only ever called from the thread it was set for.
@@ -320,34 +316,25 @@ Error CallQueue::flush() {
Object *target = message->callable.get_object();
UNLOCK_MUTEX;
-#ifdef DEBUG_ENABLED
- if (!message->callable.is_valid()) {
- // The editor would cause many of these.
- if (!Engine::get_singleton()->is_editor_hint()) {
- ERR_PRINT("Trying to execute a deferred call/notification/set on a previously freed instance. Consider using queue_free() instead of free().");
- }
- } else
-#endif
- {
- switch (message->type & FLAG_MASK) {
- case TYPE_CALL: {
- if (target || (message->type & FLAG_NULL_IS_OK)) {
- Variant *args = (Variant *)(message + 1);
- _call_function(message->callable, args, message->args, message->type & FLAG_SHOW_ERROR);
- }
- } break;
- case TYPE_NOTIFICATION: {
- if (target) {
- target->notification(message->notification);
- }
- } break;
- case TYPE_SET: {
- if (target) {
- Variant *arg = (Variant *)(message + 1);
- target->set(message->callable.get_method(), *arg);
- }
- } break;
- }
+
+ switch (message->type & FLAG_MASK) {
+ case TYPE_CALL: {
+ if (target || (message->type & FLAG_NULL_IS_OK)) {
+ Variant *args = (Variant *)(message + 1);
+ _call_function(message->callable, args, message->args, message->type & FLAG_SHOW_ERROR);
+ }
+ } break;
+ case TYPE_NOTIFICATION: {
+ if (target) {
+ target->notification(message->notification);
+ }
+ } break;
+ case TYPE_SET: {
+ if (target) {
+ Variant *arg = (Variant *)(message + 1);
+ target->set(message->callable.get_method(), *arg);
+ }
+ } break;
}
if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
diff --git a/core/string/locales.h b/core/string/locales.h
index 8a7efb4fd1..840fca65a7 100644
--- a/core/string/locales.h
+++ b/core/string/locales.h
@@ -1057,8 +1057,8 @@ static const char *script_list[][2] = {
{ "Hangul", "Hang" },
{ "Han", "Hani" },
{ "Hanunoo", "Hano" },
- { "Simplified", "Hans" },
- { "Traditional", "Hant" },
+ { "Simplified Han", "Hans" },
+ { "Traditional Han", "Hant" },
{ "Hatran", "Hatr" },
{ "Hebrew", "Hebr" },
{ "Hiragana", "Hira" },
@@ -1110,7 +1110,7 @@ static const char *script_list[][2] = {
{ "Mro", "Mroo" },
{ "Meitei Mayek", "Mtei" },
{ "Multani", "Mult" },
- { "Myanmar (Burmese)", "Mymr" },
+ { "Myanmar / Burmese", "Mymr" },
{ "​Nag Mundari", "Nagm" },
{ "Nandinagari", "Nand" },
{ "Old North Arabian", "Narb" },
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 81954dd7de..1486990995 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -312,6 +312,13 @@
[b]Note:[/b] Godot's BMP module doesn't support 16-bit per pixel images. Only 1-bit, 4-bit, 8-bit, 24-bit, and 32-bit per pixel images are supported.
</description>
</method>
+ <method name="load_dds_from_buffer">
+ <return type="int" enum="Error" />
+ <param index="0" name="buffer" type="PackedByteArray" />
+ <description>
+ Loads an image from the binary contents of a DDS file.
+ </description>
+ </method>
<method name="load_from_file" qualifiers="static">
<return type="Image" />
<param index="0" name="path" type="String" />
diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml
index 32215316d1..a52c9abefd 100644
--- a/doc/classes/NavigationServer2D.xml
+++ b/doc/classes/NavigationServer2D.xml
@@ -66,7 +66,7 @@
<param index="0" name="agent" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] is [code]true[/code] the specified [param agent] uses avoidance.
+ If [param enabled] is [code]true[/code], the specified [param agent] uses avoidance.
</description>
</method>
<method name="agent_set_avoidance_layers">
@@ -283,7 +283,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] is [code]true[/code] the specified [param link] will contribute to its current navigation map.
+ If [param enabled] is [code]true[/code], the specified [param link] will contribute to its current navigation map.
</description>
</method>
<method name="link_set_end_position">
@@ -485,7 +485,7 @@
<param index="0" name="map" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- Set the navigation [param map] edge connection use. If [param enabled] the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ Set the navigation [param map] edge connection use. If [param enabled] is [code]true[/code], the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="obstacle_create">
@@ -520,7 +520,7 @@
<param index="0" name="obstacle" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the provided [param obstacle] affects avoidance using agents.
+ If [param enabled] is [code]true[/code], the provided [param obstacle] affects avoidance using agents.
</description>
</method>
<method name="obstacle_set_avoidance_layers">
@@ -744,7 +744,7 @@
<param index="0" name="region" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ If [param enabled] is [code]true[/code], the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="set_debug_enabled">
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index ebde6fdf7b..86c605b8a3 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -73,7 +73,7 @@
<param index="0" name="agent" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the provided [param agent] calculates avoidance.
+ If [param enabled] is [code]true[/code], the provided [param agent] calculates avoidance.
</description>
</method>
<method name="agent_set_avoidance_layers">
@@ -324,7 +324,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] is [code]true[/code] the specified [param link] will contribute to its current navigation map.
+ If [param enabled] is [code]true[/code], the specified [param link] will contribute to its current navigation map.
</description>
</method>
<method name="link_set_end_position">
@@ -574,7 +574,7 @@
<param index="0" name="map" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- Set the navigation [param map] edge connection use. If [param enabled] the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ Set the navigation [param map] edge connection use. If [param enabled] is [code]true[/code], the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="obstacle_create">
@@ -616,7 +616,7 @@
<param index="0" name="obstacle" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the provided [param obstacle] affects avoidance using agents.
+ If [param enabled] is [code]true[/code], the provided [param obstacle] affects avoidance using agents.
</description>
</method>
<method name="obstacle_set_avoidance_layers">
@@ -813,7 +813,7 @@
<param index="0" name="region" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] is [code]true[/code] the specified [param region] will contribute to its current navigation map.
+ If [param enabled] is [code]true[/code], the specified [param region] will contribute to its current navigation map.
</description>
</method>
<method name="region_set_enter_cost">
@@ -877,7 +877,7 @@
<param index="0" name="region" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ If [param enabled] is [code]true[/code], the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="set_active">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 02f001edc9..d240b6ef48 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -430,6 +430,12 @@
<member name="debug/gdscript/warnings/confusable_identifier" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier contains characters that can be confused with something else, like when mixing different alphabets.
</member>
+ <member name="debug/gdscript/warnings/confusable_local_declaration" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier declared in the nested block has the same name as an identifier declared below in the parent block.
+ </member>
+ <member name="debug/gdscript/warnings/confusable_local_usage" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier that will be shadowed below in the block is used.
+ </member>
<member name="debug/gdscript/warnings/constant_used_as_function" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a constant is used as a function.
</member>
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index 3205a33168..e4e194adf0 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -16,8 +16,8 @@
<methods>
<method name="barrier">
<return type="void" />
- <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
- <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
+ <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
Puts a memory barrier in place. This is used for synchronization to avoid data races. See also [method full_barrier], which may be useful for debugging.
</description>
@@ -27,7 +27,7 @@
<param index="0" name="buffer" type="RID" />
<param index="1" name="offset" type="int" />
<param index="2" name="size_bytes" type="int" />
- <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
</description>
</method>
@@ -46,7 +46,7 @@
<param index="1" name="offset" type="int" />
<param index="2" name="size_bytes" type="int" />
<param index="3" name="data" type="PackedByteArray" />
- <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
</description>
</method>
@@ -114,7 +114,7 @@
</method>
<method name="compute_list_end">
<return type="void" />
- <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
Finishes a list of compute commands created with the [code]compute_*[/code] methods.
</description>
@@ -296,7 +296,7 @@
</method>
<method name="draw_list_end">
<return type="void" />
- <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
Finishes a list of raster drawing commands created with the [code]draw_*[/code] methods.
</description>
@@ -619,6 +619,7 @@
<method name="shader_create_from_bytecode">
<return type="RID" />
<param index="0" name="binary_data" type="PackedByteArray" />
+ <param index="1" name="placeholder_rid" type="RID" default="RID()" />
<description>
Creates a new shader instance from a binary compiled shader. It can be accessed with the RID that is returned.
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. See also [method shader_compile_binary_from_spirv] and [method shader_create_from_spirv].
@@ -633,6 +634,12 @@
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. See also [method shader_compile_spirv_from_source] and [method shader_create_from_bytecode].
</description>
</method>
+ <method name="shader_create_placeholder">
+ <return type="RID" />
+ <description>
+ Create a placeholder RID by allocating an RID without initializing it for use in [method shader_create_from_bytecode]. This allows you to create an RID for a shader and pass it around, but defer compiling the shader to a later time.
+ </description>
+ </method>
<method name="shader_get_vertex_input_attribute_mask">
<return type="int" />
<param index="0" name="shader" type="RID" />
@@ -682,7 +689,7 @@
<param index="3" name="mipmap_count" type="int" />
<param index="4" name="base_layer" type="int" />
<param index="5" name="layer_count" type="int" />
- <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
Clears the specified [param texture] by replacing all of its pixels with the specified [param color]. [param base_mipmap] and [param mipmap_count] determine which mipmaps of the texture are affected by this clear operation, while [param base_layer] and [param layer_count] determine which layers of a 3D texture (or texture array) are affected by this clear operation. For 2D textures (which only have one layer by design), [param base_layer] and [param layer_count] must both be [code]0[/code].
[b]Note:[/b] [param texture] can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to clear this texture.
@@ -699,7 +706,7 @@
<param index="6" name="dst_mipmap" type="int" />
<param index="7" name="src_layer" type="int" />
<param index="8" name="dst_layer" type="int" />
- <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
Copies the [param from_texture] to [param to_texture] with the specified [param from_pos], [param to_pos] and [param size] coordinates. The Z axis of the [param from_pos], [param to_pos] and [param size] must be [code]0[/code] for 2-dimensional textures. Source and destination mipmaps/layers must also be specified, with these parameters being [code]0[/code] for textures without mipmaps or single-layer textures. Returns [constant @GlobalScope.OK] if the texture copy was successful or [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise.
[b]Note:[/b] [param from_texture] texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to copy this texture.
@@ -793,7 +800,7 @@
<return type="int" enum="Error" />
<param index="0" name="from_texture" type="RID" />
<param index="1" name="to_texture" type="RID" />
- <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
Resolves the [param from_texture] texture onto [param to_texture] with multisample antialiasing enabled. This must be used when rendering a framebuffer for MSAA to work. Returns [constant @GlobalScope.OK] if successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise.
[b]Note:[/b] [param from_texture] and [param to_texture] textures must have the same dimension, format and type (color or depth).
@@ -810,7 +817,7 @@
<param index="0" name="texture" type="RID" />
<param index="1" name="layer" type="int" />
<param index="2" name="data" type="PackedByteArray" />
- <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
Updates texture data with new data, replacing the previous data in place. The updated texture data must have the same dimensions and format. For 2D textures (which only have one layer), [param layer] must be [code]0[/code]. Returns [constant @GlobalScope.OK] if the update was successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise.
[b]Note:[/b] Updating textures is forbidden during creation of a draw or compute list.
@@ -1590,22 +1597,22 @@
<constant name="BARRIER_MASK_VERTEX" value="1" enum="BarrierMask" is_bitfield="true">
Vertex shader barrier mask.
</constant>
- <constant name="BARRIER_MASK_FRAGMENT" value="2" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_FRAGMENT" value="8" enum="BarrierMask" is_bitfield="true">
Fragment shader barrier mask.
</constant>
- <constant name="BARRIER_MASK_COMPUTE" value="4" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_COMPUTE" value="2" enum="BarrierMask" is_bitfield="true">
Compute barrier mask.
</constant>
- <constant name="BARRIER_MASK_TRANSFER" value="8" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_TRANSFER" value="4" enum="BarrierMask" is_bitfield="true">
Transfer barrier mask.
</constant>
- <constant name="BARRIER_MASK_RASTER" value="3" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_RASTER" value="9" enum="BarrierMask" is_bitfield="true">
Raster barrier mask (vertex and fragment). Equivalent to [code]BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT[/code].
</constant>
- <constant name="BARRIER_MASK_ALL_BARRIERS" value="15" enum="BarrierMask" is_bitfield="true">
- Barrier mask for all types (raster, compute, transfer). Equivalent to [code]BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER[/code].
+ <constant name="BARRIER_MASK_ALL_BARRIERS" value="32767" enum="BarrierMask" is_bitfield="true">
+ Barrier mask for all types (vertex, fragment, compute, transfer).
</constant>
- <constant name="BARRIER_MASK_NO_BARRIER" value="16" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_NO_BARRIER" value="32768" enum="BarrierMask" is_bitfield="true">
No barrier for any type.
</constant>
<constant name="TEXTURE_TYPE_1D" value="0" enum="TextureType">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 64d54b3ba0..02c1286392 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -27,6 +27,13 @@
Bakes the material data of the Mesh passed in the [param base] parameter with optional [param material_overrides] to a set of [Image]s of size [param image_size]. Returns an array of [Image]s containing material properties as specified in [enum BakeChannels].
</description>
</method>
+ <method name="call_on_render_thread">
+ <return type="void" />
+ <param index="0" name="callable" type="Callable" />
+ <description>
+ As the RenderingServer actual logic may run on an separate thread, accessing its internals from the main (or any other) thread will result in errors. To make it easier to run code that can safely access the rendering internals (such as [RenderingDevice] and similar RD classes), push a callable via this function so it will be executed on the render thread.
+ </description>
+ </method>
<method name="camera_attributes_create">
<return type="RID" />
<description>
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index f104c5f107..7645b249e3 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -366,7 +366,7 @@
[/gdscript]
[csharp]
Tween tween = CreateTween();
- tween.TweenMethod(Callable.From(() =&gt; LookAt(Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // The LookAt() method takes up vector as second argument.
+ tween.TweenMethod(Callable.From((Vector3 target) =&gt; LookAt(target, Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // Use lambdas to bind additional arguments for the call.
[/csharp]
[/codeblocks]
[b]Example:[/b] Setting the text of a [Label], using an intermediate method and after a delay:
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index d8ddfe5c32..1fe33b7914 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1738,7 +1738,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
RenderDataGLES3 render_data;
{
render_data.render_buffers = rb;
- render_data.transparent_bg = rb.is_valid() ? rb->is_transparent : false;
+ render_data.transparent_bg = rb.is_valid() ? rt->is_transparent : false;
// Our first camera is used by default
render_data.cam_transform = p_camera_data->main_transform;
render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
@@ -1984,6 +1984,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
}
if (!keep_color) {
+ clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f;
glClearBufferfv(GL_COLOR, 0, clear_color.components);
}
RENDER_TIMESTAMP("Render Opaque Pass");
@@ -2170,7 +2171,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
if (scene_state.current_depth_draw != shader->depth_draw) {
switch (shader->depth_draw) {
case GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE: {
- glDepthMask(p_pass_mode == PASS_MODE_COLOR);
+ glDepthMask((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) ||
+ p_pass_mode == PASS_MODE_DEPTH ||
+ p_pass_mode == PASS_MODE_SHADOW);
} break;
case GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS: {
glDepthMask(GL_TRUE);
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
index 76c0097bab..829574cae0 100644
--- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp
+++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
@@ -38,8 +38,6 @@ RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
}
void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) {
- GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
-
//internal_size.x = p_config->get_internal_size().x; // ignore for now
//internal_size.y = p_config->get_internal_size().y;
width = p_config->get_target_size().x;
@@ -54,10 +52,6 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p
view_count = p_config->get_view_count();
free_render_buffer_data();
-
- GLES3::RenderTarget *rt = texture_storage->get_render_target(render_target);
-
- is_transparent = rt->is_transparent;
}
void RenderSceneBuffersGLES3::free_render_buffer_data() {
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h
index 64b95c417c..aa98797ed1 100644
--- a/drivers/gles3/storage/render_scene_buffers_gles3.h
+++ b/drivers/gles3/storage/render_scene_buffers_gles3.h
@@ -58,8 +58,6 @@ public:
//bool use_debanding = false;
uint32_t view_count = 1;
- bool is_transparent = false;
-
RID render_target;
//built-in textures used for ping pong image processing and blurring
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index cd18d84837..d521f675fb 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -4858,7 +4858,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve
return ret;
}
-RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary) {
+RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder) {
const uint8_t *binptr = p_shader_binary.ptr();
uint32_t binsize = p_shader_binary.size();
@@ -5184,14 +5184,23 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
ERR_FAIL_V_MSG(RID(), error_text);
}
-
- RID id = shader_owner.make_rid(shader);
+ RID id;
+ if (p_placeholder.is_null()) {
+ id = shader_owner.make_rid(shader);
+ } else {
+ shader_owner.initialize_rid(p_placeholder, shader);
+ id = p_placeholder;
+ }
#ifdef DEV_ENABLED
set_resource_name(id, "RID:" + itos(id.get_id()));
#endif
return id;
}
+RID RenderingDeviceVulkan::shader_create_placeholder() {
+ return shader_owner.allocate_rid();
+}
+
uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) {
_THREAD_SAFE_METHOD_
diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h
index 010f7c9337..fd832312ac 100644
--- a/drivers/vulkan/rendering_device_vulkan.h
+++ b/drivers/vulkan/rendering_device_vulkan.h
@@ -1135,7 +1135,8 @@ public:
virtual String shader_get_binary_cache_key() const;
virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "");
- virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary);
+ virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID());
+ virtual RID shader_create_placeholder();
virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader);
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index cde74e0854..6c5c99698d 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -891,7 +891,7 @@ void CodeTextEditor::_line_col_changed() {
sb.append(itos(positional_column + 1).lpad(3));
sb.append(" | ");
- sb.append(text_editor->is_indent_using_spaces() ? "Spaces" : "Tabs");
+ sb.append(text_editor->is_indent_using_spaces() ? TTR("Spaces", "Indentation") : TTR("Tabs", "Indentation"));
line_and_col_txt->set_text(sb.as_string());
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index c9a95df34e..7573fcd21e 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1059,6 +1059,7 @@ void EditorHelp::_update_doc() {
if (cd.properties[i].is_deprecated) {
DEPRECATED_DOC_TAG;
}
+
if (cd.properties[i].is_experimental) {
EXPERIMENTAL_DOC_TAG;
}
@@ -1303,6 +1304,7 @@ void EditorHelp::_update_doc() {
if (cd.signals[i].is_deprecated) {
DEPRECATED_DOC_TAG;
}
+
if (cd.signals[i].is_experimental) {
EXPERIMENTAL_DOC_TAG;
}
@@ -1363,6 +1365,7 @@ void EditorHelp::_update_doc() {
enum_line[E.key] = class_desc->get_paragraph_count() - 2;
_push_code_font();
+
class_desc->push_color(theme_cache.title_color);
if (E.value.size() && E.value[0].is_bitfield) {
class_desc->add_text("flags ");
@@ -1379,21 +1382,32 @@ void EditorHelp::_update_doc() {
class_desc->push_color(theme_cache.headline_color);
class_desc->add_text(e);
class_desc->pop();
- _pop_code_font();
class_desc->push_color(theme_cache.symbol_color);
class_desc->add_text(":");
class_desc->pop();
+ if (cd.enums.has(e)) {
+ if (cd.enums[e].is_deprecated) {
+ DEPRECATED_DOC_TAG;
+ }
+
+ if (cd.enums[e].is_experimental) {
+ EXPERIMENTAL_DOC_TAG;
+ }
+ }
+
+ _pop_code_font();
+
class_desc->add_newline();
class_desc->add_newline();
// Enum description.
- if (e != "@unnamed_enums" && cd.enums.has(e) && !cd.enums[e].strip_edges().is_empty()) {
+ if (e != "@unnamed_enums" && cd.enums.has(e) && !cd.enums[e].description.strip_edges().is_empty()) {
class_desc->push_color(theme_cache.text_color);
_push_normal_font();
class_desc->push_indent(1);
- _add_text(cd.enums[e]);
+ _add_text(cd.enums[e].description);
class_desc->pop();
_pop_normal_font();
class_desc->pop();
@@ -1417,6 +1431,7 @@ void EditorHelp::_update_doc() {
constant_line[enum_list[i].name] = class_desc->get_paragraph_count() - 2;
_push_code_font();
+
_add_bulletpoint();
class_desc->push_color(theme_cache.headline_color);
_add_text(enum_list[i].name);
@@ -1427,7 +1442,6 @@ void EditorHelp::_update_doc() {
class_desc->push_color(theme_cache.value_color);
_add_text(_fix_constant(enum_list[i].value));
class_desc->pop();
- _pop_code_font();
if (enum_list[i].is_deprecated) {
DEPRECATED_DOC_TAG;
@@ -1437,6 +1451,8 @@ void EditorHelp::_update_doc() {
EXPERIMENTAL_DOC_TAG;
}
+ _pop_code_font();
+
class_desc->add_newline();
if (!enum_list[i].description.strip_edges().is_empty()) {
@@ -1503,8 +1519,6 @@ void EditorHelp::_update_doc() {
_add_text(_fix_constant(constants[i].value));
class_desc->pop();
- _pop_code_font();
-
if (constants[i].is_deprecated) {
DEPRECATED_DOC_TAG;
}
@@ -1513,6 +1527,8 @@ void EditorHelp::_update_doc() {
EXPERIMENTAL_DOC_TAG;
}
+ _pop_code_font();
+
class_desc->add_newline();
if (!constants[i].description.strip_edges().is_empty()) {
@@ -1692,6 +1708,7 @@ void EditorHelp::_update_doc() {
if (cd.properties[i].is_deprecated) {
DEPRECATED_DOC_TAG;
}
+
if (cd.properties[i].is_experimental) {
EXPERIMENTAL_DOC_TAG;
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index a8187c27b6..a58e17724b 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -322,7 +322,7 @@ void EditorNode::_update_scene_tabs() {
scene_tabs->set_current_tab(editor_data.get_edited_scene());
}
- const Size2 add_button_size = Size2(0, scene_tabs->get_size().y);
+ const Size2 add_button_size = Size2(scene_tab_add->get_size().x, scene_tabs->get_size().y);
if (scene_tabs->get_offset_buttons_visible()) {
// Move the add button to a fixed position.
if (scene_tab_add->get_parent() == scene_tabs) {
@@ -345,7 +345,7 @@ void EditorNode::_update_scene_tabs() {
Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1);
int hsep = scene_tabs->get_theme_constant(SNAME("h_separation"));
if (scene_tabs->is_layout_rtl()) {
- scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y), add_button_size));
+ scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - add_button_size.x - hsep, last_tab.position.y), add_button_size));
} else {
scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y), add_button_size));
}
@@ -3620,7 +3620,9 @@ void EditorNode::set_current_scene(int p_idx) {
_update_title();
_update_scene_tabs();
- call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up.
+ if (tabs_to_close.is_empty()) {
+ call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up.
+ }
}
void EditorNode::setup_color_picker(ColorPicker *p_picker) {
@@ -4988,10 +4990,7 @@ void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout) {
p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes", scenes);
String currently_edited_scene_path = editor_data.get_scene_path(editor_data.get_edited_scene());
- // Don't save a bad path to the config.
- if (!currently_edited_scene_path.is_empty()) {
- p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "current_scene", currently_edited_scene_path);
- }
+ p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "current_scene", currently_edited_scene_path);
}
void EditorNode::save_editor_layout_delayed() {
@@ -5400,7 +5399,9 @@ void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout) {
if (p_layout->has_section_key(EDITOR_NODE_CONFIG_SECTION, "current_scene")) {
String current_scene = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "current_scene");
int current_scene_idx = scenes.find(current_scene);
- set_current_scene(current_scene_idx);
+ if (current_scene_idx >= 0) {
+ set_current_scene(current_scene_idx);
+ }
}
save_editor_layout_delayed();
@@ -5679,11 +5680,17 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) {
_scene_tab_closed(scene_tabs->get_hovered_tab());
}
- } else {
- if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
+ } else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
+ int tab_buttons = 0;
+ if (scene_tabs->get_offset_buttons_visible()) {
+ tab_buttons = theme->get_icon(SNAME("increment"), SNAME("TabBar"))->get_width() + theme->get_icon(SNAME("decrement"), SNAME("TabBar"))->get_width();
+ }
+
+ if ((gui_base->is_layout_rtl() && mb->get_position().x > tab_buttons) || (!gui_base->is_layout_rtl() && mb->get_position().x < scene_tabs->get_size().width - tab_buttons)) {
_menu_option_confirm(FILE_NEW_SCENE, true);
}
}
+
if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
// Context menu.
scene_tabs_context_menu->clear();
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 677b2e78bd..48fc20adf0 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1248,6 +1248,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
col->set_owner(p_node->get_owner());
col->set_transform(get_collision_shapes_transform(node_settings));
col->set_position(p_applied_root_scale * col->get_position());
+ const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
+ if (!pmo.is_null()) {
+ col->set_physics_material_override(pmo);
+ }
base = col;
} break;
case MESH_PHYSICS_RIGID_BODY_AND_MESH: {
@@ -1260,6 +1264,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
mi->set_transform(Transform3D());
rigid_body->add_child(mi, true);
mi->set_owner(rigid_body->get_owner());
+ const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
+ if (!pmo.is_null()) {
+ rigid_body->set_physics_material_override(pmo);
+ }
base = rigid_body;
} break;
case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {
@@ -1271,6 +1279,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
p_node->set_owner(nullptr);
memdelete(p_node);
p_node = col;
+ const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
+ if (!pmo.is_null()) {
+ col->set_physics_material_override(pmo);
+ }
base = col;
} break;
case MESH_PHYSICS_AREA_ONLY: {
@@ -1287,6 +1299,9 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
} break;
}
+ base->set_collision_layer(node_settings["physics/layer"]);
+ base->set_collision_mask(node_settings["physics/mask"]);
+
for (const Ref<Shape3D> &E : shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(E);
@@ -1605,6 +1620,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "Static,Dynamic,Area"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/shape_type", PROPERTY_HINT_ENUM, "Decompose Convex,Simple Convex,Trimesh,Box,Sphere,Cylinder,Capsule", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "physics/physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), Variant()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));
// Decomposition
Ref<MeshConvexDecompositionSettings> decomposition_default = Ref<MeshConvexDecompositionSettings>();
@@ -1703,9 +1721,7 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
p_options.has("generate/physics") &&
p_options["generate/physics"].operator bool();
- if (
- p_option == "physics/body_type" ||
- p_option == "physics/shape_type") {
+ if (p_option.find("physics/") >= 0) {
// Show if need to generate collisions.
return generate_physics;
}
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 321c0f34b5..6c28f5f163 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -2361,7 +2361,7 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/indent", TTR("Indent"), Key::NONE);
ED_SHORTCUT("script_text_editor/unindent", TTR("Unindent"), KeyModifierMask::SHIFT | Key::TAB);
- ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KeyModifierMask::CMD_OR_CTRL | Key::K);
+ ED_SHORTCUT_ARRAY("script_text_editor/toggle_comment", TTR("Toggle Comment"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::K), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::SLASH) });
ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KeyModifierMask::ALT | Key::F);
ED_SHORTCUT_OVERRIDE("script_text_editor/toggle_fold_line", "macos", KeyModifierMask::CTRL | KeyModifierMask::META | Key::F);
ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), Key::NONE);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 9c5d55d78f..8b58a45ea5 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -156,7 +156,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
// Common to all type of sources.
if (!source->get_name().is_empty()) {
- item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id);
+ item_text = source->get_name();
}
// Atlas source.
@@ -165,7 +165,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
texture = atlas_source->get_texture();
if (item_text.is_empty()) {
if (texture.is_valid()) {
- item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
+ item_text = texture->get_path().get_file();
} else {
item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
}
@@ -2234,6 +2234,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_source_display).unbind(1));
sources_list->connect("item_selected", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_sources_lists_current));
+ sources_list->connect("item_activated", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::display_tile_set_editor_panel).unbind(1));
sources_list->connect("visibility_changed", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::synchronize_sources_list).bind(sources_list, source_sort_button));
sources_list->add_user_signal(MethodInfo("sort_request"));
sources_list->connect("sort_request", callable_mp(this, &TileMapEditorTilesPlugin::_update_tile_set_sources_list));
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 61d57e5eab..c98d9086d1 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2529,6 +2529,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
atlas_source_inspector = memnew(EditorInspector);
atlas_source_inspector->set_v_size_flags(SIZE_EXPAND_FILL);
atlas_source_inspector->set_show_categories(true);
+ atlas_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
atlas_source_inspector->edit(atlas_source_proxy_object);
middle_vbox_container->add_child(atlas_source_inspector);
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index eb79394cc9..f620e434ab 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -34,6 +34,7 @@
#include "tiles_editor_plugin.h"
#include "editor/editor_file_system.h"
+#include "editor/editor_inspector.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
@@ -42,6 +43,7 @@
#include "scene/gui/box_container.h"
#include "scene/gui/control.h"
+#include "scene/gui/dialogs.h"
#include "scene/gui/tab_container.h"
TileSetEditor *TileSetEditor::singleton = nullptr;
@@ -158,7 +160,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
// Common to all type of sources.
if (!source->get_name().is_empty()) {
- item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id);
+ item_text = source->get_name();
}
// Atlas source.
@@ -167,7 +169,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
texture = atlas_source->get_texture();
if (item_text.is_empty()) {
if (texture.is_valid()) {
- item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
+ item_text = texture->get_path().get_file();
} else {
item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
}
@@ -964,3 +966,54 @@ TileSetEditor::TileSetEditor() {
EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element));
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
}
+
+void TileSourceInspectorPlugin::_show_id_edit_dialog(Object *p_for_source) {
+ if (!id_edit_dialog) {
+ id_edit_dialog = memnew(ConfirmationDialog);
+ TileSetEditor::get_singleton()->add_child(id_edit_dialog);
+
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ id_edit_dialog->add_child(vbox);
+
+ Label *label = memnew(Label(TTR("Warning: Modifying a source ID will result in all TileMaps using that source to reference an invalid source instead. This may result in unexpected data loss. Change this ID carefully.")));
+ label->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
+ vbox->add_child(label);
+
+ id_input = memnew(SpinBox);
+ vbox->add_child(id_input);
+ id_input->set_max(INT_MAX);
+
+ id_edit_dialog->connect("confirmed", callable_mp(this, &TileSourceInspectorPlugin::_confirm_change_id));
+ }
+ edited_source = p_for_source;
+ id_input->set_value(p_for_source->get("id"));
+ id_edit_dialog->popup_centered(Vector2i(400, 0) * EDSCALE);
+ callable_mp((Control *)id_input->get_line_edit(), &Control::grab_focus).call_deferred();
+}
+
+void TileSourceInspectorPlugin::_confirm_change_id() {
+ edited_source->set("id", id_input->get_value());
+ id_label->set_text(vformat(TTR("ID: %d"), edited_source->get("id"))); // Use get(), because the provided ID might've been invalid.
+}
+
+bool TileSourceInspectorPlugin::can_handle(Object *p_object) {
+ return p_object->is_class("TileSetAtlasSourceProxyObject") || p_object->is_class("TileSetScenesCollectionProxyObject");
+}
+
+bool TileSourceInspectorPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
+ if (p_path == "id") {
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ hbox->set_alignment(BoxContainer::ALIGNMENT_CENTER);
+
+ id_label = memnew(Label(vformat(TTR("ID: %d"), p_object->get("id"))));
+ hbox->add_child(id_label);
+
+ Button *button = memnew(Button(TTR("Edit")));
+ hbox->add_child(button);
+ button->connect("pressed", callable_mp(this, &TileSourceInspectorPlugin::_show_id_edit_dialog).bind(p_object));
+
+ add_custom_control(hbox);
+ return true;
+ }
+ return false;
+}
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index 40ca1f7ed7..86cd70d19e 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -38,9 +38,12 @@
#include "tile_set_atlas_source_editor.h"
#include "tile_set_scenes_collection_source_editor.h"
-class EditorFileDialog;
+class AcceptDialog;
+class SpinBox;
class HBoxContainer;
class SplitContainer;
+class EditorFileDialog;
+class EditorInspectorPlugin;
class TileSetEditor : public Control {
GDCLASS(TileSetEditor, Control);
@@ -123,4 +126,20 @@ public:
TileSetEditor();
};
+class TileSourceInspectorPlugin : public EditorInspectorPlugin {
+ GDCLASS(TileSourceInspectorPlugin, EditorInspectorPlugin);
+
+ AcceptDialog *id_edit_dialog = nullptr;
+ Label *id_label = nullptr;
+ SpinBox *id_input = nullptr;
+ Object *edited_source = nullptr;
+
+ void _show_id_edit_dialog(Object *p_for_source);
+ void _confirm_change_id();
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
+};
+
#endif // TILE_SET_EDITOR_H
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
index 5ffa7b4bd4..13270f3821 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/plugins/tiles/tile_set_editor.h"
#include "scene/gui/button.h"
#include "scene/gui/item_list.h"
@@ -504,6 +505,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
scenes_collection_source_inspector = memnew(EditorInspector);
scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+ scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
middle_vbox_container->add_child(scenes_collection_source_inspector);
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 4238002b72..911316822a 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -50,7 +50,8 @@
#include "scene/resources/tile_set.h"
TilesEditorUtils *TilesEditorUtils::singleton = nullptr;
-TileMapEditorPlugin *local_singleton = nullptr;
+TileMapEditorPlugin *tile_map_plugin_singleton = nullptr;
+TileSetEditorPlugin *tile_set_plugin_singleton = nullptr;
void TilesEditorUtils::_preview_frame_started() {
RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<TilesEditorUtils *>(this), &TilesEditorUtils::_pattern_preview_done));
@@ -283,6 +284,11 @@ bool TilesEditorUtils::SourceNameComparator::operator()(const int &p_a, const in
return NaturalNoCaseComparator()(name_a, name_b);
}
+void TilesEditorUtils::display_tile_set_editor_panel() {
+ tile_map_plugin_singleton->hide_editor();
+ tile_set_plugin_singleton->make_visible(true);
+}
+
void TilesEditorUtils::draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color) {
real_t scale = p_ci->get_global_transform().get_scale().x * 0.5;
p_ci->draw_set_transform(p_rect.position, 0, Vector2(1, 1) / scale);
@@ -373,13 +379,19 @@ void TileMapEditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay)
editor->forward_canvas_draw_over_viewport(p_overlay);
}
+void TileMapEditorPlugin::hide_editor() {
+ if (editor->is_visible_in_tree()) {
+ EditorNode::get_singleton()->hide_bottom_panel();
+ }
+}
+
bool TileMapEditorPlugin::is_editor_visible() const {
return editor->is_visible_in_tree();
}
TileMapEditorPlugin::TileMapEditorPlugin() {
memnew(TilesEditorUtils);
- local_singleton = this;
+ tile_map_plugin_singleton = this;
editor = memnew(TileMapEditor);
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -392,7 +404,7 @@ TileMapEditorPlugin::TileMapEditorPlugin() {
}
TileMapEditorPlugin::~TileMapEditorPlugin() {
- local_singleton = nullptr;
+ tile_map_plugin_singleton = nullptr;
}
void TileSetEditorPlugin::edit(Object *p_object) {
@@ -406,7 +418,7 @@ bool TileSetEditorPlugin::handles(Object *p_object) const {
void TileSetEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
- if (!local_singleton->is_editor_visible()) {
+ if (!tile_map_plugin_singleton->is_editor_visible()) {
EditorNode::get_singleton()->make_bottom_panel_item_visible(editor);
}
} else {
@@ -418,7 +430,8 @@ void TileSetEditorPlugin::make_visible(bool p_visible) {
}
TileSetEditorPlugin::TileSetEditorPlugin() {
- DEV_ASSERT(local_singleton);
+ DEV_ASSERT(tile_map_plugin_singleton);
+ tile_set_plugin_singleton = this;
editor = memnew(TileSetEditor);
editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -429,3 +442,7 @@ TileSetEditorPlugin::TileSetEditorPlugin() {
button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileSet"), editor);
button->hide();
}
+
+TileSetEditorPlugin::~TileSetEditorPlugin() {
+ tile_set_plugin_singleton = nullptr;
+}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index 73be9798bd..0bb45b746d 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -101,6 +101,9 @@ public:
void set_sorting_option(int p_option);
List<int> get_sorted_sources(const Ref<TileSet> p_tile_set) const;
+ // Misc.
+ void display_tile_set_editor_panel();
+
static void draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color = Color(1.0, 1.0, 1.0));
TilesEditorUtils();
@@ -129,6 +132,7 @@ public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
+ void hide_editor();
bool is_editor_visible() const;
TileMapEditorPlugin();
@@ -147,6 +151,7 @@ public:
virtual void make_visible(bool p_visible) override;
TileSetEditorPlugin();
+ ~TileSetEditorPlugin();
};
#endif // TILES_EDITOR_PLUGIN_H
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 691adcdb7a..7d2950f6f6 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1977,10 +1977,10 @@ void ProjectManager::_notification(int p_what) {
real_t size = get_size().x / EDSCALE;
// Adjust names of tabs to fit the new size.
if (size < 650) {
- local_projects_hb->set_name(TTR("Local"));
+ local_projects_vb->set_name(TTR("Local"));
asset_library->set_name(TTR("Asset Library"));
} else {
- local_projects_hb->set_name(TTR("Local Projects"));
+ local_projects_vb->set_name(TTR("Local Projects"));
asset_library->set_name(TTR("Asset Library Projects"));
}
}
@@ -2845,19 +2845,39 @@ ProjectManager::ProjectManager() {
tabs->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
tabs->connect("tab_changed", callable_mp(this, &ProjectManager::_on_tab_changed));
- local_projects_hb = memnew(HBoxContainer);
- local_projects_hb->set_name(TTR("Local Projects"));
- tabs->add_child(local_projects_hb);
+ local_projects_vb = memnew(VBoxContainer);
+ local_projects_vb->set_name(TTR("Local Projects"));
+ tabs->add_child(local_projects_vb);
{
- // Projects + search bar
- VBoxContainer *search_tree_vb = memnew(VBoxContainer);
- local_projects_hb->add_child(search_tree_vb);
- search_tree_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-
+ // A bar at top with buttons and options.
HBoxContainer *hb = memnew(HBoxContainer);
hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- search_tree_vb->add_child(hb);
+ local_projects_vb->add_child(hb);
+
+ create_btn = memnew(Button);
+ create_btn->set_text(TTR("New"));
+ create_btn->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KeyModifierMask::CMD_OR_CTRL | Key::N));
+ create_btn->connect("pressed", callable_mp(this, &ProjectManager::_new_project));
+ hb->add_child(create_btn);
+
+ import_btn = memnew(Button);
+ import_btn->set_text(TTR("Import"));
+ import_btn->set_shortcut(ED_SHORTCUT("project_manager/import_project", TTR("Import Project"), KeyModifierMask::CMD_OR_CTRL | Key::I));
+ import_btn->connect("pressed", callable_mp(this, &ProjectManager::_import_project));
+ hb->add_child(import_btn);
+
+ scan_btn = memnew(Button);
+ scan_btn->set_text(TTR("Scan"));
+ scan_btn->set_shortcut(ED_SHORTCUT("project_manager/scan_projects", TTR("Scan Projects"), KeyModifierMask::CMD_OR_CTRL | Key::S));
+ scan_btn->connect("pressed", callable_mp(this, &ProjectManager::_scan_projects));
+ hb->add_child(scan_btn);
+
+ loading_label = memnew(Label(TTR("Loading, please wait...")));
+ loading_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hb->add_child(loading_label);
+ // The loading label is shown later.
+ loading_label->hide();
search_box = memnew(LineEdit);
search_box->set_placeholder(TTR("Filter Projects"));
@@ -2867,12 +2887,6 @@ ProjectManager::ProjectManager() {
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hb->add_child(search_box);
- loading_label = memnew(Label(TTR("Loading, please wait...")));
- loading_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- hb->add_child(loading_label);
- // The loading label is shown later.
- loading_label->hide();
-
Label *sort_label = memnew(Label);
sort_label->set_text(TTR("Sort:"));
hb->add_child(sort_label);
@@ -2880,6 +2894,7 @@ ProjectManager::ProjectManager() {
filter_option = memnew(OptionButton);
filter_option->set_clip_text(true);
filter_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ filter_option->set_stretch_ratio(0.3);
filter_option->connect("item_selected", callable_mp(this, &ProjectManager::_on_order_option_changed));
hb->add_child(filter_option);
@@ -2892,41 +2907,28 @@ ProjectManager::ProjectManager() {
for (int i = 0; i < sort_filter_titles.size(); i++) {
filter_option->add_item(sort_filter_titles[i]);
}
+ }
+
+ {
+ // A container for the project list and for the side bar with buttons.
+ HBoxContainer *search_tree_hb = memnew(HBoxContainer);
+ local_projects_vb->add_child(search_tree_hb);
+ search_tree_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
search_panel = memnew(PanelContainer);
- search_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- search_tree_vb->add_child(search_panel);
+ search_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search_tree_hb->add_child(search_panel);
_project_list = memnew(ProjectList);
_project_list->connect(ProjectList::SIGNAL_SELECTION_CHANGED, callable_mp(this, &ProjectManager::_update_project_buttons));
_project_list->connect(ProjectList::SIGNAL_PROJECT_ASK_OPEN, callable_mp(this, &ProjectManager::_open_selected_projects_ask));
_project_list->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
search_panel->add_child(_project_list);
- }
- {
- // Project tab side bar
+ // The side bar with the edit, run, rename, etc. buttons.
VBoxContainer *tree_vb = memnew(VBoxContainer);
tree_vb->set_custom_minimum_size(Size2(120, 120));
- local_projects_hb->add_child(tree_vb);
-
- create_btn = memnew(Button);
- create_btn->set_text(TTR("New Project"));
- create_btn->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KeyModifierMask::CMD_OR_CTRL | Key::N));
- create_btn->connect("pressed", callable_mp(this, &ProjectManager::_new_project));
- tree_vb->add_child(create_btn);
-
- import_btn = memnew(Button);
- import_btn->set_text(TTR("Import"));
- import_btn->set_shortcut(ED_SHORTCUT("project_manager/import_project", TTR("Import Project"), KeyModifierMask::CMD_OR_CTRL | Key::I));
- import_btn->connect("pressed", callable_mp(this, &ProjectManager::_import_project));
- tree_vb->add_child(import_btn);
-
- scan_btn = memnew(Button);
- scan_btn->set_text(TTR("Scan"));
- scan_btn->set_shortcut(ED_SHORTCUT("project_manager/scan_projects", TTR("Scan Projects"), KeyModifierMask::CMD_OR_CTRL | Key::S));
- scan_btn->connect("pressed", callable_mp(this, &ProjectManager::_scan_projects));
- tree_vb->add_child(scan_btn);
+ search_tree_hb->add_child(tree_vb);
tree_vb->add_child(memnew(HSeparator));
diff --git a/editor/project_manager.h b/editor/project_manager.h
index 043178fb6f..46c465f24d 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -349,7 +349,7 @@ class ProjectManager : public Control {
Button *erase_missing_btn = nullptr;
Button *about_btn = nullptr;
- HBoxContainer *local_projects_hb = nullptr;
+ VBoxContainer *local_projects_vb = nullptr;
EditorAssetLibrary *asset_library = nullptr;
Ref<StyleBox> tag_stylebox;
diff --git a/misc/extension_api_validation/4.0-stable.expected b/misc/extension_api_validation/4.0-stable.expected
index c9ebd83ba3..380c1d5193 100644
--- a/misc/extension_api_validation/4.0-stable.expected
+++ b/misc/extension_api_validation/4.0-stable.expected
@@ -358,3 +358,24 @@ Validate extension JSON: Error: Hash changed for 'classes/EditorUndoRedoManager/
Validate extension JSON: Error: Hash changed for 'classes/UndoRedo/methods/create_action', from 0AEC1BFC to E87757EB. This means that the function has changed and no compatibility function was provided.
Added a optional parameters with default values. No adjustments should be necessary.
+
+GH-79911
+--------
+Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_RASTER': value changed value in new API, from 1.0 to 9.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_ALL_BARRIERS': value changed value in new API, from 7.0 to 32767.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_NO_BARRIER': value changed value in new API, from 8.0 to 32768.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_update/arguments/3': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_copy/arguments/9': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_clear/arguments/6': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_resolve_multisample/arguments/2': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/buffer_update/arguments/4': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/buffer_clear/arguments/3': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_end/arguments/0': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/draw_list_end', from 19365687 to E9B4FA8E. This means that the function has changed and no compatibility function was provided.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/compute_list_end/arguments/0': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/compute_list_end', from 19365687 to E9B4FA8E. This means that the function has changed and no compatibility function was provided.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/arguments/0': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/arguments/1': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/barrier', from 0FE50041 to DD9E8DAB. This means that the function has changed and no compatibility function was provided.
+
+Raster barrier was split into vertex and fragment barriers for use in mobile renderer.
diff --git a/modules/dds/image_loader_dds.cpp b/modules/dds/image_loader_dds.cpp
new file mode 100644
index 0000000000..42c8120595
--- /dev/null
+++ b/modules/dds/image_loader_dds.cpp
@@ -0,0 +1,422 @@
+/**************************************************************************/
+/* image_loader_dds.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "image_loader_dds.h"
+
+#include "core/os/os.h"
+
+#include "core/io/file_access.h"
+#include "core/io/file_access_memory.h"
+
+#include <string.h>
+
+#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
+
+// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
+
+enum {
+ DDS_MAGIC = 0x20534444,
+ DDSD_PITCH = 0x00000008,
+ DDSD_LINEARSIZE = 0x00080000,
+ DDSD_MIPMAPCOUNT = 0x00020000,
+ DDPF_FOURCC = 0x00000004,
+ DDPF_ALPHAPIXELS = 0x00000001,
+ DDPF_INDEXED = 0x00000020,
+ DDPF_RGB = 0x00000040,
+};
+
+enum DDSFormat {
+ DDS_DXT1,
+ DDS_DXT3,
+ DDS_DXT5,
+ DDS_ATI1,
+ DDS_ATI2,
+ DDS_A2XY,
+ DDS_BGRA8,
+ DDS_BGR8,
+ DDS_RGBA8, //flipped in dds
+ DDS_RGB8, //flipped in dds
+ DDS_BGR5A1,
+ DDS_BGR565,
+ DDS_BGR10A2,
+ DDS_INDEXED,
+ DDS_LUMINANCE,
+ DDS_LUMINANCE_ALPHA,
+ DDS_MAX
+};
+
+struct DDSFormatInfo {
+ const char *name = nullptr;
+ bool compressed = false;
+ bool palette = false;
+ uint32_t divisor = 0;
+ uint32_t block_size = 0;
+ Image::Format format = Image::Format::FORMAT_BPTC_RGBA;
+};
+
+static const DDSFormatInfo dds_format_info[DDS_MAX] = {
+ { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 },
+ { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 },
+ { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 },
+ { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R },
+ { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
+ { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
+ { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
+ { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 },
+ { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
+ { "RGB8", false, false, 1, 3, Image::FORMAT_RGB8 },
+ { "BGR5A1", false, false, 1, 2, Image::FORMAT_RGBA8 },
+ { "BGR565", false, false, 1, 2, Image::FORMAT_RGB8 },
+ { "BGR10A2", false, false, 1, 4, Image::FORMAT_RGBA8 },
+ { "GRAYSCALE", false, false, 1, 1, Image::FORMAT_L8 },
+ { "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 }
+};
+
+static Ref<Image> _dds_mem_loader_func(const uint8_t *p_buffer, int p_buffer_len) {
+ Ref<FileAccessMemory> memfile;
+ memfile.instantiate();
+ Error open_memfile_error = memfile->open_custom(p_buffer, p_buffer_len);
+ ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for DDS image buffer.");
+
+ Ref<Image> img;
+ img.instantiate();
+ Error load_error = ImageLoaderDDS().load_image(img, memfile, false, 1.0f);
+ ERR_FAIL_COND_V_MSG(load_error, Ref<Image>(), "Failed to load DDS image.");
+ return img;
+}
+
+Error ImageLoaderDDS::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
+ uint32_t magic = f->get_32();
+ uint32_t hsize = f->get_32();
+ uint32_t flags = f->get_32();
+ uint32_t height = f->get_32();
+ uint32_t width = f->get_32();
+ uint32_t pitch = f->get_32();
+ /* uint32_t depth = */ f->get_32();
+ uint32_t mipmaps = f->get_32();
+
+ //skip 11
+ for (int i = 0; i < 11; i++) {
+ f->get_32();
+ }
+
+ //validate
+
+ // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
+ // but non-mandatory when reading (as some writers don't set them)...
+ if (magic != DDS_MAGIC || hsize != 124) {
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid or unsupported DDS texture file '" + f->get_path() + "'.");
+ }
+
+ /* uint32_t format_size = */ f->get_32();
+ uint32_t format_flags = f->get_32();
+ uint32_t format_fourcc = f->get_32();
+ uint32_t format_rgb_bits = f->get_32();
+ uint32_t format_red_mask = f->get_32();
+ uint32_t format_green_mask = f->get_32();
+ uint32_t format_blue_mask = f->get_32();
+ uint32_t format_alpha_mask = f->get_32();
+
+ /* uint32_t caps_1 = */ f->get_32();
+ /* uint32_t caps_2 = */ f->get_32();
+ /* uint32_t caps_ddsx = */ f->get_32();
+
+ //reserved skip
+ f->get_32();
+ f->get_32();
+
+ /*
+ print_line("DDS width: "+itos(width));
+ print_line("DDS height: "+itos(height));
+ print_line("DDS mipmaps: "+itos(mipmaps));
+
+ printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size);
+ printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask);
+ */
+
+ //must avoid this later
+ while (f->get_position() < 128) {
+ f->get_8();
+ }
+
+ DDSFormat dds_format;
+
+ if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) {
+ dds_format = DDS_DXT1;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) {
+ dds_format = DDS_DXT3;
+
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) {
+ dds_format = DDS_DXT5;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) {
+ dds_format = DDS_ATI1;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) {
+ dds_format = DDS_ATI2;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) {
+ dds_format = DDS_A2XY;
+
+ } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) {
+ dds_format = DDS_BGRA8;
+ } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) {
+ dds_format = DDS_BGR8;
+ } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) {
+ dds_format = DDS_RGBA8;
+ } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) {
+ dds_format = DDS_RGB8;
+
+ } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) {
+ dds_format = DDS_BGR5A1;
+ } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) {
+ dds_format = DDS_BGR10A2;
+ } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) {
+ dds_format = DDS_BGR565;
+ } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff) {
+ dds_format = DDS_LUMINANCE;
+ } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff && format_alpha_mask == 0xff00) {
+ dds_format = DDS_LUMINANCE_ALPHA;
+ } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) {
+ dds_format = DDS_BGR565;
+ } else {
+ //printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask);
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Unrecognized or unsupported color layout in DDS '" + f->get_path() + "'.");
+ }
+
+ if (!(flags & DDSD_MIPMAPCOUNT)) {
+ mipmaps = 1;
+ }
+
+ Vector<uint8_t> src_data;
+
+ const DDSFormatInfo &info = dds_format_info[dds_format];
+ uint32_t w = width;
+ uint32_t h = height;
+
+ if (info.compressed) {
+ //compressed bc
+
+ uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
+ ERR_FAIL_COND_V(size != pitch, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), ERR_FILE_CORRUPT);
+
+ for (uint32_t i = 1; i < mipmaps; i++) {
+ w = MAX(1u, w >> 1);
+ h = MAX(1u, h >> 1);
+ uint32_t bsize = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
+ //printf("%i x %i - block: %i\n",w,h,bsize);
+ size += bsize;
+ }
+
+ src_data.resize(size);
+ uint8_t *wb = src_data.ptrw();
+ f->get_buffer(wb, size);
+
+ } else if (info.palette) {
+ //indexed
+ ERR_FAIL_COND_V(!(flags & DDSD_PITCH), ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(format_rgb_bits != 8, ERR_FILE_CORRUPT);
+
+ uint32_t size = pitch * height;
+ ERR_FAIL_COND_V(size != width * height * info.block_size, ERR_FILE_CORRUPT);
+
+ uint8_t palette[256 * 4];
+ f->get_buffer(palette, 256 * 4);
+
+ int colsize = 3;
+ for (int i = 0; i < 256; i++) {
+ if (palette[i * 4 + 3] < 255) {
+ colsize = 4;
+ }
+ }
+
+ int w2 = width;
+ int h2 = height;
+
+ for (uint32_t i = 1; i < mipmaps; i++) {
+ w2 = (w2 + 1) >> 1;
+ h2 = (h2 + 1) >> 1;
+ size += w2 * h2 * info.block_size;
+ }
+
+ src_data.resize(size + 256 * colsize);
+ uint8_t *wb = src_data.ptrw();
+ f->get_buffer(wb, size);
+
+ for (int i = 0; i < 256; i++) {
+ int dst_ofs = size + i * colsize;
+ int src_ofs = i * 4;
+ wb[dst_ofs + 0] = palette[src_ofs + 2];
+ wb[dst_ofs + 1] = palette[src_ofs + 1];
+ wb[dst_ofs + 2] = palette[src_ofs + 0];
+ if (colsize == 4) {
+ wb[dst_ofs + 3] = palette[src_ofs + 3];
+ }
+ }
+ } else {
+ //uncompressed generic...
+
+ uint32_t size = width * height * info.block_size;
+
+ for (uint32_t i = 1; i < mipmaps; i++) {
+ w = (w + 1) >> 1;
+ h = (h + 1) >> 1;
+ size += w * h * info.block_size;
+ }
+
+ if (dds_format == DDS_BGR565) {
+ size = size * 3 / 2;
+ } else if (dds_format == DDS_BGR5A1) {
+ size = size * 2;
+ }
+
+ src_data.resize(size);
+ uint8_t *wb = src_data.ptrw();
+ f->get_buffer(wb, size);
+
+ switch (dds_format) {
+ case DDS_BGR5A1: {
+ // TO RGBA
+ int colcount = size / 4;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int src_ofs = i * 2;
+ int dst_ofs = i * 4;
+
+ uint8_t a = wb[src_ofs + 1] & 0x80;
+ uint8_t b = wb[src_ofs] & 0x1F;
+ uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x3) << 3);
+ uint8_t r = (wb[src_ofs + 1] >> 2) & 0x1F;
+ wb[dst_ofs + 0] = r << 3;
+ wb[dst_ofs + 1] = g << 3;
+ wb[dst_ofs + 2] = b << 3;
+ wb[dst_ofs + 3] = a ? 255 : 0;
+ }
+ } break;
+ case DDS_BGR565: {
+ int colcount = size / 3;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int src_ofs = i * 2;
+ int dst_ofs = i * 3;
+
+ uint8_t b = wb[src_ofs] & 0x1F;
+ uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x7) << 3);
+ uint8_t r = wb[src_ofs + 1] >> 3;
+ wb[dst_ofs + 0] = r << 3;
+ wb[dst_ofs + 1] = g << 2;
+ wb[dst_ofs + 2] = b << 3; //b<<3;
+ }
+
+ } break;
+ case DDS_BGR10A2: {
+ // TO RGBA
+ int colcount = size / 4;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int ofs = i * 4;
+
+ uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24);
+
+ uint8_t a = (w32 & 0xc0000000) >> 24;
+ uint8_t r = (w32 & 0x3ff00000) >> 22;
+ uint8_t g = (w32 & 0xffc00) >> 12;
+ uint8_t b = (w32 & 0x3ff) >> 2;
+
+ wb[ofs + 0] = r;
+ wb[ofs + 1] = g;
+ wb[ofs + 2] = b;
+ wb[ofs + 3] = a == 0xc0 ? 255 : a; //0xc0 should be opaque
+ }
+ } break;
+ case DDS_BGRA8: {
+ int colcount = size / 4;
+
+ for (int i = 0; i < colcount; i++) {
+ SWAP(wb[i * 4 + 0], wb[i * 4 + 2]);
+ }
+
+ } break;
+ case DDS_BGR8: {
+ int colcount = size / 3;
+
+ for (int i = 0; i < colcount; i++) {
+ SWAP(wb[i * 3 + 0], wb[i * 3 + 2]);
+ }
+ } break;
+ case DDS_RGBA8: {
+ /* do nothing either
+ int colcount = size/4;
+
+ for(int i=0;i<colcount;i++) {
+ uint8_t r = wb[i*4+1];
+ uint8_t g = wb[i*4+2];
+ uint8_t b = wb[i*4+3];
+ uint8_t a = wb[i*4+0];
+
+ wb[i*4+0]=r;
+ wb[i*4+1]=g;
+ wb[i*4+2]=b;
+ wb[i*4+3]=a;
+ }
+ */
+ } break;
+ case DDS_RGB8: {
+ // do nothing
+ /*
+ int colcount = size/3;
+
+ for(int i=0;i<colcount;i++) {
+ SWAP( wb[i*3+0],wb[i*3+2] );
+ }*/
+ } break;
+ case DDS_LUMINANCE: {
+ // do nothing i guess?
+
+ } break;
+ case DDS_LUMINANCE_ALPHA: {
+ // do nothing i guess?
+
+ } break;
+
+ default: {
+ }
+ }
+ }
+
+ p_image->set_data(width, height, mipmaps - 1, info.format, src_data);
+ return OK;
+}
+
+ImageLoaderDDS::ImageLoaderDDS() {
+ Image::_dds_mem_loader_func = _dds_mem_loader_func;
+}
+
+void ImageLoaderDDS::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("dds");
+}
diff --git a/modules/mono/mono_gd/android_mono_config.h b/modules/dds/image_loader_dds.h
index bb9c2a5d5b..81cfd43551 100644
--- a/modules/mono/mono_gd/android_mono_config.h
+++ b/modules/dds/image_loader_dds.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* android_mono_config.h */
+/* image_loader_dds.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,16 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef ANDROID_MONO_CONFIG_H
-#define ANDROID_MONO_CONFIG_H
+#ifndef IMAGE_LOADER_DDS_H
+#define IMAGE_LOADER_DDS_H
-#ifdef ANDROID_ENABLED
+#include "core/io/image_loader.h"
-#include "core/string/ustring.h"
+class ImageLoaderDDS : public ImageFormatLoader {
+public:
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ ImageLoaderDDS();
+};
-// This function is defined in an auto-generated source file
-String get_godot_android_mono_config();
-
-#endif // ANDROID_ENABLED
-
-#endif // ANDROID_MONO_CONFIG_H
+#endif // IMAGE_LOADER_DDS_H
diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp
index d336269eb3..b4d406eda9 100644
--- a/modules/dds/register_types.cpp
+++ b/modules/dds/register_types.cpp
@@ -30,9 +30,11 @@
#include "register_types.h"
+#include "image_loader_dds.h"
#include "texture_loader_dds.h"
static Ref<ResourceFormatDDS> resource_loader_dds;
+static Ref<ImageLoaderDDS> image_loader_dds;
void initialize_dds_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
@@ -41,6 +43,9 @@ void initialize_dds_module(ModuleInitializationLevel p_level) {
resource_loader_dds.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_dds);
+
+ image_loader_dds.instantiate();
+ ImageLoader::add_image_format_loader(image_loader_dds);
}
void uninitialize_dds_module(ModuleInitializationLevel p_level) {
@@ -50,4 +55,7 @@ void uninitialize_dds_module(ModuleInitializationLevel p_level) {
ResourceLoader::remove_resource_format_loader(resource_loader_dds);
resource_loader_dds.unref();
+
+ ImageLoader::remove_image_format_loader(image_loader_dds);
+ image_loader_dds.unref();
}
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index 8a3a36e84b..861cf20cc2 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -29,72 +29,11 @@
/**************************************************************************/
#include "texture_loader_dds.h"
+#include "image_loader_dds.h"
#include "core/io/file_access.h"
#include "scene/resources/image_texture.h"
-#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
-
-// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
-
-enum {
- DDS_MAGIC = 0x20534444,
- DDSD_PITCH = 0x00000008,
- DDSD_LINEARSIZE = 0x00080000,
- DDSD_MIPMAPCOUNT = 0x00020000,
- DDPF_FOURCC = 0x00000004,
- DDPF_ALPHAPIXELS = 0x00000001,
- DDPF_INDEXED = 0x00000020,
- DDPF_RGB = 0x00000040,
-};
-
-enum DDSFormat {
- DDS_DXT1,
- DDS_DXT3,
- DDS_DXT5,
- DDS_ATI1,
- DDS_ATI2,
- DDS_A2XY,
- DDS_BGRA8,
- DDS_BGR8,
- DDS_RGBA8, //flipped in dds
- DDS_RGB8, //flipped in dds
- DDS_BGR5A1,
- DDS_BGR565,
- DDS_BGR10A2,
- DDS_INDEXED,
- DDS_LUMINANCE,
- DDS_LUMINANCE_ALPHA,
- DDS_MAX
-};
-
-struct DDSFormatInfo {
- const char *name = nullptr;
- bool compressed = false;
- bool palette = false;
- uint32_t divisor = 0;
- uint32_t block_size = 0;
- Image::Format format = Image::Format::FORMAT_BPTC_RGBA;
-};
-
-static const DDSFormatInfo dds_format_info[DDS_MAX] = {
- { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 },
- { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 },
- { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 },
- { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R },
- { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
- { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
- { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
- { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 },
- { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
- { "RGB8", false, false, 1, 3, Image::FORMAT_RGB8 },
- { "BGR5A1", false, false, 1, 2, Image::FORMAT_RGBA8 },
- { "BGR565", false, false, 1, 2, Image::FORMAT_RGB8 },
- { "BGR10A2", false, false, 1, 4, Image::FORMAT_RGBA8 },
- { "GRAYSCALE", false, false, 1, 1, Image::FORMAT_L8 },
- { "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 }
-};
-
Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_CANT_OPEN;
@@ -113,303 +52,12 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Unable to open DDS texture file '" + p_path + "'.");
- uint32_t magic = f->get_32();
- uint32_t hsize = f->get_32();
- uint32_t flags = f->get_32();
- uint32_t height = f->get_32();
- uint32_t width = f->get_32();
- uint32_t pitch = f->get_32();
- /* uint32_t depth = */ f->get_32();
- uint32_t mipmaps = f->get_32();
-
- //skip 11
- for (int i = 0; i < 11; i++) {
- f->get_32();
- }
-
- //validate
-
- // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
- // but non-mandatory when reading (as some writers don't set them)...
- if (magic != DDS_MAGIC || hsize != 124) {
- ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid or unsupported DDS texture file '" + p_path + "'.");
- }
-
- /* uint32_t format_size = */ f->get_32();
- uint32_t format_flags = f->get_32();
- uint32_t format_fourcc = f->get_32();
- uint32_t format_rgb_bits = f->get_32();
- uint32_t format_red_mask = f->get_32();
- uint32_t format_green_mask = f->get_32();
- uint32_t format_blue_mask = f->get_32();
- uint32_t format_alpha_mask = f->get_32();
-
- /* uint32_t caps_1 = */ f->get_32();
- /* uint32_t caps_2 = */ f->get_32();
- /* uint32_t caps_ddsx = */ f->get_32();
-
- //reserved skip
- f->get_32();
- f->get_32();
-
- /*
- print_line("DDS width: "+itos(width));
- print_line("DDS height: "+itos(height));
- print_line("DDS mipmaps: "+itos(mipmaps));
-
- printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size);
- printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask);
- */
-
- //must avoid this later
- while (f->get_position() < 128) {
- f->get_8();
- }
-
- DDSFormat dds_format;
-
- if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) {
- dds_format = DDS_DXT1;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) {
- dds_format = DDS_DXT3;
-
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) {
- dds_format = DDS_DXT5;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) {
- dds_format = DDS_ATI1;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) {
- dds_format = DDS_ATI2;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) {
- dds_format = DDS_A2XY;
-
- } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) {
- dds_format = DDS_BGRA8;
- } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) {
- dds_format = DDS_BGR8;
- } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) {
- dds_format = DDS_RGBA8;
- } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) {
- dds_format = DDS_RGB8;
-
- } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) {
- dds_format = DDS_BGR5A1;
- } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) {
- dds_format = DDS_BGR10A2;
- } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) {
- dds_format = DDS_BGR565;
- } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff) {
- dds_format = DDS_LUMINANCE;
- } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff && format_alpha_mask == 0xff00) {
- dds_format = DDS_LUMINANCE_ALPHA;
- } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) {
- dds_format = DDS_BGR565;
- } else {
- //printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask);
- ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'.");
- }
-
- if (!(flags & DDSD_MIPMAPCOUNT)) {
- mipmaps = 1;
- }
-
- Vector<uint8_t> src_data;
-
- const DDSFormatInfo &info = dds_format_info[dds_format];
- uint32_t w = width;
- uint32_t h = height;
-
- if (info.compressed) {
- //compressed bc
-
- uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
- ERR_FAIL_COND_V(size != pitch, Ref<Resource>());
- ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), Ref<Resource>());
-
- for (uint32_t i = 1; i < mipmaps; i++) {
- w = MAX(1u, w >> 1);
- h = MAX(1u, h >> 1);
- uint32_t bsize = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
- //printf("%i x %i - block: %i\n",w,h,bsize);
- size += bsize;
- }
-
- src_data.resize(size);
- uint8_t *wb = src_data.ptrw();
- f->get_buffer(wb, size);
-
- } else if (info.palette) {
- //indexed
- ERR_FAIL_COND_V(!(flags & DDSD_PITCH), Ref<Resource>());
- ERR_FAIL_COND_V(format_rgb_bits != 8, Ref<Resource>());
-
- uint32_t size = pitch * height;
- ERR_FAIL_COND_V(size != width * height * info.block_size, Ref<Resource>());
-
- uint8_t palette[256 * 4];
- f->get_buffer(palette, 256 * 4);
-
- int colsize = 3;
- for (int i = 0; i < 256; i++) {
- if (palette[i * 4 + 3] < 255) {
- colsize = 4;
- }
- }
-
- int w2 = width;
- int h2 = height;
-
- for (uint32_t i = 1; i < mipmaps; i++) {
- w2 = (w2 + 1) >> 1;
- h2 = (h2 + 1) >> 1;
- size += w2 * h2 * info.block_size;
- }
-
- src_data.resize(size + 256 * colsize);
- uint8_t *wb = src_data.ptrw();
- f->get_buffer(wb, size);
-
- for (int i = 0; i < 256; i++) {
- int dst_ofs = size + i * colsize;
- int src_ofs = i * 4;
- wb[dst_ofs + 0] = palette[src_ofs + 2];
- wb[dst_ofs + 1] = palette[src_ofs + 1];
- wb[dst_ofs + 2] = palette[src_ofs + 0];
- if (colsize == 4) {
- wb[dst_ofs + 3] = palette[src_ofs + 3];
- }
- }
- } else {
- //uncompressed generic...
-
- uint32_t size = width * height * info.block_size;
-
- for (uint32_t i = 1; i < mipmaps; i++) {
- w = (w + 1) >> 1;
- h = (h + 1) >> 1;
- size += w * h * info.block_size;
- }
-
- if (dds_format == DDS_BGR565) {
- size = size * 3 / 2;
- } else if (dds_format == DDS_BGR5A1) {
- size = size * 2;
- }
-
- src_data.resize(size);
- uint8_t *wb = src_data.ptrw();
- f->get_buffer(wb, size);
-
- switch (dds_format) {
- case DDS_BGR5A1: {
- // TO RGBA
- int colcount = size / 4;
-
- for (int i = colcount - 1; i >= 0; i--) {
- int src_ofs = i * 2;
- int dst_ofs = i * 4;
-
- uint8_t a = wb[src_ofs + 1] & 0x80;
- uint8_t b = wb[src_ofs] & 0x1F;
- uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x3) << 3);
- uint8_t r = (wb[src_ofs + 1] >> 2) & 0x1F;
- wb[dst_ofs + 0] = r << 3;
- wb[dst_ofs + 1] = g << 3;
- wb[dst_ofs + 2] = b << 3;
- wb[dst_ofs + 3] = a ? 255 : 0;
- }
- } break;
- case DDS_BGR565: {
- int colcount = size / 3;
-
- for (int i = colcount - 1; i >= 0; i--) {
- int src_ofs = i * 2;
- int dst_ofs = i * 3;
-
- uint8_t b = wb[src_ofs] & 0x1F;
- uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x7) << 3);
- uint8_t r = wb[src_ofs + 1] >> 3;
- wb[dst_ofs + 0] = r << 3;
- wb[dst_ofs + 1] = g << 2;
- wb[dst_ofs + 2] = b << 3; //b<<3;
- }
-
- } break;
- case DDS_BGR10A2: {
- // TO RGBA
- int colcount = size / 4;
-
- for (int i = colcount - 1; i >= 0; i--) {
- int ofs = i * 4;
-
- uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24);
-
- uint8_t a = (w32 & 0xc0000000) >> 24;
- uint8_t r = (w32 & 0x3ff00000) >> 22;
- uint8_t g = (w32 & 0xffc00) >> 12;
- uint8_t b = (w32 & 0x3ff) >> 2;
-
- wb[ofs + 0] = r;
- wb[ofs + 1] = g;
- wb[ofs + 2] = b;
- wb[ofs + 3] = a == 0xc0 ? 255 : a; //0xc0 should be opaque
- }
- } break;
- case DDS_BGRA8: {
- int colcount = size / 4;
-
- for (int i = 0; i < colcount; i++) {
- SWAP(wb[i * 4 + 0], wb[i * 4 + 2]);
- }
-
- } break;
- case DDS_BGR8: {
- int colcount = size / 3;
-
- for (int i = 0; i < colcount; i++) {
- SWAP(wb[i * 3 + 0], wb[i * 3 + 2]);
- }
- } break;
- case DDS_RGBA8: {
- /* do nothing either
- int colcount = size/4;
-
- for(int i=0;i<colcount;i++) {
- uint8_t r = wb[i*4+1];
- uint8_t g = wb[i*4+2];
- uint8_t b = wb[i*4+3];
- uint8_t a = wb[i*4+0];
-
- wb[i*4+0]=r;
- wb[i*4+1]=g;
- wb[i*4+2]=b;
- wb[i*4+3]=a;
- }
- */
- } break;
- case DDS_RGB8: {
- // do nothing
- /*
- int colcount = size/3;
-
- for(int i=0;i<colcount;i++) {
- SWAP( wb[i*3+0],wb[i*3+2] );
- }*/
- } break;
- case DDS_LUMINANCE: {
- // do nothing i guess?
-
- } break;
- case DDS_LUMINANCE_ALPHA: {
- // do nothing i guess?
-
- } break;
-
- default: {
- }
- }
+ Ref<Image> img = memnew(Image);
+ Error i_error = ImageLoaderDDS().load_image(img, f, false, 1.0);
+ if (r_error) {
+ *r_error = i_error;
}
- Ref<Image> img = memnew(Image(width, height, mipmaps - 1, info.format, src_data));
Ref<ImageTexture> texture = ImageTexture::create_from_image(img);
if (r_error) {
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 0c300eade4..9cc1fc0c31 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -533,6 +533,7 @@
[codeblock]
@export_node_path("Button", "TouchScreenButton") var some_button
[/codeblock]
+ [b]Note:[/b] The type must be a native class or a globally registered script (using the [code]class_name[/code] keyword) that inherits [Node].
</description>
</annotation>
<annotation name="@export_placeholder">
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
index 0d8453738d..26f326838c 100644
--- a/modules/gdscript/editor/gdscript_docgen.cpp
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -101,14 +101,16 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
doc.inherits = p_script->native->get_name();
}
- doc.brief_description = p_class->doc_brief_description;
- doc.description = p_class->doc_description;
- for (const Pair<String, String> &p : p_class->doc_tutorials) {
+ doc.brief_description = p_class->doc_data.brief;
+ doc.description = p_class->doc_data.description;
+ for (const Pair<String, String> &p : p_class->doc_data.tutorials) {
DocData::TutorialDoc td;
td.title = p.first;
td.link = p.second;
doc.tutorials.append(td);
}
+ doc.is_deprecated = p_class->doc_data.is_deprecated;
+ doc.is_experimental = p_class->doc_data.is_experimental;
for (const GDP::ClassNode::Member &member : p_class->members) {
switch (member.type) {
@@ -130,7 +132,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[const_name] = m_const->start_line;
DocData::ConstantDoc const_doc;
- DocData::constant_doc_from_variant(const_doc, const_name, m_const->initializer->reduced_value, m_const->doc_description);
+ DocData::constant_doc_from_variant(const_doc, const_name, m_const->initializer->reduced_value, m_const->doc_data.description);
+ const_doc.is_deprecated = m_const->doc_data.is_deprecated;
+ const_doc.is_experimental = m_const->doc_data.is_experimental;
doc.constants.push_back(const_doc);
} break;
@@ -153,7 +157,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
}
DocData::MethodDoc method_doc;
- DocData::method_doc_from_methodinfo(method_doc, mi, m_func->doc_description);
+ DocData::method_doc_from_methodinfo(method_doc, mi, m_func->doc_data.description);
+ method_doc.is_deprecated = m_func->doc_data.is_deprecated;
+ method_doc.is_experimental = m_func->doc_data.is_experimental;
doc.methods.push_back(method_doc);
} break;
@@ -172,7 +178,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
}
DocData::MethodDoc signal_doc;
- DocData::signal_doc_from_methodinfo(signal_doc, mi, m_signal->doc_description);
+ DocData::signal_doc_from_methodinfo(signal_doc, mi, m_signal->doc_data.description);
+ signal_doc.is_deprecated = m_signal->doc_data.is_deprecated;
+ signal_doc.is_experimental = m_signal->doc_data.is_experimental;
doc.signals.push_back(signal_doc);
} break;
@@ -185,7 +193,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
DocData::PropertyDoc prop_doc;
prop_doc.name = var_name;
- prop_doc.description = m_var->doc_description;
+ prop_doc.description = m_var->doc_data.description;
+ prop_doc.is_deprecated = m_var->doc_data.is_deprecated;
+ prop_doc.is_experimental = m_var->doc_data.is_experimental;
GDType dt = m_var->get_datatype();
switch (dt.kind) {
@@ -236,15 +246,21 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[name] = m_enum->start_line;
- doc.enums[name] = m_enum->doc_description;
+ DocData::EnumDoc enum_doc;
+ enum_doc.description = m_enum->doc_data.description;
+ enum_doc.is_deprecated = m_enum->doc_data.is_deprecated;
+ enum_doc.is_experimental = m_enum->doc_data.is_experimental;
+ doc.enums[name] = enum_doc;
for (const GDP::EnumNode::Value &val : m_enum->values) {
DocData::ConstantDoc const_doc;
const_doc.name = val.identifier->name;
const_doc.value = String(Variant(val.value));
const_doc.is_value_valid = true;
- const_doc.description = val.doc_description;
const_doc.enumeration = name;
+ const_doc.description = val.doc_data.description;
+ const_doc.is_deprecated = val.doc_data.is_deprecated;
+ const_doc.is_experimental = val.doc_data.is_experimental;
doc.constants.push_back(const_doc);
}
@@ -257,10 +273,12 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[name] = m_enum_val.identifier->start_line;
- DocData::ConstantDoc constant_doc;
- constant_doc.enumeration = "@unnamed_enums";
- DocData::constant_doc_from_variant(constant_doc, name, m_enum_val.value, m_enum_val.doc_description);
- doc.constants.push_back(constant_doc);
+ DocData::ConstantDoc const_doc;
+ DocData::constant_doc_from_variant(const_doc, name, m_enum_val.value, m_enum_val.doc_data.description);
+ const_doc.enumeration = "@unnamed_enums";
+ const_doc.is_deprecated = m_enum_val.doc_data.is_deprecated;
+ const_doc.is_experimental = m_enum_val.doc_data.is_experimental;
+ doc.constants.push_back(const_doc);
} break;
case GDP::ClassNode::Member::GROUP:
case GDP::ClassNode::Member::UNDEFINED:
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 01e94ebb22..cb04913620 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1772,6 +1772,15 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
+#ifdef DEBUG_ENABLED
+ if (p_assignable->identifier != nullptr && p_assignable->identifier->suite != nullptr && p_assignable->identifier->suite->parent_block != nullptr) {
+ if (p_assignable->identifier->suite->parent_block->has_local(p_assignable->identifier->name)) {
+ const GDScriptParser::SuiteNode::Local &local = p_assignable->identifier->suite->parent_block->get_local(p_assignable->identifier->name);
+ parser->push_warning(p_assignable->identifier, GDScriptWarning::CONFUSABLE_LOCAL_DECLARATION, local.get_name(), p_assignable->identifier->name);
+ }
+ }
+#endif
+
GDScriptParser::DataType specified_type;
bool has_specified_type = p_assignable->datatype_specifier != nullptr;
if (has_specified_type) {
@@ -3518,12 +3527,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
case GDScriptParser::ClassNode::Member::FUNCTION: {
if (is_base && (!base.is_meta_type || member.function->is_static)) {
p_identifier->set_datatype(make_callable_type(member.function->info));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
return;
}
} break;
case GDScriptParser::ClassNode::Member::CLASS: {
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
return;
}
@@ -3662,9 +3673,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
found_source = true;
} break;
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS:
break;
}
+#ifdef DEBUG_ENABLED
+ if (!found_source && p_identifier->suite != nullptr && p_identifier->suite->has_local(p_identifier->name)) {
+ parser->push_warning(p_identifier, GDScriptWarning::CONFUSABLE_LOCAL_USAGE, p_identifier->name);
+ }
+#endif
+
// Not a local, so check members.
if (!found_source) {
reduce_identifier_from_base(p_identifier);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 47cd3f768b..6057a00f9b 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -226,7 +226,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
if (opcodes.size()) {
function->code = opcodes;
- function->_code_ptr = &function->code[0];
+ function->_code_ptr = &function->code.write[0];
function->_code_size = opcodes.size();
} else {
@@ -577,6 +577,12 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
append(Address());
append(p_target);
append(p_operator);
+ append(0); // Signature storage.
+ append(0); // Return type storage.
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*(opcodes.ptr()));
+ for (int i = 0; i < _pointer_size; i++) {
+ append(0); // Space for function pointer.
+ }
}
void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
@@ -610,6 +616,12 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
append(p_right_operand);
append(p_target);
append(p_operator);
+ append(0); // Signature storage.
+ append(0); // Return type storage.
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*(opcodes.ptr()));
+ for (int i = 0; i < _pointer_size; i++) {
+ append(0); // Space for function pointer.
+ }
}
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 3f571602e8..2a52db4158 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -225,194 +225,211 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName identifier = in->name;
- // Try function parameters.
- if (codegen.parameters.has(identifier)) {
- return codegen.parameters[identifier];
- }
-
- // Try local variables and constants.
- if (!p_initializer && codegen.locals.has(identifier)) {
- return codegen.locals[identifier];
- }
+ switch (in->source) {
+ // LOCALS.
+ case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
+ case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
+ case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
+ case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
+ case GDScriptParser::IdentifierNode::LOCAL_BIND: {
+ // Try function parameters.
+ if (codegen.parameters.has(identifier)) {
+ return codegen.parameters[identifier];
+ }
- // Try class members.
- if (_is_class_member_property(codegen, identifier)) {
- // Get property.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
- gen->write_get_member(temp, identifier);
- return temp;
- }
+ // Try local variables and constants.
+ if (!p_initializer && codegen.locals.has(identifier)) {
+ return codegen.locals[identifier];
+ }
+ } break;
- // Try members.
- if (!codegen.function_node || !codegen.function_node->is_static) {
- // Try member variables.
- if (codegen.script->member_indices.has(identifier)) {
- if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
- // Perform getter.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);
- Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
- gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
+ // MEMBERS.
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: {
+ // Try class members.
+ if (_is_class_member_property(codegen, identifier)) {
+ // Get property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
+ gen->write_get_member(temp, identifier);
return temp;
- } else {
- // No getter or inside getter: direct member access.
- int idx = codegen.script->member_indices[identifier].index;
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
}
- }
- }
- // Try static variables.
- {
- GDScript *scr = codegen.script;
- while (scr) {
- if (scr->static_variables_indices.has(identifier)) {
- if (scr->static_variables_indices[identifier].getter != StringName() && scr->static_variables_indices[identifier].getter != codegen.function_name) {
- // Perform getter.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
- GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);
- Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
- gen->write_call(temp, class_addr, scr->static_variables_indices[identifier].getter, args);
- return temp;
- } else {
- // No getter or inside getter: direct variable access.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
- GDScriptCodeGenerator::Address _class = codegen.add_constant(scr);
- int index = scr->static_variables_indices[identifier].index;
- gen->write_get_static_variable(temp, _class, index);
- return temp;
+ // Try members.
+ if (!codegen.function_node || !codegen.function_node->is_static) {
+ // Try member variables.
+ if (codegen.script->member_indices.has(identifier)) {
+ if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
+ // Perform getter.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);
+ Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
+ gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
+ return temp;
+ } else {
+ // No getter or inside getter: direct member access.
+ int idx = codegen.script->member_indices[identifier].index;
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
+ }
}
}
- scr = scr->_base;
- }
- }
+ } break;
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: {
+ // Try methods and signals (can be Callable and Signal).
+
+ // Search upwards through parent classes:
+ const GDScriptParser::ClassNode *base_class = codegen.class_node;
+ while (base_class != nullptr) {
+ if (base_class->has_member(identifier)) {
+ const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+
+ gen->write_get_named(temp, identifier, self);
+ return temp;
+ }
+ }
+ base_class = base_class->base_type.class_type;
+ }
- // Try class constants.
- {
- GDScript *owner = codegen.script;
- while (owner) {
- GDScript *scr = owner;
+ // Try in native base.
+ GDScript *scr = codegen.script;
GDScriptNativeClass *nc = nullptr;
while (scr) {
- if (scr->constants.has(identifier)) {
- return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
- }
if (scr->native.is_valid()) {
nc = scr->native.ptr();
}
scr = scr->_base;
}
- // Class C++ integer constant.
- if (nc) {
- bool success = false;
- int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
- if (success) {
- return codegen.add_constant(constant);
- }
- }
+ if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
- owner = owner->_owner;
- }
- }
+ gen->write_get_named(temp, identifier, self);
+ return temp;
+ }
+ } break;
+ case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS: {
+ // Try class constants.
+ GDScript *owner = codegen.script;
+ while (owner) {
+ GDScript *scr = owner;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->constants.has(identifier)) {
+ return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
+ }
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
+ }
- // Try signals and methods (can be made callables).
- {
- // Search upwards through parent classes:
- const GDScriptParser::ClassNode *base_class = codegen.class_node;
- while (base_class != nullptr) {
- if (base_class->has_member(identifier)) {
- const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
- if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
- // Get like it was a property.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
- GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+ // Class C++ integer constant.
+ if (nc) {
+ bool success = false;
+ int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
+ if (success) {
+ return codegen.add_constant(constant);
+ }
+ }
- gen->write_get_named(temp, identifier, self);
- return temp;
+ owner = owner->_owner;
+ }
+ } break;
+ case GDScriptParser::IdentifierNode::STATIC_VARIABLE: {
+ // Try static variables.
+ GDScript *scr = codegen.script;
+ while (scr) {
+ if (scr->static_variables_indices.has(identifier)) {
+ if (scr->static_variables_indices[identifier].getter != StringName() && scr->static_variables_indices[identifier].getter != codegen.function_name) {
+ // Perform getter.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
+ GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);
+ Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
+ gen->write_call(temp, class_addr, scr->static_variables_indices[identifier].getter, args);
+ return temp;
+ } else {
+ // No getter or inside getter: direct variable access.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
+ GDScriptCodeGenerator::Address _class = codegen.add_constant(scr);
+ int index = scr->static_variables_indices[identifier].index;
+ gen->write_get_static_variable(temp, _class, index);
+ return temp;
+ }
}
+ scr = scr->_base;
}
- base_class = base_class->base_type.class_type;
- }
+ } break;
- // Try in native base.
- GDScript *scr = codegen.script;
- GDScriptNativeClass *nc = nullptr;
- while (scr) {
- if (scr->native.is_valid()) {
- nc = scr->native.ptr();
+ // GLOBALS.
+ case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE: {
+ // Try globals.
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
+ // If it's an autoload singleton, we postpone to load it at runtime.
+ // This is so one autoload doesn't try to load another before it's compiled.
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
+ GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
+ gen->write_store_global(global, idx);
+ return global;
+ } else {
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
+ Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ return codegen.add_constant(global);
+ }
}
- scr = scr->_base;
- }
-
- if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
- // Get like it was a property.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
- GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
-
- gen->write_get_named(temp, identifier, self);
- return temp;
- }
- }
-
- // Try globals.
- if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
- // If it's an autoload singleton, we postpone to load it at runtime.
- // This is so one autoload doesn't try to load another before it's compiled.
- HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
- GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
- gen->write_store_global(global, idx);
- return global;
- } else {
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
- Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
- return codegen.add_constant(global);
- }
- }
- // Try global classes.
- if (ScriptServer::is_global_class(identifier)) {
- const GDScriptParser::ClassNode *class_node = codegen.class_node;
- while (class_node->outer) {
- class_node = class_node->outer;
- }
+ // Try global classes.
+ if (ScriptServer::is_global_class(identifier)) {
+ const GDScriptParser::ClassNode *class_node = codegen.class_node;
+ while (class_node->outer) {
+ class_node = class_node->outer;
+ }
- Ref<Resource> res;
+ Ref<Resource> res;
- if (class_node->identifier && class_node->identifier->name == identifier) {
- res = Ref<GDScript>(main_script);
- } else {
- String global_class_path = ScriptServer::get_global_class_path(identifier);
- if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
- Error err = OK;
- res = GDScriptCache::get_full_script(global_class_path, err);
- if (err != OK) {
- _set_error("Can't load global class " + String(identifier), p_expression);
- r_error = ERR_COMPILATION_FAILED;
- return GDScriptCodeGenerator::Address();
- }
- } else {
- res = ResourceLoader::load(global_class_path);
- if (res.is_null()) {
- _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
- r_error = ERR_COMPILATION_FAILED;
- return GDScriptCodeGenerator::Address();
+ if (class_node->identifier && class_node->identifier->name == identifier) {
+ res = Ref<GDScript>(main_script);
+ } else {
+ String global_class_path = ScriptServer::get_global_class_path(identifier);
+ if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
+ Error err = OK;
+ res = GDScriptCache::get_full_script(global_class_path, err);
+ if (err != OK) {
+ _set_error("Can't load global class " + String(identifier), p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
+ } else {
+ res = ResourceLoader::load(global_class_path);
+ if (res.is_null()) {
+ _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
+ }
}
- }
- }
- return codegen.add_constant(res);
- }
+ return codegen.add_constant(res);
+ }
#ifdef TOOLS_ENABLED
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
- GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
- gen->write_store_named_global(global, identifier);
- return global;
- }
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
+ GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
+ gen->write_store_named_global(global, identifier);
+ return global;
+ }
#endif
+ } break;
+ }
+
// Not found, error.
_set_error("Identifier not found: " + String(identifier), p_expression);
r_error = ERR_COMPILATION_FAILED;
@@ -2710,20 +2727,21 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
case GDScriptParser::ClassNode::Member::GROUP: {
const GDScriptParser::AnnotationNode *annotation = member.annotation;
- StringName name = annotation->export_info.name;
+ // Avoid name conflict. See GH-78252.
+ StringName name = vformat("@group_%d_%s", p_script->members.size(), annotation->export_info.name);
// This is not a normal member, but we need this to keep indices in order.
GDScript::MemberInfo minfo;
minfo.index = p_script->member_indices.size();
PropertyInfo prop_info;
- prop_info.name = name;
+ prop_info.name = annotation->export_info.name;
prop_info.usage = annotation->export_info.usage;
prop_info.hint_string = annotation->export_info.hint_string;
p_script->member_info[name] = prop_info;
p_script->member_indices[name] = minfo;
- p_script->members.insert(name);
+ p_script->members.insert(Variant());
} break;
default:
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index ec1d0af329..438ec02740 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -113,6 +113,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
switch (opcode) {
case OPCODE_OPERATOR: {
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr);
int operation = _code_ptr[ip + 4];
text += "operator ";
@@ -125,7 +126,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += " ";
text += DADDR(2);
- incr += 5;
+ incr += 7 + _pointer_size;
} break;
case OPCODE_OPERATOR_VALIDATED: {
text += "validated operator ";
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 47ceee3943..d27ea974e3 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -818,9 +818,10 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
node.insert_text = node.display.quote(p_quote_style);
r_result.insert(node.display, node);
- List<StringName> node_types;
- ClassDB::get_inheriters_from_class("Node", &node_types);
- for (const StringName &E : node_types) {
+
+ List<StringName> native_classes;
+ ClassDB::get_inheriters_from_class("Node", &native_classes);
+ for (const StringName &E : native_classes) {
if (!ClassDB::is_class_exposed(E)) {
continue;
}
@@ -828,6 +829,17 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
option.insert_text = option.display.quote(p_quote_style);
r_result.insert(option.display, option);
}
+
+ List<StringName> global_script_classes;
+ ScriptServer::get_global_class_list(&global_script_classes);
+ for (const StringName &E : global_script_classes) {
+ if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(E), "Node")) {
+ continue;
+ }
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
+ option.insert_text = option.display.quote(p_quote_style);
+ r_result.insert(option.display, option);
+ }
} else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
@@ -1403,7 +1415,7 @@ struct RecursionCheck {
}
};
-static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type);
static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
@@ -1467,7 +1479,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
} break;
case GDScriptParser::Node::IDENTIFIER: {
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
- found = _guess_identifier_type(p_context, id->name, r_type);
+ found = _guess_identifier_type(p_context, id, r_type);
} break;
case GDScriptParser::Node::DICTIONARY: {
// Try to recreate the dictionary.
@@ -1910,7 +1922,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
return found;
}
-static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type) {
static int recursion_depth = 0;
RecursionCheck recursion(&recursion_depth);
if (unlikely(recursion.check())) {
@@ -1924,36 +1936,49 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
GDScriptParser::SuiteNode *suite = p_context.current_suite;
bool is_function_parameter = false;
- if (suite) {
- if (suite->has_local(p_identifier)) {
- const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier);
+ bool can_be_local = true;
+ switch (p_identifier->source) {
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS:
+ case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
+ case GDScriptParser::IdentifierNode::STATIC_VARIABLE:
+ can_be_local = false;
+ break;
+ default:
+ break;
+ }
- id_type = local.get_datatype();
+ if (can_be_local && suite && suite->has_local(p_identifier->name)) {
+ const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);
- // Check initializer as the first assignment.
- switch (local.type) {
- case GDScriptParser::SuiteNode::Local::VARIABLE:
- if (local.variable->initializer) {
- last_assign_line = local.variable->initializer->end_line;
- last_assigned_expression = local.variable->initializer;
- }
- break;
- case GDScriptParser::SuiteNode::Local::CONSTANT:
- if (local.constant->initializer) {
- last_assign_line = local.constant->initializer->end_line;
- last_assigned_expression = local.constant->initializer;
- }
- break;
- case GDScriptParser::SuiteNode::Local::PARAMETER:
- if (local.parameter->initializer) {
- last_assign_line = local.parameter->initializer->end_line;
- last_assigned_expression = local.parameter->initializer;
- }
- is_function_parameter = true;
- break;
- default:
- break;
- }
+ id_type = local.get_datatype();
+
+ // Check initializer as the first assignment.
+ switch (local.type) {
+ case GDScriptParser::SuiteNode::Local::VARIABLE:
+ if (local.variable->initializer) {
+ last_assign_line = local.variable->initializer->end_line;
+ last_assigned_expression = local.variable->initializer;
+ }
+ break;
+ case GDScriptParser::SuiteNode::Local::CONSTANT:
+ if (local.constant->initializer) {
+ last_assign_line = local.constant->initializer->end_line;
+ last_assigned_expression = local.constant->initializer;
+ }
+ break;
+ case GDScriptParser::SuiteNode::Local::PARAMETER:
+ if (local.parameter->initializer) {
+ last_assign_line = local.parameter->initializer->end_line;
+ last_assigned_expression = local.parameter->initializer;
+ }
+ is_function_parameter = true;
+ break;
+ default:
+ break;
}
}
@@ -1968,7 +1993,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);
if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);
- if (id->name == p_identifier) {
+ if (id->name == p_identifier->name && id->source == p_identifier->source) {
last_assign_line = assign->assigned_value->end_line;
last_assigned_expression = assign->assigned_value;
}
@@ -1986,7 +2011,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
// Credit: Zylann.
// TODO: this could be hacked to detect ANDed conditions too...
const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition);
- if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier) {
+ if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier->name && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->source == p_identifier->source) {
// Bingo.
GDScriptParser::CompletionContext c = p_context;
c.current_line = type_test->operand->start_line;
@@ -2022,8 +2047,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
case GDScriptParser::DataType::CLASS:
if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {
GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
- if (parent_function->parameters_indices.has(p_identifier)) {
- const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]];
+ if (parent_function->parameters_indices.has(p_identifier->name)) {
+ const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];
if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
id_type = parameter->get_datatype();
}
@@ -2048,7 +2073,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
MethodInfo info;
if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {
for (const PropertyInfo &E : info.arguments) {
- if (E.name == p_identifier) {
+ if (E.name == p_identifier->name) {
r_type = _type_from_property(E);
return true;
}
@@ -2076,14 +2101,14 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
base.type.class_type = p_context.current_class;
base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;
- if (_guess_identifier_type_from_base(p_context, base, p_identifier, r_type)) {
+ if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, r_type)) {
return true;
}
}
// Check global scripts.
- if (ScriptServer::is_global_class(p_identifier)) {
- String script = ScriptServer::get_global_class_path(p_identifier);
+ if (ScriptServer::is_global_class(p_identifier->name)) {
+ String script = ScriptServer::get_global_class_path(p_identifier->name);
if (script.to_lower().ends_with(".gd")) {
Error err = OK;
Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
@@ -2099,7 +2124,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
return true;
}
} else {
- Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
+ Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name));
if (scr.is_valid()) {
r_type = _type_from_variant(scr);
r_type.type.is_meta_type = true;
@@ -2110,20 +2135,20 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
}
// Check global variables (including autoloads).
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
- r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) {
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name]);
return true;
}
// Check ClassDB.
- if (ClassDB::class_exists(p_identifier) && ClassDB::is_class_exposed(p_identifier)) {
+ if (ClassDB::class_exists(p_identifier->name) && ClassDB::is_class_exposed(p_identifier->name)) {
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = p_identifier;
+ r_type.type.native_type = p_identifier->name;
r_type.type.is_constant = true;
- if (Engine::get_singleton()->has_singleton(p_identifier)) {
+ if (Engine::get_singleton()->has_singleton(p_identifier->name)) {
r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
+ r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier->name);
} else {
r_type.type.is_meta_type = true;
r_type.value = Variant();
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index dfe66e6688..5230773c13 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -473,7 +473,7 @@ private:
MethodBind **_methods_ptr = nullptr;
int _lambdas_count = 0;
GDScriptFunction **_lambdas_ptr = nullptr;
- const int *_code_ptr = nullptr;
+ int *_code_ptr = nullptr;
int _code_size = 0;
int _argument_count = 0;
int _stack_size = 0;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 2b91ba8f86..debc85ebbf 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -571,8 +571,8 @@ void GDScriptParser::parse_program() {
class_doc_line = MIN(class_doc_line, E.key);
}
}
- if (has_comment(class_doc_line)) {
- get_class_doc_comment(class_doc_line, head->doc_brief_description, head->doc_description, head->doc_tutorials, false);
+ if (has_comment(class_doc_line, true)) {
+ head->doc_data = parse_class_doc_comment(class_doc_line, false);
}
#endif // TOOLS_ENABLED
@@ -771,12 +771,16 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(b
// Check whether current line has a doc comment
if (has_comment(previous.start_line, true)) {
- member->doc_description = get_doc_comment(previous.start_line, true);
+ if constexpr (std::is_same_v<T, ClassNode>) {
+ member->doc_data = parse_class_doc_comment(previous.start_line, true, true);
+ } else {
+ member->doc_data = parse_doc_comment(previous.start_line, true);
+ }
} else if (has_comment(doc_comment_line, true)) {
if constexpr (std::is_same_v<T, ClassNode>) {
- get_class_doc_comment(doc_comment_line, member->doc_brief_description, member->doc_description, member->doc_tutorials, true);
+ member->doc_data = parse_class_doc_comment(doc_comment_line, true);
} else {
- member->doc_description = get_doc_comment(doc_comment_line);
+ member->doc_data = parse_doc_comment(doc_comment_line);
}
}
#endif // TOOLS_ENABLED
@@ -1314,25 +1318,34 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_static) {
#ifdef TOOLS_ENABLED
// Enum values documentation.
for (int i = 0; i < enum_node->values.size(); i++) {
+ int doc_comment_line = enum_node->values[i].line;
+ bool single_line = false;
+
+ if (has_comment(doc_comment_line, true)) {
+ single_line = true;
+ } else if (has_comment(doc_comment_line - 1, true)) {
+ doc_comment_line--;
+ } else {
+ continue;
+ }
+
if (i == enum_node->values.size() - 1) {
// If close bracket is same line as last value.
- if (enum_node->values[i].line != previous.start_line && has_comment(enum_node->values[i].line)) {
- if (named) {
- enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
- } else {
- current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
- }
+ if (doc_comment_line == previous.start_line) {
+ break;
}
} else {
// If two values are same line.
- if (enum_node->values[i].line != enum_node->values[i + 1].line && has_comment(enum_node->values[i].line)) {
- if (named) {
- enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
- } else {
- current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
- }
+ if (doc_comment_line == enum_node->values[i + 1].line) {
+ continue;
}
}
+
+ if (named) {
+ enum_node->values.write[i].doc_data = parse_doc_comment(doc_comment_line, single_line);
+ } else {
+ current_class->set_enum_value_doc_data(enum_node->values[i].identifier->name, parse_doc_comment(doc_comment_line, single_line));
+ }
}
#endif // TOOLS_ENABLED
@@ -2267,6 +2280,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
IdentifierNode *identifier = alloc_node<IdentifierNode>();
complete_extents(identifier);
identifier->name = previous.get_identifier();
+#ifdef DEBUG_ENABLED
+ identifier->suite = current_suite;
+#endif
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
@@ -3411,19 +3427,20 @@ bool GDScriptParser::has_comment(int p_line, bool p_must_be_doc) {
return tokenizer.get_comments()[p_line].comment.begins_with("##");
}
-String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
+GDScriptParser::MemberDocData GDScriptParser::parse_doc_comment(int p_line, bool p_single_line) {
+ MemberDocData result;
+
const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
- ERR_FAIL_COND_V(!comments.has(p_line), String());
+ ERR_FAIL_COND_V(!comments.has(p_line), result);
if (p_single_line) {
if (comments[p_line].comment.begins_with("##")) {
- return comments[p_line].comment.trim_prefix("##").strip_edges();
+ result.description = comments[p_line].comment.trim_prefix("##").strip_edges();
+ return result;
}
- return "";
+ return result;
}
- String doc;
-
int line = p_line;
DocLineState state = DOC_LINE_NORMAL;
@@ -3451,29 +3468,42 @@ String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
}
String doc_line = comments[line].comment.trim_prefix("##");
- doc += _process_doc_line(doc_line, doc, space_prefix, state);
line++;
+
+ if (state == DOC_LINE_NORMAL) {
+ String stripped_line = doc_line.strip_edges();
+ if (stripped_line.begins_with("@deprecated")) {
+ result.is_deprecated = true;
+ continue;
+ } else if (stripped_line.begins_with("@experimental")) {
+ result.is_experimental = true;
+ continue;
+ }
+ }
+
+ result.description += _process_doc_line(doc_line, result.description, space_prefix, state);
}
- return doc;
+ return result;
}
-void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) {
+GDScriptParser::ClassDocData GDScriptParser::parse_class_doc_comment(int p_line, bool p_inner_class, bool p_single_line) {
+ ClassDocData result;
+
const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
- if (!comments.has(p_line)) {
- return;
+ ERR_FAIL_COND_V(!comments.has(p_line), result);
+
+ if (p_single_line) {
+ if (comments[p_line].comment.begins_with("##")) {
+ result.brief = comments[p_line].comment.trim_prefix("##").strip_edges();
+ return result;
+ }
+ return result;
}
- ERR_FAIL_COND(!p_brief.is_empty() || !p_desc.is_empty() || p_tutorials.size() != 0);
int line = p_line;
DocLineState state = DOC_LINE_NORMAL;
- enum Mode {
- BRIEF,
- DESC,
- TUTORIALS,
- DONE,
- };
- Mode mode = BRIEF;
+ bool is_in_brief = true;
if (p_inner_class) {
while (comments.has(line - 1)) {
@@ -3500,18 +3530,21 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
break;
}
- String doc_line = comments[line++].comment.trim_prefix("##");
- String title, link; // For tutorials.
+ String doc_line = comments[line].comment.trim_prefix("##");
+ line++;
if (state == DOC_LINE_NORMAL) {
- // Set the read mode.
String stripped_line = doc_line.strip_edges();
- if (stripped_line.is_empty()) {
- if (mode == BRIEF && !p_brief.is_empty()) {
- mode = DESC;
- }
+
+ // A blank line separates the description from the brief.
+ if (is_in_brief && !result.brief.is_empty() && stripped_line.is_empty()) {
+ is_in_brief = false;
continue;
- } else if (stripped_line.begins_with("@tutorial")) {
+ }
+
+ if (stripped_line.begins_with("@tutorial")) {
+ String title, link;
+
int begin_scan = String("@tutorial").length();
if (begin_scan >= stripped_line.length()) {
continue; // Invalid syntax.
@@ -3553,24 +3586,21 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
link = stripped_line.substr(colon_pos).strip_edges();
}
- mode = TUTORIALS;
- } else if (mode == TUTORIALS) { // Tutorial docs are single line, we need a @tag after it.
- mode = DONE;
+ result.tutorials.append(Pair<String, String>(title, link));
+ continue;
+ } else if (stripped_line.begins_with("@deprecated")) {
+ result.is_deprecated = true;
+ continue;
+ } else if (stripped_line.begins_with("@experimental")) {
+ result.is_experimental = true;
+ continue;
}
}
- switch (mode) {
- case BRIEF:
- p_brief += _process_doc_line(doc_line, p_brief, space_prefix, state);
- break;
- case DESC:
- p_desc += _process_doc_line(doc_line, p_desc, space_prefix, state);
- break;
- case TUTORIALS:
- p_tutorials.append(Pair<String, String>(title, link));
- break;
- case DONE:
- break;
+ if (is_in_brief) {
+ result.brief += _process_doc_line(doc_line, result.brief, space_prefix, state);
+ } else {
+ result.description += _process_doc_line(doc_line, result.description, space_prefix, state);
}
}
@@ -3578,11 +3608,11 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
const ClassNode::Member &m = current_class->members[0];
int first_member_line = m.get_line();
if (first_member_line == line) {
- p_brief = "";
- p_desc = "";
- p_tutorials.clear();
+ result = ClassDocData(); // Clear result.
}
}
+
+ return result;
}
#endif // TOOLS_ENABLED
@@ -3867,6 +3897,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
}
+ // WARNING: Do not merge with the previous `if` because there `!=`, not `==`!
if (p_annotation->name == SNAME("@export_flags")) {
const int64_t max_flags = 32;
Vector<String> t = arg_string.split(":", true, 1);
@@ -3892,6 +3923,18 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Starting from argument %d, the flag value must be specified explicitly.)", i + 1, max_flags + 1), p_annotation->arguments[i]);
return false;
}
+ } else if (p_annotation->name == SNAME("@export_node_path")) {
+ String native_class = arg_string;
+ if (ScriptServer::is_global_class(arg_string)) {
+ native_class = ScriptServer::get_global_class_native_base(arg_string);
+ }
+ if (!ClassDB::class_exists(native_class)) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_node_path": The class "%s" was not found in the global scope.)", i + 1, arg_string), p_annotation->arguments[i]);
+ return false;
+ } else if (!ClassDB::is_parent_class(native_class, SNAME("Node"))) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_node_path": The class "%s" does not inherit "Node".)", i + 1, arg_string), p_annotation->arguments[i]);
+ return false;
+ }
}
if (i > 0) {
@@ -3909,8 +3952,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
if (export_type.builtin_type == Variant::INT) {
variable->export_info.type = Variant::INT;
}
- }
- if (p_annotation->name == SNAME("@export_multiline")) {
+ } else if (p_annotation->name == SNAME("@export_multiline")) {
if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
DataType inner_type = export_type.get_container_element_type();
if (inner_type.builtin_type != Variant::STRING) {
@@ -3938,6 +3980,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
}
+ // WARNING: Do not merge with the previous `else if`! Otherwise `else` (default variable type check)
+ // will not work for the above annotations. `@export` and `@export_enum` validate the type separately.
if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 18757eb9fd..20f5dcf06d 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -257,6 +257,22 @@ public:
int line = 0, column = 0;
};
+#ifdef TOOLS_ENABLED
+ struct ClassDocData {
+ String brief;
+ String description;
+ Vector<Pair<String, String>> tutorials;
+ bool is_deprecated = false;
+ bool is_experimental = false;
+ };
+
+ struct MemberDocData {
+ String description;
+ bool is_deprecated = false;
+ bool is_experimental = false;
+ };
+#endif // TOOLS_ENABLED
+
struct Node {
enum Type {
NONE,
@@ -505,7 +521,7 @@ public:
int leftmost_column = 0;
int rightmost_column = 0;
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
};
@@ -513,7 +529,7 @@ public:
Vector<Value> values;
Variant dictionary;
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
EnumNode() {
@@ -720,14 +736,12 @@ public:
DataType base_type;
String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project.
#ifdef TOOLS_ENABLED
- String doc_description;
- String doc_brief_description;
- Vector<Pair<String, String>> doc_tutorials;
+ ClassDocData doc_data;
// EnumValue docs are parsed after itself, so we need a method to add/modify the doc property later.
- void set_enum_value_doc(const StringName &p_name, const String &p_doc_description) {
+ void set_enum_value_doc_data(const StringName &p_name, const MemberDocData &p_doc_data) {
ERR_FAIL_INDEX(members_indices[p_name], members.size());
- members.write[members_indices[p_name]].enum_value.doc_description = p_doc_description;
+ members.write[members_indices[p_name]].enum_value.doc_data = p_doc_data;
}
#endif // TOOLS_ENABLED
@@ -753,7 +767,9 @@ public:
members.push_back(Member(p_enum_value));
}
void add_member_group(AnnotationNode *p_annotation_node) {
- members_indices[p_annotation_node->export_info.name] = members.size();
+ // Avoid name conflict. See GH-78252.
+ StringName name = vformat("@group_%d_%s", members.size(), p_annotation_node->export_info.name);
+ members_indices[name] = members.size();
members.push_back(Member(p_annotation_node));
}
@@ -764,7 +780,7 @@ public:
struct ConstantNode : public AssignableNode {
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
ConstantNode() {
@@ -819,7 +835,7 @@ public:
LambdaNode *source_lambda = nullptr;
#ifdef TOOLS_ENABLED
Vector<Variant> default_arg_values;
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
bool resolved_signature = false;
@@ -843,19 +859,24 @@ public:
struct IdentifierNode : public ExpressionNode {
StringName name;
+#ifdef DEBUG_ENABLED
+ SuiteNode *suite = nullptr; // The block in which the identifier is used.
+#endif
enum Source {
UNDEFINED_SOURCE,
FUNCTION_PARAMETER,
- LOCAL_CONSTANT,
LOCAL_VARIABLE,
+ LOCAL_CONSTANT,
LOCAL_ITERATOR, // `for` loop iterator.
LOCAL_BIND, // Pattern bind.
- MEMBER_SIGNAL,
MEMBER_VARIABLE,
- STATIC_VARIABLE,
MEMBER_CONSTANT,
+ MEMBER_FUNCTION,
+ MEMBER_SIGNAL,
+ MEMBER_CLASS,
INHERITED_VARIABLE,
+ STATIC_VARIABLE,
};
Source source = UNDEFINED_SOURCE;
@@ -1006,7 +1027,7 @@ public:
Vector<ParameterNode *> parameters;
HashMap<StringName, int> parameters_indices;
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
SignalNode() {
@@ -1211,7 +1232,7 @@ public:
int assignments = 0;
bool is_static = false;
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
VariableNode() {
@@ -1486,12 +1507,12 @@ private:
ExpressionNode *parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign);
TypeNode *parse_type(bool p_allow_void = false);
+
#ifdef TOOLS_ENABLED
- // Doc comments.
int class_doc_line = 0x7FFFFFFF;
bool has_comment(int p_line, bool p_must_be_doc = false);
- String get_doc_comment(int p_line, bool p_single_line = false);
- void get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class);
+ MemberDocData parse_doc_comment(int p_line, bool p_single_line = false);
+ ClassDocData parse_class_doc_comment(int p_line, bool p_inner_class, bool p_single_line = false);
#endif // TOOLS_ENABLED
public:
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 3927a4dd3e..4f374b63b0 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -579,6 +579,24 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
return make_identifier(name);
}
+ if (!only_ascii) {
+ // Kept here in case the order with push_error matters.
+ Token id = make_identifier(name);
+
+#ifdef DEBUG_ENABLED
+ // Additional checks for identifiers but only in debug and if it's available in TextServer.
+ if (TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY)) {
+ int64_t confusable = TS->is_confusable(name, keyword_list);
+ if (confusable >= 0) {
+ push_error(vformat(R"(Identifier "%s" is visually similar to the GDScript keyword "%s" and thus not allowed.)", name, keyword_list[confusable]));
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ // Cannot be a keyword, as keywords are ASCII only.
+ return id;
+ }
+
// Define some helper macros for the switch case.
#define KEYWORD_GROUP_CASE(char) \
break; \
@@ -614,19 +632,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
}
// Not a keyword, so must be an identifier.
- Token id = make_identifier(name);
-
-#ifdef DEBUG_ENABLED
- // Additional checks for identifiers but only in debug and if it's available in TextServer.
- if (!only_ascii && TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY)) {
- int64_t confusable = TS->is_confusable(name, keyword_list);
- if (confusable >= 0) {
- push_error(vformat(R"(Identifier "%s" is visually similar to the GDScript keyword "%s" and thus not allowed.)", name, keyword_list[confusable]));
- }
- }
-#endif // DEBUG_ENABLED
-
- return id;
+ return make_identifier(name);
#undef KEYWORD_GROUP_CASE
#undef KEYWORD
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 44c4cb0fc3..1ddd54b323 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -685,7 +685,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_SWITCH(_code_ptr[ip]) {
OPCODE(OPCODE_OPERATOR) {
- CHECK_SPACE(5);
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr);
+ CHECK_SPACE(7 + _pointer_size);
bool valid;
Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
@@ -694,28 +695,71 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(a, 0);
GET_VARIANT_PTR(b, 1);
GET_VARIANT_PTR(dst, 2);
+ // Compute signatures (types of operands) so it can be optimized when matching.
+ uint32_t op_signature = _code_ptr[ip + 5];
+ uint32_t actual_signature = (a->get_type() << 8) | (b->get_type());
+
+ // Check if this is the first run. If so, store the current signature for the optimized path.
+ if (unlikely(op_signature == 0)) {
+ static Mutex initializer_mutex;
+ initializer_mutex.lock();
+ Variant::Type a_type = (Variant::Type)((actual_signature >> 8) & 0xFF);
+ Variant::Type b_type = (Variant::Type)(actual_signature & 0xFF);
+ Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(op, a_type, b_type);
+
+ if (unlikely(!op_func)) {
+#ifdef DEBUG_ENABLED
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+#endif
+ initializer_mutex.unlock();
+ OPCODE_BREAK;
+ } else {
+ Variant::Type ret_type = Variant::get_operator_return_type(op, a_type, b_type);
+ VariantInternal::initialize(dst, ret_type);
+ op_func(a, b, dst);
+
+ // Check again in case another thread already set it.
+ if (_code_ptr[ip + 5] == 0) {
+ _code_ptr[ip + 5] = actual_signature;
+ _code_ptr[ip + 6] = static_cast<int>(ret_type);
+ Variant::ValidatedOperatorEvaluator *tmp = reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]);
+ *tmp = op_func;
+ }
+ }
+ initializer_mutex.unlock();
+ } else if (likely(op_signature == actual_signature)) {
+ // If the signature matches, we can use the optimized path.
+ Variant::Type ret_type = static_cast<Variant::Type>(_code_ptr[ip + 6]);
+ Variant::ValidatedOperatorEvaluator op_func = *reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]);
+
+ // Make sure the return value has the correct type.
+ VariantInternal::initialize(dst, ret_type);
+ op_func(a, b, dst);
+ } else {
+ // If the signature doesn't match, we have to use the slow path.
#ifdef DEBUG_ENABLED
- Variant ret;
- Variant::evaluate(op, *a, *b, ret, valid);
+ Variant ret;
+ Variant::evaluate(op, *a, *b, ret, valid);
#else
- Variant::evaluate(op, *a, *b, *dst, valid);
+ Variant::evaluate(op, *a, *b, *dst, valid);
#endif
#ifdef DEBUG_ENABLED
- if (!valid) {
- if (ret.get_type() == Variant::STRING) {
- //return a string when invalid with the error
- err_text = ret;
- err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
- } else {
- err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ if (!valid) {
+ if (ret.get_type() == Variant::STRING) {
+ //return a string when invalid with the error
+ err_text = ret;
+ err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
+ } else {
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ }
+ OPCODE_BREAK;
}
- OPCODE_BREAK;
- }
- *dst = ret;
+ *dst = ret;
#endif
- ip += 5;
+ }
+ ip += 7 + _pointer_size;
}
DISPATCH_OPCODE;
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 8de78d2b9a..24aa793c47 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -136,6 +136,12 @@ String GDScriptWarning::get_message() const {
case CONFUSABLE_IDENTIFIER:
CHECK_SYMBOLS(1);
return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]);
+ case CONFUSABLE_LOCAL_DECLARATION:
+ CHECK_SYMBOLS(2);
+ return vformat(R"(The %s "%s" is declared below in the parent block.)", symbols[0], symbols[1]);
+ case CONFUSABLE_LOCAL_USAGE:
+ CHECK_SYMBOLS(1);
+ return vformat(R"(The identifier "%s" will be shadowed below in the block.)", symbols[0]);
case INFERENCE_ON_VARIANT:
CHECK_SYMBOLS(1);
return vformat("The %s type is being inferred from a Variant value, so it will be typed as Variant.", symbols[0]);
@@ -213,6 +219,8 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"DEPRECATED_KEYWORD",
"RENAMED_IN_GODOT_4_HINT",
"CONFUSABLE_IDENTIFIER",
+ "CONFUSABLE_LOCAL_DECLARATION",
+ "CONFUSABLE_LOCAL_USAGE",
"INFERENCE_ON_VARIANT",
"NATIVE_METHOD_OVERRIDE",
"GET_NODE_DEFAULT_WITHOUT_ONREADY",
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index ae6207fcdc..8444d46a88 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -83,6 +83,8 @@ public:
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
RENAMED_IN_GODOT_4_HINT, // A variable or function that could not be found has been renamed in Godot 4.
CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
+ CONFUSABLE_LOCAL_DECLARATION, // The parent block declares an identifier with the same name below.
+ CONFUSABLE_LOCAL_USAGE, // The identifier will be shadowed below in the block.
INFERENCE_ON_VARIANT, // The declaration uses type inference but the value is typed as Variant.
NATIVE_METHOD_OVERRIDE, // The script method overrides a native one, this may not work as intended.
GET_NODE_DEFAULT_WITHOUT_ONREADY, // A class variable uses `get_node()` (or the `$` notation) as its default value, but does not use the @onready annotation.
@@ -128,6 +130,8 @@ public:
WARN, // DEPRECATED_KEYWORD
WARN, // RENAMED_IN_GODOT_4_HINT
WARN, // CONFUSABLE_IDENTIFIER
+ WARN, // CONFUSABLE_LOCAL_DECLARATION
+ WARN, // CONFUSABLE_LOCAL_USAGE
ERROR, // INFERENCE_ON_VARIANT // Most likely done by accident, usually inference is trying for a particular type.
ERROR, // NATIVE_METHOD_OVERRIDE // May not work as expected.
ERROR, // GET_NODE_DEFAULT_WITHOUT_ONREADY // May not work as expected.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.gd
new file mode 100644
index 0000000000..57ae41922f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.gd
@@ -0,0 +1,4 @@
+var v1 = v1
+
+func test():
+ print(v1)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.out
new file mode 100644
index 0000000000..c337882d9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "v1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
index c4108f50de..b000c82717 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
@@ -126,7 +126,7 @@ func test():
assert(a_objects.get_typed_builtin() == TYPE_OBJECT)
assert(a_objects.get_typed_script() == A)
- var a_passed = (func check_a_passing(a_objects: Array[A]): return a_objects.size()).call(a_objects)
+ var a_passed = (func check_a_passing(p_objects: Array[A]): return p_objects.size()).call(a_objects)
assert(a_passed == 4)
var b_passed = (func check_b_passing(basic: Array): return basic[0] != null).call(b_objects)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.gd b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.gd
new file mode 100644
index 0000000000..3178f8d496
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.gd
@@ -0,0 +1,6 @@
+func test():
+ if true:
+ var a = 1
+ print(a)
+ var a = 2
+ print(a)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.out
new file mode 100644
index 0000000000..7365072ea7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> CONFUSABLE_LOCAL_DECLARATION
+>> The variable "a" is declared below in the parent block.
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.gd b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.gd
new file mode 100644
index 0000000000..4462c067bc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.gd
@@ -0,0 +1,6 @@
+var a = 1
+
+func test():
+ print(a)
+ var a = 2
+ print(a)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out
new file mode 100644
index 0000000000..0e0d607831
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out
@@ -0,0 +1,11 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> CONFUSABLE_LOCAL_USAGE
+>> The identifier "a" will be shadowed below in the block.
+>> WARNING
+>> Line: 5
+>> SHADOWED_VARIABLE
+>> The local variable "a" is shadowing an already-declared variable at line 1.
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.gd b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.gd
new file mode 100644
index 0000000000..eef8eb66e6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.gd
@@ -0,0 +1,6 @@
+var a = 1
+
+func test():
+ print(a)
+ var a = a + 1
+ print(a)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out
new file mode 100644
index 0000000000..228a510490
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> CONFUSABLE_LOCAL_USAGE
+>> The identifier "a" will be shadowed below in the block.
+>> WARNING
+>> Line: 5
+>> CONFUSABLE_LOCAL_USAGE
+>> The identifier "a" will be shadowed below in the block.
+>> WARNING
+>> Line: 5
+>> SHADOWED_VARIABLE
+>> The local variable "a" is shadowing an already-declared variable at line 1.
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.gd b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.gd
new file mode 100644
index 0000000000..1f207f27ac
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.gd
@@ -0,0 +1,7 @@
+var a = 1
+
+func test():
+ for _i in 3:
+ print(a)
+ var a = 2
+ print(a)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out
new file mode 100644
index 0000000000..0d20e9f7a0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> CONFUSABLE_LOCAL_USAGE
+>> The identifier "a" will be shadowed below in the block.
+>> WARNING
+>> Line: 6
+>> SHADOWED_VARIABLE
+>> The local variable "a" is shadowing an already-declared variable at line 1.
+1
+2
+1
+2
+1
+2
diff --git a/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.gd b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.gd
new file mode 100644
index 0000000000..e46f24cc5f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.gd
@@ -0,0 +1,17 @@
+extends RefCounted # TODO: Fix standalone annotations parsing.
+
+# GH-73843
+@export_group("Resource")
+
+# GH-78252
+@export var prop_1: int
+@export_category("prop_1")
+@export var prop_2: int
+
+func test():
+ var resource := Resource.new()
+ prints("Not shadowed:", resource.get_class())
+
+ for property in get_property_list():
+ if property.name in ["prop_1", "prop_2"]:
+ print(property)
diff --git a/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out
new file mode 100644
index 0000000000..96ae84e986
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+Not shadowed: Resource
+{ "name": "prop_1", "class_name": &"", "type": 2, "hint": 0, "hint_string": "int", "usage": 4102 }
+{ "name": "prop_1", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 128 }
+{ "name": "prop_2", "class_name": &"", "type": 2, "hint": 0, "hint_string": "int", "usage": 4102 }
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index a4667f784d..564c5929c7 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -23,8 +23,6 @@ env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.cpp")
if env["platform"] in ["macos", "ios"]:
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.mm")
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.m")
-elif env["platform"] == "android":
- env_mono.add_source_files(env.modules_sources, "mono_gd/android_mono_config.gen.cpp")
if env.editor_build:
env_mono.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/mono/config.py b/modules/mono/config.py
index a36083b64b..2b2a8d6235 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -1,6 +1,6 @@
# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "android", "haiku", "web", "ios"]
# Eventually support for each them should be added back (except Haiku if not supported by .NET Core)
-supported_platforms = ["windows", "macos", "linuxbsd"]
+supported_platforms = ["windows", "macos", "linuxbsd", "android"]
def can_build(env, platform):
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index ea2d14958b..4d61372ab0 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -3,6 +3,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
@@ -45,6 +47,17 @@ namespace GodotTools.Export
}
},
{ "default_value", true }
+ },
+ new Godot.Collections.Dictionary()
+ {
+ {
+ "option", new Godot.Collections.Dictionary()
+ {
+ { "name", "dotnet/embed_build_outputs" },
+ { "type", (int)Variant.Type.Bool }
+ }
+ },
+ { "default_value", false }
}
};
}
@@ -114,7 +127,7 @@ namespace GodotTools.Export
if (!DeterminePlatformFromFeatures(features, out string platform))
throw new NotSupportedException("Target platform not supported.");
- if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS }
+ if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS, OS.Platforms.Android }
.Contains(platform))
{
throw new NotImplementedException("Target platform not yet implemented.");
@@ -129,15 +142,19 @@ namespace GodotTools.Export
{
archs.Add("x86_64");
}
- else if (features.Contains("x86_32"))
+ if (features.Contains("x86_32"))
{
archs.Add("x86_32");
}
- else if (features.Contains("arm64"))
+ if (features.Contains("arm64"))
{
archs.Add("arm64");
}
- else if (features.Contains("universal"))
+ if (features.Contains("arm32"))
+ {
+ archs.Add("arm32");
+ }
+ if (features.Contains("universal"))
{
if (platform == OS.Platforms.MacOS)
{
@@ -146,6 +163,8 @@ namespace GodotTools.Export
}
}
+ bool embedBuildResults = (bool)GetOption("dotnet/embed_build_outputs") || features.Contains("android");
+
foreach (var arch in archs)
{
string ridOS = DetermineRuntimeIdentifierOS(platform);
@@ -190,17 +209,44 @@ namespace GodotTools.Export
"Publish succeeded but project assembly not found in the output directory");
}
- // Add to the exported project shared object list.
+ var manifest = new StringBuilder();
+ // Add to the exported project shared object list or packed resources.
foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
{
- AddSharedObject(file, tags: null,
- Path.Join(projectDataDirName,
- Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file))));
+ if (embedBuildResults)
+ {
+ var filePath = SanitizeSlashes(Path.GetRelativePath(publishOutputTempDir, file));
+ var fileData = File.ReadAllBytes(file);
+ var hash = Convert.ToBase64String(SHA512.HashData(fileData));
+
+ manifest.Append($"{filePath}\t{hash}\n");
+
+ AddFile($"res://.godot/mono/publish/{arch}/{filePath}", fileData, false);
+ }
+ else
+ {
+ AddSharedObject(file, tags: null,
+ Path.Join(projectDataDirName,
+ Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file))));
+ }
+ }
+
+ if (embedBuildResults)
+ {
+ var fileData = Encoding.Default.GetBytes(manifest.ToString());
+ AddFile($"res://.godot/mono/publish/{arch}/.dotnet-publish-manifest", fileData, false);
}
}
}
+ private string SanitizeSlashes(string path)
+ {
+ if (Path.DirectorySeparatorChar == '\\')
+ return path.Replace('\\', '/');
+ return path;
+ }
+
private string DetermineRuntimeIdentifierOS(string platform)
=> OS.DotNetOSPlatformMap[platform];
@@ -214,7 +260,7 @@ namespace GodotTools.Export
"x86_64" => "x64",
"armeabi-v7a" => "arm",
"arm64-v8a" => "arm64",
- "armv7" => "arm",
+ "arm32" => "arm",
"arm64" => "arm64",
_ => throw new ArgumentOutOfRangeException(nameof(arch), arch, "Unexpected architecture")
};
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
index 5bce66ea87..44b1c2554c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -10,301 +10,301 @@ namespace Godot
{
// Color names and values are derived from core/math/color_names.inc
internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> {
- { "ALICEBLUE", new Color(0xF0F8FFFF) },
- { "ANTIQUEWHITE", new Color(0xFAEBD7FF) },
- { "AQUA", new Color(0x00FFFFFF) },
- { "AQUAMARINE", new Color(0x7FFFD4FF) },
- { "AZURE", new Color(0xF0FFFFFF) },
- { "BEIGE", new Color(0xF5F5DCFF) },
- { "BISQUE", new Color(0xFFE4C4FF) },
- { "BLACK", new Color(0x000000FF) },
- { "BLANCHEDALMOND", new Color(0xFFEBCDFF) },
- { "BLUE", new Color(0x0000FFFF) },
- { "BLUEVIOLET", new Color(0x8A2BE2FF) },
- { "BROWN", new Color(0xA52A2AFF) },
- { "BURLYWOOD", new Color(0xDEB887FF) },
- { "CADETBLUE", new Color(0x5F9EA0FF) },
- { "CHARTREUSE", new Color(0x7FFF00FF) },
- { "CHOCOLATE", new Color(0xD2691EFF) },
- { "CORAL", new Color(0xFF7F50FF) },
- { "CORNFLOWERBLUE", new Color(0x6495EDFF) },
- { "CORNSILK", new Color(0xFFF8DCFF) },
- { "CRIMSON", new Color(0xDC143CFF) },
- { "CYAN", new Color(0x00FFFFFF) },
- { "DARKBLUE", new Color(0x00008BFF) },
- { "DARKCYAN", new Color(0x008B8BFF) },
- { "DARKGOLDENROD", new Color(0xB8860BFF) },
- { "DARKGRAY", new Color(0xA9A9A9FF) },
- { "DARKGREEN", new Color(0x006400FF) },
- { "DARKKHAKI", new Color(0xBDB76BFF) },
- { "DARKMAGENTA", new Color(0x8B008BFF) },
- { "DARKOLIVEGREEN", new Color(0x556B2FFF) },
- { "DARKORANGE", new Color(0xFF8C00FF) },
- { "DARKORCHID", new Color(0x9932CCFF) },
- { "DARKRED", new Color(0x8B0000FF) },
- { "DARKSALMON", new Color(0xE9967AFF) },
- { "DARKSEAGREEN", new Color(0x8FBC8FFF) },
- { "DARKSLATEBLUE", new Color(0x483D8BFF) },
- { "DARKSLATEGRAY", new Color(0x2F4F4FFF) },
- { "DARKTURQUOISE", new Color(0x00CED1FF) },
- { "DARKVIOLET", new Color(0x9400D3FF) },
- { "DEEPPINK", new Color(0xFF1493FF) },
- { "DEEPSKYBLUE", new Color(0x00BFFFFF) },
- { "DIMGRAY", new Color(0x696969FF) },
- { "DODGERBLUE", new Color(0x1E90FFFF) },
- { "FIREBRICK", new Color(0xB22222FF) },
- { "FLORALWHITE", new Color(0xFFFAF0FF) },
- { "FORESTGREEN", new Color(0x228B22FF) },
- { "FUCHSIA", new Color(0xFF00FFFF) },
- { "GAINSBORO", new Color(0xDCDCDCFF) },
- { "GHOSTWHITE", new Color(0xF8F8FFFF) },
- { "GOLD", new Color(0xFFD700FF) },
- { "GOLDENROD", new Color(0xDAA520FF) },
- { "GRAY", new Color(0xBEBEBEFF) },
- { "GREEN", new Color(0x00FF00FF) },
- { "GREENYELLOW", new Color(0xADFF2FFF) },
- { "HONEYDEW", new Color(0xF0FFF0FF) },
- { "HOTPINK", new Color(0xFF69B4FF) },
- { "INDIANRED", new Color(0xCD5C5CFF) },
- { "INDIGO", new Color(0x4B0082FF) },
- { "IVORY", new Color(0xFFFFF0FF) },
- { "KHAKI", new Color(0xF0E68CFF) },
- { "LAVENDER", new Color(0xE6E6FAFF) },
- { "LAVENDERBLUSH", new Color(0xFFF0F5FF) },
- { "LAWNGREEN", new Color(0x7CFC00FF) },
- { "LEMONCHIFFON", new Color(0xFFFACDFF) },
- { "LIGHTBLUE", new Color(0xADD8E6FF) },
- { "LIGHTCORAL", new Color(0xF08080FF) },
- { "LIGHTCYAN", new Color(0xE0FFFFFF) },
- { "LIGHTGOLDENROD", new Color(0xFAFAD2FF) },
- { "LIGHTGRAY", new Color(0xD3D3D3FF) },
- { "LIGHTGREEN", new Color(0x90EE90FF) },
- { "LIGHTPINK", new Color(0xFFB6C1FF) },
- { "LIGHTSALMON", new Color(0xFFA07AFF) },
- { "LIGHTSEAGREEN", new Color(0x20B2AAFF) },
- { "LIGHTSKYBLUE", new Color(0x87CEFAFF) },
- { "LIGHTSLATEGRAY", new Color(0x778899FF) },
- { "LIGHTSTEELBLUE", new Color(0xB0C4DEFF) },
- { "LIGHTYELLOW", new Color(0xFFFFE0FF) },
- { "LIME", new Color(0x00FF00FF) },
- { "LIMEGREEN", new Color(0x32CD32FF) },
- { "LINEN", new Color(0xFAF0E6FF) },
- { "MAGENTA", new Color(0xFF00FFFF) },
- { "MAROON", new Color(0xB03060FF) },
- { "MEDIUMAQUAMARINE", new Color(0x66CDAAFF) },
- { "MEDIUMBLUE", new Color(0x0000CDFF) },
- { "MEDIUMORCHID", new Color(0xBA55D3FF) },
- { "MEDIUMPURPLE", new Color(0x9370DBFF) },
- { "MEDIUMSEAGREEN", new Color(0x3CB371FF) },
- { "MEDIUMSLATEBLUE", new Color(0x7B68EEFF) },
- { "MEDIUMSPRINGGREEN", new Color(0x00FA9AFF) },
- { "MEDIUMTURQUOISE", new Color(0x48D1CCFF) },
- { "MEDIUMVIOLETRED", new Color(0xC71585FF) },
- { "MIDNIGHTBLUE", new Color(0x191970FF) },
- { "MINTCREAM", new Color(0xF5FFFAFF) },
- { "MISTYROSE", new Color(0xFFE4E1FF) },
- { "MOCCASIN", new Color(0xFFE4B5FF) },
- { "NAVAJOWHITE", new Color(0xFFDEADFF) },
- { "NAVYBLUE", new Color(0x000080FF) },
- { "OLDLACE", new Color(0xFDF5E6FF) },
- { "OLIVE", new Color(0x808000FF) },
- { "OLIVEDRAB", new Color(0x6B8E23FF) },
- { "ORANGE", new Color(0xFFA500FF) },
- { "ORANGERED", new Color(0xFF4500FF) },
- { "ORCHID", new Color(0xDA70D6FF) },
- { "PALEGOLDENROD", new Color(0xEEE8AAFF) },
- { "PALEGREEN", new Color(0x98FB98FF) },
- { "PALETURQUOISE", new Color(0xAFEEEEFF) },
- { "PALEVIOLETRED", new Color(0xDB7093FF) },
- { "PAPAYAWHIP", new Color(0xFFEFD5FF) },
- { "PEACHPUFF", new Color(0xFFDAB9FF) },
- { "PERU", new Color(0xCD853FFF) },
- { "PINK", new Color(0xFFC0CBFF) },
- { "PLUM", new Color(0xDDA0DDFF) },
- { "POWDERBLUE", new Color(0xB0E0E6FF) },
- { "PURPLE", new Color(0xA020F0FF) },
- { "REBECCAPURPLE", new Color(0x663399FF) },
- { "RED", new Color(0xFF0000FF) },
- { "ROSYBROWN", new Color(0xBC8F8FFF) },
- { "ROYALBLUE", new Color(0x4169E1FF) },
- { "SADDLEBROWN", new Color(0x8B4513FF) },
- { "SALMON", new Color(0xFA8072FF) },
- { "SANDYBROWN", new Color(0xF4A460FF) },
- { "SEAGREEN", new Color(0x2E8B57FF) },
- { "SEASHELL", new Color(0xFFF5EEFF) },
- { "SIENNA", new Color(0xA0522DFF) },
- { "SILVER", new Color(0xC0C0C0FF) },
- { "SKYBLUE", new Color(0x87CEEBFF) },
- { "SLATEBLUE", new Color(0x6A5ACDFF) },
- { "SLATEGRAY", new Color(0x708090FF) },
- { "SNOW", new Color(0xFFFAFAFF) },
- { "SPRINGGREEN", new Color(0x00FF7FFF) },
- { "STEELBLUE", new Color(0x4682B4FF) },
- { "TAN", new Color(0xD2B48CFF) },
- { "TEAL", new Color(0x008080FF) },
- { "THISTLE", new Color(0xD8BFD8FF) },
- { "TOMATO", new Color(0xFF6347FF) },
- { "TRANSPARENT", new Color(0xFFFFFF00) },
- { "TURQUOISE", new Color(0x40E0D0FF) },
- { "VIOLET", new Color(0xEE82EEFF) },
- { "WEBGRAY", new Color(0x808080FF) },
- { "WEBGREEN", new Color(0x008000FF) },
- { "WEBMAROON", new Color(0x800000FF) },
- { "WEBPURPLE", new Color(0x800080FF) },
- { "WHEAT", new Color(0xF5DEB3FF) },
- { "WHITE", new Color(0xFFFFFFFF) },
- { "WHITESMOKE", new Color(0xF5F5F5FF) },
- { "YELLOW", new Color(0xFFFF00FF) },
- { "YELLOWGREEN", new Color(0x9ACD32FF) },
+ { "ALICEBLUE", Colors.AliceBlue },
+ { "ANTIQUEWHITE", Colors.AntiqueWhite },
+ { "AQUA", Colors.Aqua },
+ { "AQUAMARINE", Colors.Aquamarine },
+ { "AZURE", Colors.Azure },
+ { "BEIGE", Colors.Beige },
+ { "BISQUE", Colors.Bisque },
+ { "BLACK", Colors.Black },
+ { "BLANCHEDALMOND", Colors.BlanchedAlmond },
+ { "BLUE", Colors.Blue },
+ { "BLUEVIOLET", Colors.BlueViolet },
+ { "BROWN", Colors.Brown },
+ { "BURLYWOOD", Colors.Burlywood },
+ { "CADETBLUE", Colors.CadetBlue },
+ { "CHARTREUSE", Colors.Chartreuse },
+ { "CHOCOLATE", Colors.Chocolate },
+ { "CORAL", Colors.Coral },
+ { "CORNFLOWERBLUE", Colors.CornflowerBlue },
+ { "CORNSILK", Colors.Cornsilk },
+ { "CRIMSON", Colors.Crimson },
+ { "CYAN", Colors.Cyan },
+ { "DARKBLUE", Colors.DarkBlue },
+ { "DARKCYAN", Colors.DarkCyan },
+ { "DARKGOLDENROD", Colors.DarkGoldenrod },
+ { "DARKGRAY", Colors.DarkGray },
+ { "DARKGREEN", Colors.DarkGreen },
+ { "DARKKHAKI", Colors.DarkKhaki },
+ { "DARKMAGENTA", Colors.DarkMagenta },
+ { "DARKOLIVEGREEN", Colors.DarkOliveGreen },
+ { "DARKORANGE", Colors.DarkOrange },
+ { "DARKORCHID", Colors.DarkOrchid },
+ { "DARKRED", Colors.DarkRed },
+ { "DARKSALMON", Colors.DarkSalmon },
+ { "DARKSEAGREEN", Colors.DarkSeaGreen },
+ { "DARKSLATEBLUE", Colors.DarkSlateBlue },
+ { "DARKSLATEGRAY", Colors.DarkSlateGray },
+ { "DARKTURQUOISE", Colors.DarkTurquoise },
+ { "DARKVIOLET", Colors.DarkViolet },
+ { "DEEPPINK", Colors.DeepPink },
+ { "DEEPSKYBLUE", Colors.DeepSkyBlue },
+ { "DIMGRAY", Colors.DimGray },
+ { "DODGERBLUE", Colors.DodgerBlue },
+ { "FIREBRICK", Colors.Firebrick },
+ { "FLORALWHITE", Colors.FloralWhite },
+ { "FORESTGREEN", Colors.ForestGreen },
+ { "FUCHSIA", Colors.Fuchsia },
+ { "GAINSBORO", Colors.Gainsboro },
+ { "GHOSTWHITE", Colors.GhostWhite },
+ { "GOLD", Colors.Gold },
+ { "GOLDENROD", Colors.Goldenrod },
+ { "GRAY", Colors.Gray },
+ { "GREEN", Colors.Green },
+ { "GREENYELLOW", Colors.GreenYellow },
+ { "HONEYDEW", Colors.Honeydew },
+ { "HOTPINK", Colors.HotPink },
+ { "INDIANRED", Colors.IndianRed },
+ { "INDIGO", Colors.Indigo },
+ { "IVORY", Colors.Ivory },
+ { "KHAKI", Colors.Khaki },
+ { "LAVENDER", Colors.Lavender },
+ { "LAVENDERBLUSH", Colors.LavenderBlush },
+ { "LAWNGREEN", Colors.LawnGreen },
+ { "LEMONCHIFFON", Colors.LemonChiffon },
+ { "LIGHTBLUE", Colors.LightBlue },
+ { "LIGHTCORAL", Colors.LightCoral },
+ { "LIGHTCYAN", Colors.LightCyan },
+ { "LIGHTGOLDENROD", Colors.LightGoldenrod },
+ { "LIGHTGRAY", Colors.LightGray },
+ { "LIGHTGREEN", Colors.LightGreen },
+ { "LIGHTPINK", Colors.LightPink },
+ { "LIGHTSALMON", Colors.LightSalmon },
+ { "LIGHTSEAGREEN", Colors.LightSeaGreen },
+ { "LIGHTSKYBLUE", Colors.LightSkyBlue },
+ { "LIGHTSLATEGRAY", Colors.LightSlateGray },
+ { "LIGHTSTEELBLUE", Colors.LightSteelBlue },
+ { "LIGHTYELLOW", Colors.LightYellow },
+ { "LIME", Colors.Lime },
+ { "LIMEGREEN", Colors.LimeGreen },
+ { "LINEN", Colors.Linen },
+ { "MAGENTA", Colors.Magenta },
+ { "MAROON", Colors.Maroon },
+ { "MEDIUMAQUAMARINE", Colors.MediumAquamarine },
+ { "MEDIUMBLUE", Colors.MediumBlue },
+ { "MEDIUMORCHID", Colors.MediumOrchid },
+ { "MEDIUMPURPLE", Colors.MediumPurple },
+ { "MEDIUMSEAGREEN", Colors.MediumSeaGreen },
+ { "MEDIUMSLATEBLUE", Colors.MediumSlateBlue },
+ { "MEDIUMSPRINGGREEN", Colors.MediumSpringGreen },
+ { "MEDIUMTURQUOISE", Colors.MediumTurquoise },
+ { "MEDIUMVIOLETRED", Colors.MediumVioletRed },
+ { "MIDNIGHTBLUE", Colors.MidnightBlue },
+ { "MINTCREAM", Colors.MintCream },
+ { "MISTYROSE", Colors.MistyRose },
+ { "MOCCASIN", Colors.Moccasin },
+ { "NAVAJOWHITE", Colors.NavajoWhite },
+ { "NAVYBLUE", Colors.NavyBlue },
+ { "OLDLACE", Colors.OldLace },
+ { "OLIVE", Colors.Olive },
+ { "OLIVEDRAB", Colors.OliveDrab },
+ { "ORANGE", Colors.Orange },
+ { "ORANGERED", Colors.OrangeRed },
+ { "ORCHID", Colors.Orchid },
+ { "PALEGOLDENROD", Colors.PaleGoldenrod },
+ { "PALEGREEN", Colors.PaleGreen },
+ { "PALETURQUOISE", Colors.PaleTurquoise },
+ { "PALEVIOLETRED", Colors.PaleVioletRed },
+ { "PAPAYAWHIP", Colors.PapayaWhip },
+ { "PEACHPUFF", Colors.PeachPuff },
+ { "PERU", Colors.Peru },
+ { "PINK", Colors.Pink },
+ { "PLUM", Colors.Plum },
+ { "POWDERBLUE", Colors.PowderBlue },
+ { "PURPLE", Colors.Purple },
+ { "REBECCAPURPLE", Colors.RebeccaPurple },
+ { "RED", Colors.Red },
+ { "ROSYBROWN", Colors.RosyBrown },
+ { "ROYALBLUE", Colors.RoyalBlue },
+ { "SADDLEBROWN", Colors.SaddleBrown },
+ { "SALMON", Colors.Salmon },
+ { "SANDYBROWN", Colors.SandyBrown },
+ { "SEAGREEN", Colors.SeaGreen },
+ { "SEASHELL", Colors.Seashell },
+ { "SIENNA", Colors.Sienna },
+ { "SILVER", Colors.Silver },
+ { "SKYBLUE", Colors.SkyBlue },
+ { "SLATEBLUE", Colors.SlateBlue },
+ { "SLATEGRAY", Colors.SlateGray },
+ { "SNOW", Colors.Snow },
+ { "SPRINGGREEN", Colors.SpringGreen },
+ { "STEELBLUE", Colors.SteelBlue },
+ { "TAN", Colors.Tan },
+ { "TEAL", Colors.Teal },
+ { "THISTLE", Colors.Thistle },
+ { "TOMATO", Colors.Tomato },
+ { "TRANSPARENT", Colors.Transparent },
+ { "TURQUOISE", Colors.Turquoise },
+ { "VIOLET", Colors.Violet },
+ { "WEBGRAY", Colors.WebGray },
+ { "WEBGREEN", Colors.WebGreen },
+ { "WEBMAROON", Colors.WebMaroon },
+ { "WEBPURPLE", Colors.WebPurple },
+ { "WHEAT", Colors.Wheat },
+ { "WHITE", Colors.White },
+ { "WHITESMOKE", Colors.WhiteSmoke },
+ { "YELLOW", Colors.Yellow },
+ { "YELLOWGREEN", Colors.YellowGreen },
};
#pragma warning disable CS1591 // Disable warning: "Missing XML comment for publicly visible type or member"
- public static Color AliceBlue { get { return namedColors["ALICEBLUE"]; } }
- public static Color AntiqueWhite { get { return namedColors["ANTIQUEWHITE"]; } }
- public static Color Aqua { get { return namedColors["AQUA"]; } }
- public static Color Aquamarine { get { return namedColors["AQUAMARINE"]; } }
- public static Color Azure { get { return namedColors["AZURE"]; } }
- public static Color Beige { get { return namedColors["BEIGE"]; } }
- public static Color Bisque { get { return namedColors["BISQUE"]; } }
- public static Color Black { get { return namedColors["BLACK"]; } }
- public static Color BlanchedAlmond { get { return namedColors["BLANCHEDALMOND"]; } }
- public static Color Blue { get { return namedColors["BLUE"]; } }
- public static Color BlueViolet { get { return namedColors["BLUEVIOLET"]; } }
- public static Color Brown { get { return namedColors["BROWN"]; } }
- public static Color Burlywood { get { return namedColors["BURLYWOOD"]; } }
- public static Color CadetBlue { get { return namedColors["CADETBLUE"]; } }
- public static Color Chartreuse { get { return namedColors["CHARTREUSE"]; } }
- public static Color Chocolate { get { return namedColors["CHOCOLATE"]; } }
- public static Color Coral { get { return namedColors["CORAL"]; } }
- public static Color CornflowerBlue { get { return namedColors["CORNFLOWERBLUE"]; } }
- public static Color Cornsilk { get { return namedColors["CORNSILK"]; } }
- public static Color Crimson { get { return namedColors["CRIMSON"]; } }
- public static Color Cyan { get { return namedColors["CYAN"]; } }
- public static Color DarkBlue { get { return namedColors["DARKBLUE"]; } }
- public static Color DarkCyan { get { return namedColors["DARKCYAN"]; } }
- public static Color DarkGoldenrod { get { return namedColors["DARKGOLDENROD"]; } }
- public static Color DarkGray { get { return namedColors["DARKGRAY"]; } }
- public static Color DarkGreen { get { return namedColors["DARKGREEN"]; } }
- public static Color DarkKhaki { get { return namedColors["DARKKHAKI"]; } }
- public static Color DarkMagenta { get { return namedColors["DARKMAGENTA"]; } }
- public static Color DarkOliveGreen { get { return namedColors["DARKOLIVEGREEN"]; } }
- public static Color DarkOrange { get { return namedColors["DARKORANGE"]; } }
- public static Color DarkOrchid { get { return namedColors["DARKORCHID"]; } }
- public static Color DarkRed { get { return namedColors["DARKRED"]; } }
- public static Color DarkSalmon { get { return namedColors["DARKSALMON"]; } }
- public static Color DarkSeaGreen { get { return namedColors["DARKSEAGREEN"]; } }
- public static Color DarkSlateBlue { get { return namedColors["DARKSLATEBLUE"]; } }
- public static Color DarkSlateGray { get { return namedColors["DARKSLATEGRAY"]; } }
- public static Color DarkTurquoise { get { return namedColors["DARKTURQUOISE"]; } }
- public static Color DarkViolet { get { return namedColors["DARKVIOLET"]; } }
- public static Color DeepPink { get { return namedColors["DEEPPINK"]; } }
- public static Color DeepSkyBlue { get { return namedColors["DEEPSKYBLUE"]; } }
- public static Color DimGray { get { return namedColors["DIMGRAY"]; } }
- public static Color DodgerBlue { get { return namedColors["DODGERBLUE"]; } }
- public static Color Firebrick { get { return namedColors["FIREBRICK"]; } }
- public static Color FloralWhite { get { return namedColors["FLORALWHITE"]; } }
- public static Color ForestGreen { get { return namedColors["FORESTGREEN"]; } }
- public static Color Fuchsia { get { return namedColors["FUCHSIA"]; } }
- public static Color Gainsboro { get { return namedColors["GAINSBORO"]; } }
- public static Color GhostWhite { get { return namedColors["GHOSTWHITE"]; } }
- public static Color Gold { get { return namedColors["GOLD"]; } }
- public static Color Goldenrod { get { return namedColors["GOLDENROD"]; } }
- public static Color Gray { get { return namedColors["GRAY"]; } }
- public static Color Green { get { return namedColors["GREEN"]; } }
- public static Color GreenYellow { get { return namedColors["GREENYELLOW"]; } }
- public static Color Honeydew { get { return namedColors["HONEYDEW"]; } }
- public static Color HotPink { get { return namedColors["HOTPINK"]; } }
- public static Color IndianRed { get { return namedColors["INDIANRED"]; } }
- public static Color Indigo { get { return namedColors["INDIGO"]; } }
- public static Color Ivory { get { return namedColors["IVORY"]; } }
- public static Color Khaki { get { return namedColors["KHAKI"]; } }
- public static Color Lavender { get { return namedColors["LAVENDER"]; } }
- public static Color LavenderBlush { get { return namedColors["LAVENDERBLUSH"]; } }
- public static Color LawnGreen { get { return namedColors["LAWNGREEN"]; } }
- public static Color LemonChiffon { get { return namedColors["LEMONCHIFFON"]; } }
- public static Color LightBlue { get { return namedColors["LIGHTBLUE"]; } }
- public static Color LightCoral { get { return namedColors["LIGHTCORAL"]; } }
- public static Color LightCyan { get { return namedColors["LIGHTCYAN"]; } }
- public static Color LightGoldenrod { get { return namedColors["LIGHTGOLDENROD"]; } }
- public static Color LightGray { get { return namedColors["LIGHTGRAY"]; } }
- public static Color LightGreen { get { return namedColors["LIGHTGREEN"]; } }
- public static Color LightPink { get { return namedColors["LIGHTPINK"]; } }
- public static Color LightSalmon { get { return namedColors["LIGHTSALMON"]; } }
- public static Color LightSeaGreen { get { return namedColors["LIGHTSEAGREEN"]; } }
- public static Color LightSkyBlue { get { return namedColors["LIGHTSKYBLUE"]; } }
- public static Color LightSlateGray { get { return namedColors["LIGHTSLATEGRAY"]; } }
- public static Color LightSteelBlue { get { return namedColors["LIGHTSTEELBLUE"]; } }
- public static Color LightYellow { get { return namedColors["LIGHTYELLOW"]; } }
- public static Color Lime { get { return namedColors["LIME"]; } }
- public static Color LimeGreen { get { return namedColors["LIMEGREEN"]; } }
- public static Color Linen { get { return namedColors["LINEN"]; } }
- public static Color Magenta { get { return namedColors["MAGENTA"]; } }
- public static Color Maroon { get { return namedColors["MAROON"]; } }
- public static Color MediumAquamarine { get { return namedColors["MEDIUMAQUAMARINE"]; } }
- public static Color MediumBlue { get { return namedColors["MEDIUMBLUE"]; } }
- public static Color MediumOrchid { get { return namedColors["MEDIUMORCHID"]; } }
- public static Color MediumPurple { get { return namedColors["MEDIUMPURPLE"]; } }
- public static Color MediumSeaGreen { get { return namedColors["MEDIUMSEAGREEN"]; } }
- public static Color MediumSlateBlue { get { return namedColors["MEDIUMSLATEBLUE"]; } }
- public static Color MediumSpringGreen { get { return namedColors["MEDIUMSPRINGGREEN"]; } }
- public static Color MediumTurquoise { get { return namedColors["MEDIUMTURQUOISE"]; } }
- public static Color MediumVioletRed { get { return namedColors["MEDIUMVIOLETRED"]; } }
- public static Color MidnightBlue { get { return namedColors["MIDNIGHTBLUE"]; } }
- public static Color MintCream { get { return namedColors["MINTCREAM"]; } }
- public static Color MistyRose { get { return namedColors["MISTYROSE"]; } }
- public static Color Moccasin { get { return namedColors["MOCCASIN"]; } }
- public static Color NavajoWhite { get { return namedColors["NAVAJOWHITE"]; } }
- public static Color NavyBlue { get { return namedColors["NAVYBLUE"]; } }
- public static Color OldLace { get { return namedColors["OLDLACE"]; } }
- public static Color Olive { get { return namedColors["OLIVE"]; } }
- public static Color OliveDrab { get { return namedColors["OLIVEDRAB"]; } }
- public static Color Orange { get { return namedColors["ORANGE"]; } }
- public static Color OrangeRed { get { return namedColors["ORANGERED"]; } }
- public static Color Orchid { get { return namedColors["ORCHID"]; } }
- public static Color PaleGoldenrod { get { return namedColors["PALEGOLDENROD"]; } }
- public static Color PaleGreen { get { return namedColors["PALEGREEN"]; } }
- public static Color PaleTurquoise { get { return namedColors["PALETURQUOISE"]; } }
- public static Color PaleVioletRed { get { return namedColors["PALEVIOLETRED"]; } }
- public static Color PapayaWhip { get { return namedColors["PAPAYAWHIP"]; } }
- public static Color PeachPuff { get { return namedColors["PEACHPUFF"]; } }
- public static Color Peru { get { return namedColors["PERU"]; } }
- public static Color Pink { get { return namedColors["PINK"]; } }
- public static Color Plum { get { return namedColors["PLUM"]; } }
- public static Color PowderBlue { get { return namedColors["POWDERBLUE"]; } }
- public static Color Purple { get { return namedColors["PURPLE"]; } }
- public static Color RebeccaPurple { get { return namedColors["REBECCAPURPLE"]; } }
- public static Color Red { get { return namedColors["RED"]; } }
- public static Color RosyBrown { get { return namedColors["ROSYBROWN"]; } }
- public static Color RoyalBlue { get { return namedColors["ROYALBLUE"]; } }
- public static Color SaddleBrown { get { return namedColors["SADDLEBROWN"]; } }
- public static Color Salmon { get { return namedColors["SALMON"]; } }
- public static Color SandyBrown { get { return namedColors["SANDYBROWN"]; } }
- public static Color SeaGreen { get { return namedColors["SEAGREEN"]; } }
- public static Color Seashell { get { return namedColors["SEASHELL"]; } }
- public static Color Sienna { get { return namedColors["SIENNA"]; } }
- public static Color Silver { get { return namedColors["SILVER"]; } }
- public static Color SkyBlue { get { return namedColors["SKYBLUE"]; } }
- public static Color SlateBlue { get { return namedColors["SLATEBLUE"]; } }
- public static Color SlateGray { get { return namedColors["SLATEGRAY"]; } }
- public static Color Snow { get { return namedColors["SNOW"]; } }
- public static Color SpringGreen { get { return namedColors["SPRINGGREEN"]; } }
- public static Color SteelBlue { get { return namedColors["STEELBLUE"]; } }
- public static Color Tan { get { return namedColors["TAN"]; } }
- public static Color Teal { get { return namedColors["TEAL"]; } }
- public static Color Thistle { get { return namedColors["THISTLE"]; } }
- public static Color Tomato { get { return namedColors["TOMATO"]; } }
- public static Color Transparent { get { return namedColors["TRANSPARENT"]; } }
- public static Color Turquoise { get { return namedColors["TURQUOISE"]; } }
- public static Color Violet { get { return namedColors["VIOLET"]; } }
- public static Color WebGray { get { return namedColors["WEBGRAY"]; } }
- public static Color WebGreen { get { return namedColors["WEBGREEN"]; } }
- public static Color WebMaroon { get { return namedColors["WEBMAROON"]; } }
- public static Color WebPurple { get { return namedColors["WEBPURPLE"]; } }
- public static Color Wheat { get { return namedColors["WHEAT"]; } }
- public static Color White { get { return namedColors["WHITE"]; } }
- public static Color WhiteSmoke { get { return namedColors["WHITESMOKE"]; } }
- public static Color Yellow { get { return namedColors["YELLOW"]; } }
- public static Color YellowGreen { get { return namedColors["YELLOWGREEN"]; } }
+ public static Color AliceBlue => new Color(0xF0F8FFFF);
+ public static Color AntiqueWhite => new Color(0xFAEBD7FF);
+ public static Color Aqua => new Color(0x00FFFFFF);
+ public static Color Aquamarine => new Color(0x7FFFD4FF);
+ public static Color Azure => new Color(0xF0FFFFFF);
+ public static Color Beige => new Color(0xF5F5DCFF);
+ public static Color Bisque => new Color(0xFFE4C4FF);
+ public static Color Black => new Color(0x000000FF);
+ public static Color BlanchedAlmond => new Color(0xFFEBCDFF);
+ public static Color Blue => new Color(0x0000FFFF);
+ public static Color BlueViolet => new Color(0x8A2BE2FF);
+ public static Color Brown => new Color(0xA52A2AFF);
+ public static Color Burlywood => new Color(0xDEB887FF);
+ public static Color CadetBlue => new Color(0x5F9EA0FF);
+ public static Color Chartreuse => new Color(0x7FFF00FF);
+ public static Color Chocolate => new Color(0xD2691EFF);
+ public static Color Coral => new Color(0xFF7F50FF);
+ public static Color CornflowerBlue => new Color(0x6495EDFF);
+ public static Color Cornsilk => new Color(0xFFF8DCFF);
+ public static Color Crimson => new Color(0xDC143CFF);
+ public static Color Cyan => new Color(0x00FFFFFF);
+ public static Color DarkBlue => new Color(0x00008BFF);
+ public static Color DarkCyan => new Color(0x008B8BFF);
+ public static Color DarkGoldenrod => new Color(0xB8860BFF);
+ public static Color DarkGray => new Color(0xA9A9A9FF);
+ public static Color DarkGreen => new Color(0x006400FF);
+ public static Color DarkKhaki => new Color(0xBDB76BFF);
+ public static Color DarkMagenta => new Color(0x8B008BFF);
+ public static Color DarkOliveGreen => new Color(0x556B2FFF);
+ public static Color DarkOrange => new Color(0xFF8C00FF);
+ public static Color DarkOrchid => new Color(0x9932CCFF);
+ public static Color DarkRed => new Color(0x8B0000FF);
+ public static Color DarkSalmon => new Color(0xE9967AFF);
+ public static Color DarkSeaGreen => new Color(0x8FBC8FFF);
+ public static Color DarkSlateBlue => new Color(0x483D8BFF);
+ public static Color DarkSlateGray => new Color(0x2F4F4FFF);
+ public static Color DarkTurquoise => new Color(0x00CED1FF);
+ public static Color DarkViolet => new Color(0x9400D3FF);
+ public static Color DeepPink => new Color(0xFF1493FF);
+ public static Color DeepSkyBlue => new Color(0x00BFFFFF);
+ public static Color DimGray => new Color(0x696969FF);
+ public static Color DodgerBlue => new Color(0x1E90FFFF);
+ public static Color Firebrick => new Color(0xB22222FF);
+ public static Color FloralWhite => new Color(0xFFFAF0FF);
+ public static Color ForestGreen => new Color(0x228B22FF);
+ public static Color Fuchsia => new Color(0xFF00FFFF);
+ public static Color Gainsboro => new Color(0xDCDCDCFF);
+ public static Color GhostWhite => new Color(0xF8F8FFFF);
+ public static Color Gold => new Color(0xFFD700FF);
+ public static Color Goldenrod => new Color(0xDAA520FF);
+ public static Color Gray => new Color(0xBEBEBEFF);
+ public static Color Green => new Color(0x00FF00FF);
+ public static Color GreenYellow => new Color(0xADFF2FFF);
+ public static Color Honeydew => new Color(0xF0FFF0FF);
+ public static Color HotPink => new Color(0xFF69B4FF);
+ public static Color IndianRed => new Color(0xCD5C5CFF);
+ public static Color Indigo => new Color(0x4B0082FF);
+ public static Color Ivory => new Color(0xFFFFF0FF);
+ public static Color Khaki => new Color(0xF0E68CFF);
+ public static Color Lavender => new Color(0xE6E6FAFF);
+ public static Color LavenderBlush => new Color(0xFFF0F5FF);
+ public static Color LawnGreen => new Color(0x7CFC00FF);
+ public static Color LemonChiffon => new Color(0xFFFACDFF);
+ public static Color LightBlue => new Color(0xADD8E6FF);
+ public static Color LightCoral => new Color(0xF08080FF);
+ public static Color LightCyan => new Color(0xE0FFFFFF);
+ public static Color LightGoldenrod => new Color(0xFAFAD2FF);
+ public static Color LightGray => new Color(0xD3D3D3FF);
+ public static Color LightGreen => new Color(0x90EE90FF);
+ public static Color LightPink => new Color(0xFFB6C1FF);
+ public static Color LightSalmon => new Color(0xFFA07AFF);
+ public static Color LightSeaGreen => new Color(0x20B2AAFF);
+ public static Color LightSkyBlue => new Color(0x87CEFAFF);
+ public static Color LightSlateGray => new Color(0x778899FF);
+ public static Color LightSteelBlue => new Color(0xB0C4DEFF);
+ public static Color LightYellow => new Color(0xFFFFE0FF);
+ public static Color Lime => new Color(0x00FF00FF);
+ public static Color LimeGreen => new Color(0x32CD32FF);
+ public static Color Linen => new Color(0xFAF0E6FF);
+ public static Color Magenta => new Color(0xFF00FFFF);
+ public static Color Maroon => new Color(0xB03060FF);
+ public static Color MediumAquamarine => new Color(0x66CDAAFF);
+ public static Color MediumBlue => new Color(0x0000CDFF);
+ public static Color MediumOrchid => new Color(0xBA55D3FF);
+ public static Color MediumPurple => new Color(0x9370DBFF);
+ public static Color MediumSeaGreen => new Color(0x3CB371FF);
+ public static Color MediumSlateBlue => new Color(0x7B68EEFF);
+ public static Color MediumSpringGreen => new Color(0x00FA9AFF);
+ public static Color MediumTurquoise => new Color(0x48D1CCFF);
+ public static Color MediumVioletRed => new Color(0xC71585FF);
+ public static Color MidnightBlue => new Color(0x191970FF);
+ public static Color MintCream => new Color(0xF5FFFAFF);
+ public static Color MistyRose => new Color(0xFFE4E1FF);
+ public static Color Moccasin => new Color(0xFFE4B5FF);
+ public static Color NavajoWhite => new Color(0xFFDEADFF);
+ public static Color NavyBlue => new Color(0x000080FF);
+ public static Color OldLace => new Color(0xFDF5E6FF);
+ public static Color Olive => new Color(0x808000FF);
+ public static Color OliveDrab => new Color(0x6B8E23FF);
+ public static Color Orange => new Color(0xFFA500FF);
+ public static Color OrangeRed => new Color(0xFF4500FF);
+ public static Color Orchid => new Color(0xDA70D6FF);
+ public static Color PaleGoldenrod => new Color(0xEEE8AAFF);
+ public static Color PaleGreen => new Color(0x98FB98FF);
+ public static Color PaleTurquoise => new Color(0xAFEEEEFF);
+ public static Color PaleVioletRed => new Color(0xDB7093FF);
+ public static Color PapayaWhip => new Color(0xFFEFD5FF);
+ public static Color PeachPuff => new Color(0xFFDAB9FF);
+ public static Color Peru => new Color(0xCD853FFF);
+ public static Color Pink => new Color(0xFFC0CBFF);
+ public static Color Plum => new Color(0xDDA0DDFF);
+ public static Color PowderBlue => new Color(0xB0E0E6FF);
+ public static Color Purple => new Color(0xA020F0FF);
+ public static Color RebeccaPurple => new Color(0x663399FF);
+ public static Color Red => new Color(0xFF0000FF);
+ public static Color RosyBrown => new Color(0xBC8F8FFF);
+ public static Color RoyalBlue => new Color(0x4169E1FF);
+ public static Color SaddleBrown => new Color(0x8B4513FF);
+ public static Color Salmon => new Color(0xFA8072FF);
+ public static Color SandyBrown => new Color(0xF4A460FF);
+ public static Color SeaGreen => new Color(0x2E8B57FF);
+ public static Color Seashell => new Color(0xFFF5EEFF);
+ public static Color Sienna => new Color(0xA0522DFF);
+ public static Color Silver => new Color(0xC0C0C0FF);
+ public static Color SkyBlue => new Color(0x87CEEBFF);
+ public static Color SlateBlue => new Color(0x6A5ACDFF);
+ public static Color SlateGray => new Color(0x708090FF);
+ public static Color Snow => new Color(0xFFFAFAFF);
+ public static Color SpringGreen => new Color(0x00FF7FFF);
+ public static Color SteelBlue => new Color(0x4682B4FF);
+ public static Color Tan => new Color(0xD2B48CFF);
+ public static Color Teal => new Color(0x008080FF);
+ public static Color Thistle => new Color(0xD8BFD8FF);
+ public static Color Tomato => new Color(0xFF6347FF);
+ public static Color Transparent => new Color(0xFFFFFF00);
+ public static Color Turquoise => new Color(0x40E0D0FF);
+ public static Color Violet => new Color(0xEE82EEFF);
+ public static Color WebGray => new Color(0x808080FF);
+ public static Color WebGreen => new Color(0x008000FF);
+ public static Color WebMaroon => new Color(0x800000FF);
+ public static Color WebPurple => new Color(0x800080FF);
+ public static Color Wheat => new Color(0xF5DEB3FF);
+ public static Color White => new Color(0xFFFFFFFF);
+ public static Color WhiteSmoke => new Color(0xF5F5F5FF);
+ public static Color Yellow => new Color(0xFFFF00FF);
+ public static Color YellowGreen => new Color(0x9ACD32FF);
#pragma warning restore CS1591
}
}
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 159cb91d1b..279b5cfed2 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -141,19 +141,52 @@ private:
#else // TOOLS_ENABLED
String arch = Engine::get_singleton()->get_architecture_name();
String appname_safe = path::get_csharp_project_name();
- String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
- if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = exe_dir.path_join("data_Godot_" + arch);
- }
+ String packed_path = "res://.godot/mono/publish/" + arch;
+ if (DirAccess::exists(packed_path)) {
+ // The dotnet publish data is packed in the pck/zip.
+ String data_dir_root = OS::get_singleton()->get_cache_path().path_join("data_" + appname_safe + "_" + arch);
+ bool has_data = false;
+ if (!has_data) {
+ // 1. Try to access the data directly.
+ String global_packed = ProjectSettings::get_singleton()->globalize_path(packed_path);
+ if (global_packed.is_absolute_path() && FileAccess::exists(global_packed.path_join(".dotnet-publish-manifest"))) {
+ data_dir_root = global_packed;
+ has_data = true;
+ }
+ }
+ if (!has_data) {
+ // 2. Check if the data was extracted before and is up-to-date.
+ String packed_manifest = packed_path.path_join(".dotnet-publish-manifest");
+ String extracted_manifest = data_dir_root.path_join(".dotnet-publish-manifest");
+ if (FileAccess::exists(packed_manifest) && FileAccess::exists(extracted_manifest)) {
+ if (FileAccess::get_file_as_bytes(packed_manifest) == FileAccess::get_file_as_bytes(extracted_manifest)) {
+ has_data = true;
+ }
+ }
+ }
+ if (!has_data) {
+ // 3. Extract the data to a temporary location to load from there.
+ Ref<DirAccess> da = DirAccess::create_for_path(packed_path);
+ ERR_FAIL_NULL(da);
+ ERR_FAIL_COND(da->copy_dir(packed_path, data_dir_root) != OK);
+ }
+ api_assemblies_dir = data_dir_root;
+ } else {
+ // The dotnet publish data is in a directory next to the executable.
+ String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
+ if (!DirAccess::exists(data_dir_root)) {
+ data_dir_root = exe_dir.path_join("data_Godot_" + arch);
+ }
#ifdef MACOS_ENABLED
- if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
- }
- if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = res_dir.path_join("data_Godot_" + arch);
- }
+ if (!DirAccess::exists(data_dir_root)) {
+ data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
+ }
+ if (!DirAccess::exists(data_dir_root)) {
+ data_dir_root = res_dir.path_join("data_Godot_" + arch);
+ }
#endif
- api_assemblies_dir = data_dir_root;
+ api_assemblies_dir = data_dir_root;
+ }
#endif
}
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 4337478370..5e52f25cf4 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -55,9 +55,7 @@
// TODO mobile
#if 0
-#ifdef ANDROID_ENABLED
-#include "support/android_support.h"
-#elif defined(IOS_ENABLED)
+#ifdef IOS_ENABLED
#include "support/ios_support.h"
#endif
#endif
@@ -554,10 +552,6 @@ GDMono::~GDMono() {
finalizing_scripts_domain = false;
runtime_initialized = false;
-#if defined(ANDROID_ENABLED)
- gdmono::android::support::cleanup();
-#endif
-
singleton = nullptr;
}
diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp
deleted file mode 100644
index 14b442516e..0000000000
--- a/modules/mono/mono_gd/support/android_support.cpp
+++ /dev/null
@@ -1,720 +0,0 @@
-/**************************************************************************/
-/* android_support.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 "android_support.h"
-
-#if defined(ANDROID_ENABLED)
-
-#include "../../utils/path_utils.h"
-#include "../../utils/string_utils.h"
-#include "../gd_mono_cache.h"
-#include "../gd_mono_marshal.h"
-
-#include "core/os/os.h"
-#include "core/string/ustring.h"
-
-#include "java_godot_wrapper.h"
-#include "os_android.h"
-#include "thread_jandroid.h"
-
-#include <mono/utils/mono-dl-fallback.h>
-
-#include <dlfcn.h> // dlopen, dlsym
-#include <sys/system_properties.h>
-#include <cstddef>
-
-#if __ANDROID_API__ < 24
-#include "thirdparty/misc/ifaddrs-android.h"
-#else
-#include <ifaddrs.h>
-#endif
-
-// Warning: JNI boilerplate ahead... continue at your own risk
-
-namespace gdmono {
-namespace android {
-namespace support {
-
-template <typename T>
-struct ScopedLocalRef {
- JNIEnv *env;
- T local_ref;
-
- _FORCE_INLINE_ T get() const { return local_ref; }
- _FORCE_INLINE_ operator T() const { return local_ref; }
- _FORCE_INLINE_ operator jvalue() const { return (jvalue)local_ref; }
-
- _FORCE_INLINE_ operator bool() const { return local_ref != nullptr; }
-
- _FORCE_INLINE_ bool operator==(std::nullptr_t) const {
- return local_ref == nullptr;
- }
-
- _FORCE_INLINE_ bool operator!=(std::nullptr_t) const {
- return local_ref != nullptr;
- }
-
- ScopedLocalRef(const ScopedLocalRef &) = delete;
- ScopedLocalRef &operator=(const ScopedLocalRef &) = delete;
-
- ScopedLocalRef(JNIEnv *p_env, T p_local_ref) :
- env(p_env),
- local_ref(p_local_ref) {
- }
-
- ~ScopedLocalRef() {
- if (local_ref) {
- env->DeleteLocalRef(local_ref);
- }
- }
-};
-
-bool jni_exception_check(JNIEnv *p_env) {
- if (p_env->ExceptionCheck()) {
- // Print the exception to logcat
- p_env->ExceptionDescribe();
-
- p_env->ExceptionClear();
- return true;
- }
-
- return false;
-}
-
-String app_native_lib_dir_cache;
-
-String determine_app_native_lib_dir() {
- JNIEnv *env = get_jni_env();
-
- ScopedLocalRef<jclass> activityThreadClass(env, env->FindClass("android/app/ActivityThread"));
- jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
- ScopedLocalRef<jobject> activityThread(env, env->CallStaticObjectMethod(activityThreadClass, currentActivityThread));
- jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
- ScopedLocalRef<jobject> ctx(env, env->CallObjectMethod(activityThread, getApplication));
-
- jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
- ScopedLocalRef<jobject> applicationInfo(env, env->CallObjectMethod(ctx, getApplicationInfo));
- jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
- ScopedLocalRef<jstring> nativeLibraryDir(env, (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField));
-
- String result;
-
- const char *const nativeLibraryDirUtf8 = env->GetStringUTFChars(nativeLibraryDir, nullptr);
- if (nativeLibraryDirUtf8) {
- result.parse_utf8(nativeLibraryDirUtf8);
- env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDirUtf8);
- }
-
- return result;
-}
-
-String get_app_native_lib_dir() {
- if (app_native_lib_dir_cache.is_empty()) {
- app_native_lib_dir_cache = determine_app_native_lib_dir();
- }
- return app_native_lib_dir_cache;
-}
-
-int gd_mono_convert_dl_flags(int flags) {
- // from mono's runtime-bootstrap.c
-
- int lflags = flags & MONO_DL_LOCAL ? 0 : RTLD_GLOBAL;
-
- if (flags & MONO_DL_LAZY) {
- lflags |= RTLD_LAZY;
- } else {
- lflags |= RTLD_NOW;
- }
-
- return lflags;
-}
-
-#ifndef GD_MONO_SO_NAME
-#define GD_MONO_SO_NAME "libmonosgen-2.0.so"
-#endif
-
-const char *mono_so_name = GD_MONO_SO_NAME;
-const char *godot_so_name = "libgodot_android.so";
-
-void *mono_dl_handle = nullptr;
-void *godot_dl_handle = nullptr;
-
-void *try_dlopen(const String &p_so_path, int p_flags) {
- if (!FileAccess::exists(p_so_path)) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Cannot find shared library: '%s'\n", p_so_path.utf8().get_data());
- }
- return nullptr;
- }
-
- int lflags = gd_mono_convert_dl_flags(p_flags);
-
- void *handle = dlopen(p_so_path.utf8().get_data(), lflags);
-
- if (!handle) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", p_so_path.utf8().get_data(), dlerror());
- }
- return nullptr;
- }
-
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Successfully loaded shared library: '%s'\n", p_so_path.utf8().get_data());
- }
-
- return handle;
-}
-
-void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) {
- if (p_name == nullptr) {
- // __Internal
-
- if (!mono_dl_handle) {
- String app_native_lib_dir = get_app_native_lib_dir();
- String so_path = path::join(app_native_lib_dir, mono_so_name);
-
- mono_dl_handle = try_dlopen(so_path, p_flags);
- }
-
- return mono_dl_handle;
- }
-
- String name = String::utf8(p_name);
-
- if (name.ends_with(".dll.so") || name.ends_with(".exe.so")) {
- String app_native_lib_dir = get_app_native_lib_dir();
-
- String orig_so_name = name.get_file();
- String so_name = "lib-aot-" + orig_so_name;
- String so_path = path::join(app_native_lib_dir, so_name);
-
- return try_dlopen(so_path, p_flags);
- }
-
- return nullptr;
-}
-
-void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, void *p_user_data) {
- void *sym_addr = dlsym(p_handle, p_name);
-
- if (sym_addr) {
- return sym_addr;
- }
-
- if (p_handle == mono_dl_handle && godot_dl_handle) {
- // Looking up for '__Internal' P/Invoke. We want to search in both the Mono and Godot shared libraries.
- // This is needed to resolve the monodroid P/Invoke functions that are defined at the bottom of the file.
- sym_addr = dlsym(godot_dl_handle, p_name);
-
- if (sym_addr) {
- return sym_addr;
- }
- }
-
- if (r_err) {
- *r_err = str_format_new("%s\n", dlerror());
- }
-
- return nullptr;
-}
-
-void *gd_mono_android_dlclose(void *p_handle, void *p_user_data) {
- dlclose(p_handle);
-
- // Not sure if this ever happens. Does Mono close the handle for the main module?
- if (p_handle == mono_dl_handle) {
- mono_dl_handle = nullptr;
- }
-
- return nullptr;
-}
-
-int32_t build_version_sdk_int = 0;
-
-int32_t get_build_version_sdk_int() {
- // The JNI code is the equivalent of:
- //
- // android.os.Build.VERSION.SDK_INT
-
- if (build_version_sdk_int == 0) {
- JNIEnv *env = get_jni_env();
-
- jclass versionClass = env->FindClass("android/os/Build$VERSION");
- ERR_FAIL_NULL_V(versionClass, 0);
-
- jfieldID sdkIntField = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
- ERR_FAIL_NULL_V(sdkIntField, 0);
-
- build_version_sdk_int = (int32_t)env->GetStaticIntField(versionClass, sdkIntField);
- }
-
- return build_version_sdk_int;
-}
-
-jobject certStore = nullptr; // KeyStore
-
-MonoBoolean _gd_mono_init_cert_store() {
- // The JNI code is the equivalent of:
- //
- // try {
- // certStoreLocal = KeyStore.getInstance("AndroidCAStore");
- // certStoreLocal.load(null);
- // certStore = certStoreLocal;
- // return true;
- // } catch (Exception e) {
- // return false;
- // }
-
- JNIEnv *env = get_jni_env();
-
- ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
-
- jmethodID getInstance = env->GetStaticMethodID(keyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;");
- jmethodID load = env->GetMethodID(keyStoreClass, "load", "(Ljava/security/KeyStore$LoadStoreParameter;)V");
-
- ScopedLocalRef<jstring> androidCAStoreString(env, env->NewStringUTF("AndroidCAStore"));
-
- ScopedLocalRef<jobject> certStoreLocal(env, env->CallStaticObjectMethod(keyStoreClass, getInstance, androidCAStoreString.get()));
-
- if (jni_exception_check(env)) {
- return 0;
- }
-
- env->CallVoidMethod(certStoreLocal, load, nullptr);
-
- if (jni_exception_check(env)) {
- return 0;
- }
-
- certStore = env->NewGlobalRef(certStoreLocal);
-
- return 1;
-}
-
-MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
- // The JNI code is the equivalent of:
- //
- // Certificate certificate = certStore.getCertificate(alias);
- // if (certificate == null) {
- // return null;
- // }
- // return certificate.getEncoded();
-
- MonoError mono_error;
- char *alias_utf8 = mono_string_to_utf8_checked(p_alias, &mono_error);
-
- if (!mono_error_ok(&mono_error)) {
- ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&mono_error) + "'.");
- mono_error_cleanup(&mono_error);
- return nullptr;
- }
-
- JNIEnv *env = get_jni_env();
-
- ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
- mono_free(alias_utf8);
-
- ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
- ERR_FAIL_NULL_V(keyStoreClass, nullptr);
- ScopedLocalRef<jclass> certificateClass(env, env->FindClass("java/security/cert/Certificate"));
- ERR_FAIL_NULL_V(certificateClass, nullptr);
-
- jmethodID getCertificate = env->GetMethodID(keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
- ERR_FAIL_NULL_V(getCertificate, nullptr);
-
- jmethodID getEncoded = env->GetMethodID(certificateClass, "getEncoded", "()[B");
- ERR_FAIL_NULL_V(getEncoded, nullptr);
-
- ScopedLocalRef<jobject> certificate(env, env->CallObjectMethod(certStore, getCertificate, js_alias.get()));
-
- if (!certificate) {
- return nullptr;
- }
-
- ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
- jsize encodedLength = env->GetArrayLength(encoded);
-
- MonoArray *encoded_ret = mono_array_new(mono_domain_get(), mono_get_byte_class(), encodedLength);
- uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
-
- env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));
-
- return encoded_ret;
-}
-
-void register_internal_calls() {
- GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", _gd_mono_init_cert_store);
- GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", _gd_mono_android_cert_store_lookup);
-}
-
-void initialize() {
- // We need to set this environment variable to make the monodroid BCL use btls instead of legacy as the default provider
- OS::get_singleton()->set_environment("XA_TLS_PROVIDER", "btls");
-
- mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, gd_mono_android_dlclose, nullptr);
-
- String app_native_lib_dir = get_app_native_lib_dir();
- String so_path = path::join(app_native_lib_dir, godot_so_name);
-
- godot_dl_handle = try_dlopen(so_path, gd_mono_convert_dl_flags(MONO_DL_LAZY));
-}
-
-void cleanup() {
- // This is called after shutting down the Mono runtime
-
- if (mono_dl_handle) {
- gd_mono_android_dlclose(mono_dl_handle, nullptr);
- }
-
- if (godot_dl_handle) {
- gd_mono_android_dlclose(godot_dl_handle, nullptr);
- }
-
- JNIEnv *env = get_jni_env();
-
- if (certStore) {
- env->DeleteGlobalRef(certStore);
- certStore = nullptr;
- }
-}
-} // namespace support
-} // namespace android
-} // namespace gdmono
-
-using namespace gdmono::android::support;
-
-// The following are P/Invoke functions required by the monodroid profile of the BCL.
-// These are P/Invoke functions and not internal calls, hence why they use
-// 'mono_bool' and 'const char*' instead of 'MonoBoolean' and 'MonoString*'.
-
-#define GD_PINVOKE_EXPORT extern "C" __attribute__((visibility("default")))
-
-GD_PINVOKE_EXPORT int32_t _monodroid_get_android_api_level() {
- return get_build_version_sdk_int();
-}
-
-GD_PINVOKE_EXPORT void monodroid_free(void *ptr) {
- free(ptr);
-}
-
-GD_PINVOKE_EXPORT int32_t monodroid_get_system_property(const char *p_name, char **r_value) {
- char prop_value_str[PROP_VALUE_MAX + 1] = { 0 };
-
- int len = __system_property_get(p_name, prop_value_str);
-
- if (r_value) {
- if (len >= 0) {
- *r_value = (char *)malloc(len + 1);
- ERR_FAIL_NULL_V_MSG(*r_value, -1, "Out of memory.");
- memcpy(*r_value, prop_value_str, len);
- (*r_value)[len] = '\0';
- } else {
- *r_value = nullptr;
- }
- }
-
- return len;
-}
-
-GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_up_state(const char *p_ifname, mono_bool *r_is_up) {
- // The JNI code is the equivalent of:
- //
- // NetworkInterface.getByName(p_ifname).isUp()
-
- if (!r_is_up || !p_ifname || strlen(p_ifname) == 0) {
- return 0;
- }
-
- *r_is_up = 0;
-
- JNIEnv *env = get_jni_env();
-
- jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
- ERR_FAIL_NULL_V(networkInterfaceClass, 0);
-
- jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
- ERR_FAIL_NULL_V(getByName, 0);
-
- jmethodID isUp = env->GetMethodID(networkInterfaceClass, "isUp", "()Z");
- ERR_FAIL_NULL_V(isUp, 0);
-
- ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
- ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
-
- if (!networkInterface) {
- return 0;
- }
-
- *r_is_up = (mono_bool)env->CallBooleanMethod(networkInterface, isUp);
-
- return 1;
-}
-
-GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_supports_multicast(const char *p_ifname, mono_bool *r_supports_multicast) {
- // The JNI code is the equivalent of:
- //
- // NetworkInterface.getByName(p_ifname).supportsMulticast()
-
- if (!r_supports_multicast || !p_ifname || strlen(p_ifname) == 0) {
- return 0;
- }
-
- *r_supports_multicast = 0;
-
- JNIEnv *env = get_jni_env();
-
- jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
- ERR_FAIL_NULL_V(networkInterfaceClass, 0);
-
- jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
- ERR_FAIL_NULL_V(getByName, 0);
-
- jmethodID supportsMulticast = env->GetMethodID(networkInterfaceClass, "supportsMulticast", "()Z");
- ERR_FAIL_NULL_V(supportsMulticast, 0);
-
- ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
- ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
-
- if (!networkInterface) {
- return 0;
- }
-
- *r_supports_multicast = (mono_bool)env->CallBooleanMethod(networkInterface, supportsMulticast);
-
- return 1;
-}
-
-static const int dns_servers_len = 8;
-
-static void interop_get_active_network_dns_servers(char **r_dns_servers, int *dns_servers_count) {
- // The JNI code is the equivalent of:
- //
- // ConnectivityManager connectivityManager = (ConnectivityManager)getApplicationContext()
- // .getSystemService(Context.CONNECTIVITY_SERVICE);
- // Network activeNerwork = connectivityManager.getActiveNetwork();
- // LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNerwork);
- // List<String> dnsServers = linkProperties.getDnsServers().stream()
- // .map(inetAddress -> inetAddress.getHostAddress()).collect(Collectors.toList());
-
-#ifdef DEBUG_ENABLED
- CRASH_COND(get_build_version_sdk_int() < 23);
-#endif
-
- JNIEnv *env = get_jni_env();
-
- GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
- jobject activity = godot_java->get_activity();
-
- ScopedLocalRef<jclass> activityClass(env, env->GetObjectClass(activity));
- ERR_FAIL_NULL(activityClass);
-
- jmethodID getApplicationContext = env->GetMethodID(activityClass, "getApplicationContext", "()Landroid/content/Context;");
-
- ScopedLocalRef<jobject> applicationContext(env, env->CallObjectMethod(activity, getApplicationContext));
-
- ScopedLocalRef<jclass> contextClass(env, env->FindClass("android/content/Context"));
- ERR_FAIL_NULL(contextClass);
-
- jfieldID connectivityServiceField = env->GetStaticFieldID(contextClass, "CONNECTIVITY_SERVICE", "Ljava/lang/String;");
- ScopedLocalRef<jstring> connectivityServiceString(env, (jstring)env->GetStaticObjectField(contextClass, connectivityServiceField));
-
- jmethodID getSystemService = env->GetMethodID(contextClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
-
- ScopedLocalRef<jobject> connectivityManager(env, env->CallObjectMethod(applicationContext, getSystemService, connectivityServiceString.get()));
-
- if (!connectivityManager) {
- return;
- }
-
- ScopedLocalRef<jclass> connectivityManagerClass(env, env->FindClass("android/net/ConnectivityManager"));
- ERR_FAIL_NULL(connectivityManagerClass);
-
- jmethodID getActiveNetwork = env->GetMethodID(connectivityManagerClass, "getActiveNetwork", "()Landroid/net/Network;");
- ERR_FAIL_NULL(getActiveNetwork);
-
- ScopedLocalRef<jobject> activeNetwork(env, env->CallObjectMethod(connectivityManager, getActiveNetwork));
-
- if (!activeNetwork) {
- return;
- }
-
- jmethodID getLinkProperties = env->GetMethodID(connectivityManagerClass,
- "getLinkProperties", "(Landroid/net/Network;)Landroid/net/LinkProperties;");
- ERR_FAIL_NULL(getLinkProperties);
-
- ScopedLocalRef<jobject> linkProperties(env, env->CallObjectMethod(connectivityManager, getLinkProperties, activeNetwork.get()));
-
- if (!linkProperties) {
- return;
- }
-
- ScopedLocalRef<jclass> linkPropertiesClass(env, env->FindClass("android/net/LinkProperties"));
- ERR_FAIL_NULL(linkPropertiesClass);
-
- jmethodID getDnsServers = env->GetMethodID(linkPropertiesClass, "getDnsServers", "()Ljava/util/List;");
- ERR_FAIL_NULL(getDnsServers);
-
- ScopedLocalRef<jobject> dnsServers(env, env->CallObjectMethod(linkProperties, getDnsServers));
-
- if (!dnsServers) {
- return;
- }
-
- ScopedLocalRef<jclass> listClass(env, env->FindClass("java/util/List"));
- ERR_FAIL_NULL(listClass);
-
- jmethodID listSize = env->GetMethodID(listClass, "size", "()I");
- ERR_FAIL_NULL(listSize);
-
- int dnsServersCount = env->CallIntMethod(dnsServers, listSize);
-
- if (dnsServersCount > dns_servers_len) {
- dnsServersCount = dns_servers_len;
- }
-
- if (dnsServersCount <= 0) {
- return;
- }
-
- jmethodID listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
- ERR_FAIL_NULL(listGet);
-
- ScopedLocalRef<jclass> inetAddressClass(env, env->FindClass("java/net/InetAddress"));
- ERR_FAIL_NULL(inetAddressClass);
-
- jmethodID getHostAddress = env->GetMethodID(inetAddressClass, "getHostAddress", "()Ljava/lang/String;");
- ERR_FAIL_NULL(getHostAddress);
-
- for (int i = 0; i < dnsServersCount; i++) {
- ScopedLocalRef<jobject> dnsServer(env, env->CallObjectMethod(dnsServers, listGet, (jint)i));
- if (!dnsServer) {
- continue;
- }
-
- ScopedLocalRef<jstring> hostAddress(env, (jstring)env->CallObjectMethod(dnsServer, getHostAddress));
- const char *host_address = env->GetStringUTFChars(hostAddress, 0);
-
- r_dns_servers[i] = strdup(host_address); // freed by the BCL
- (*dns_servers_count)++;
-
- env->ReleaseStringUTFChars(hostAddress, host_address);
- }
-
- // jesus...
-}
-
-GD_PINVOKE_EXPORT int32_t _monodroid_get_dns_servers(void **r_dns_servers_array) {
- if (!r_dns_servers_array) {
- return -1;
- }
-
- *r_dns_servers_array = nullptr;
-
- char *dns_servers[dns_servers_len];
- int dns_servers_count = 0;
-
- if (_monodroid_get_android_api_level() < 26) {
- // The 'net.dns*' system properties are no longer available in Android 8.0 (API level 26) and greater:
- // https://developer.android.com/about/versions/oreo/android-8.0-changes.html#o-pri
-
- char prop_name[] = "net.dns*";
-
- for (int i = 0; i < dns_servers_len; i++) {
- prop_name[7] = (char)(i + 0x31);
- char *prop_value;
- int32_t len = monodroid_get_system_property(prop_name, &prop_value);
-
- if (len > 0) {
- dns_servers[dns_servers_count] = strndup(prop_value, (size_t)len); // freed by the BCL
- dns_servers_count++;
- free(prop_value);
- }
- }
- } else {
- // Alternative for Oreo and greater
- interop_get_active_network_dns_servers(dns_servers, &dns_servers_count);
- }
-
- if (dns_servers_count > 0) {
- size_t ret_size = sizeof(char *) * (size_t)dns_servers_count;
- *r_dns_servers_array = malloc(ret_size); // freed by the BCL
- ERR_FAIL_NULL_V_MSG(*r_dns_servers_array, -1, "Out of memory.");
- memcpy(*r_dns_servers_array, dns_servers, ret_size);
- }
-
- return dns_servers_count;
-}
-
-GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
- // The JNI code is the equivalent of:
- //
- // TimeZone.getDefault().getID()
-
- JNIEnv *env = get_jni_env();
-
- ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
- ERR_FAIL_NULL_V(timeZoneClass, nullptr);
-
- jmethodID getDefault = env->GetStaticMethodID(timeZoneClass, "getDefault", "()Ljava/util/TimeZone;");
- ERR_FAIL_NULL_V(getDefault, nullptr);
-
- jmethodID getID = env->GetMethodID(timeZoneClass, "getID", "()Ljava/lang/String;");
- ERR_FAIL_NULL_V(getID, nullptr);
-
- ScopedLocalRef<jobject> defaultTimeZone(env, env->CallStaticObjectMethod(timeZoneClass, getDefault));
-
- if (!defaultTimeZone) {
- return nullptr;
- }
-
- ScopedLocalRef<jstring> defaultTimeZoneID(env, (jstring)env->CallObjectMethod(defaultTimeZone, getID));
-
- if (!defaultTimeZoneID) {
- return nullptr;
- }
-
- const char *default_time_zone_id = env->GetStringUTFChars(defaultTimeZoneID, 0);
-
- char *result = strdup(default_time_zone_id); // freed by the BCL
-
- env->ReleaseStringUTFChars(defaultTimeZoneID, default_time_zone_id);
-
- return result;
-}
-
-GD_PINVOKE_EXPORT int32_t _monodroid_getifaddrs(struct ifaddrs **p_ifap) {
- return getifaddrs(p_ifap);
-}
-
-GD_PINVOKE_EXPORT void _monodroid_freeifaddrs(struct ifaddrs *p_ifap) {
- freeifaddrs(p_ifap);
-}
-
-#endif
diff --git a/modules/mono/mono_gd/support/android_support.h b/modules/mono/mono_gd/support/android_support.h
deleted file mode 100644
index 5be4bac6e1..0000000000
--- a/modules/mono/mono_gd/support/android_support.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/**************************************************************************/
-/* android_support.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 ANDROID_SUPPORT_H
-#define ANDROID_SUPPORT_H
-
-#if defined(ANDROID_ENABLED)
-
-#include "core/string/ustring.h"
-
-namespace gdmono {
-namespace android {
-namespace support {
-
-String get_app_native_lib_dir();
-
-void initialize();
-void cleanup();
-
-void register_internal_calls();
-} // namespace support
-} // namespace android
-} // namespace gdmono
-
-#endif // ANDROID_ENABLED
-
-#endif // ANDROID_SUPPORT_H
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
index 23de01a191..9c1165bf8a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -88,16 +88,16 @@ class Godot(private val context: Context) : SensorEventListener {
private val mSensorManager: SensorManager by lazy {
requireActivity().getSystemService(Context.SENSOR_SERVICE) as SensorManager
}
- private val mAccelerometer: Sensor by lazy {
+ private val mAccelerometer: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
}
- private val mGravity: Sensor by lazy {
+ private val mGravity: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
}
- private val mMagnetometer: Sensor by lazy {
+ private val mMagnetometer: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
}
- private val mGyroscope: Sensor by lazy {
+ private val mGyroscope: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
}
private val mClipboard: ClipboardManager by lazy {
@@ -492,10 +492,18 @@ class Godot(private val context: Context) : SensorEventListener {
}
renderView!!.onActivityResumed()
- mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
- mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME)
- mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
- mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
+ if (mAccelerometer != null) {
+ mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (mGravity != null) {
+ mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (mMagnetometer != null) {
+ mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (mGyroscope != null) {
+ mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
+ }
if (useImmersive) {
val window = requireActivity().window
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index cb72e4ec08..8d5133311a 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -645,7 +645,8 @@ int Node::get_multiplayer_authority() const {
bool Node::is_multiplayer_authority() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
- return get_multiplayer()->get_unique_id() == data.multiplayer_authority;
+ Ref<MultiplayerAPI> api = get_multiplayer();
+ return api.is_valid() && (api->get_unique_id() == data.multiplayer_authority);
}
/***** RPC CONFIG ********/
@@ -724,7 +725,12 @@ Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallE
Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
- return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
+
+ Ref<MultiplayerAPI> api = get_multiplayer();
+ if (api.is_null()) {
+ return ERR_UNCONFIGURED;
+ }
+ return api->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
diff --git a/scene/resources/placeholder_textures.cpp b/scene/resources/placeholder_textures.cpp
index 224ea1f177..c395195c47 100644
--- a/scene/resources/placeholder_textures.cpp
+++ b/scene/resources/placeholder_textures.cpp
@@ -51,6 +51,9 @@ Ref<Image> PlaceholderTexture2D::get_image() const {
}
RID PlaceholderTexture2D::get_rid() const {
+ if (rid.is_null()) {
+ rid = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
return rid;
}
@@ -61,12 +64,13 @@ void PlaceholderTexture2D::_bind_methods() {
}
PlaceholderTexture2D::PlaceholderTexture2D() {
- rid = RS::get_singleton()->texture_2d_placeholder_create();
}
PlaceholderTexture2D::~PlaceholderTexture2D() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(rid);
+ if (rid.is_valid()) {
+ RS::get_singleton()->free(rid);
+ }
}
///////////////////////////////////////////////
@@ -103,6 +107,13 @@ Vector<Ref<Image>> PlaceholderTexture3D::get_data() const {
return Vector<Ref<Image>>();
}
+RID PlaceholderTexture3D::get_rid() const {
+ if (rid.is_null()) {
+ rid = RenderingServer::get_singleton()->texture_3d_placeholder_create();
+ }
+ return rid;
+}
+
void PlaceholderTexture3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture3D::set_size);
ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTexture3D::get_size);
@@ -110,11 +121,12 @@ void PlaceholderTexture3D::_bind_methods() {
}
PlaceholderTexture3D::PlaceholderTexture3D() {
- rid = RS::get_singleton()->texture_3d_placeholder_create();
}
PlaceholderTexture3D::~PlaceholderTexture3D() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(rid);
+ if (rid.is_valid()) {
+ RS::get_singleton()->free(rid);
+ }
}
/////////////////////////////////////////////////
@@ -159,6 +171,13 @@ Ref<Image> PlaceholderTextureLayered::get_layer_data(int p_layer) const {
return Ref<Image>();
}
+RID PlaceholderTextureLayered::get_rid() const {
+ if (rid.is_null()) {
+ rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
+ }
+ return rid;
+}
+
void PlaceholderTextureLayered::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTextureLayered::set_size);
ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTextureLayered::get_size);
@@ -169,9 +188,10 @@ void PlaceholderTextureLayered::_bind_methods() {
PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) {
layered_type = p_type;
- rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
}
PlaceholderTextureLayered::~PlaceholderTextureLayered() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(rid);
+ if (rid.is_valid()) {
+ RS::get_singleton()->free(rid);
+ }
}
diff --git a/scene/resources/placeholder_textures.h b/scene/resources/placeholder_textures.h
index 116ed0f0f0..300d641cc3 100644
--- a/scene/resources/placeholder_textures.h
+++ b/scene/resources/placeholder_textures.h
@@ -36,7 +36,7 @@
class PlaceholderTexture2D : public Texture2D {
GDCLASS(PlaceholderTexture2D, Texture2D)
- RID rid;
+ mutable RID rid;
Size2 size = Size2(1, 1);
protected:
@@ -59,7 +59,7 @@ public:
class PlaceholderTexture3D : public Texture3D {
GDCLASS(PlaceholderTexture3D, Texture3D)
- RID rid;
+ mutable RID rid;
Vector3i size = Vector3i(1, 1, 1);
protected:
@@ -74,6 +74,7 @@ public:
virtual int get_depth() const override;
virtual bool has_mipmaps() const override;
virtual Vector<Ref<Image>> get_data() const override;
+ virtual RID get_rid() const override;
PlaceholderTexture3D();
~PlaceholderTexture3D();
@@ -82,7 +83,7 @@ public:
class PlaceholderTextureLayered : public TextureLayered {
GDCLASS(PlaceholderTextureLayered, TextureLayered)
- RID rid;
+ mutable RID rid;
Size2i size = Size2i(1, 1);
int layers = 1;
LayeredType layered_type = LAYERED_TYPE_2D_ARRAY;
@@ -101,6 +102,7 @@ public:
virtual int get_layers() const override;
virtual bool has_mipmaps() const override;
virtual Ref<Image> get_layer_data(int p_layer) const override;
+ virtual RID get_rid() const override;
PlaceholderTextureLayered(LayeredType p_type);
~PlaceholderTextureLayered();
diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp
index ee1a47c338..dc8545426f 100644
--- a/scene/resources/video_stream.cpp
+++ b/scene/resources/video_stream.cpp
@@ -75,7 +75,7 @@ bool VideoStreamPlayback::is_playing() const {
}
void VideoStreamPlayback::set_paused(bool p_paused) {
- GDVIRTUAL_CALL(_is_playing, p_paused);
+ GDVIRTUAL_CALL(_set_paused, p_paused);
}
bool VideoStreamPlayback::is_paused() const {
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index ef09552603..7bca1dc3be 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -779,6 +779,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
scene_state.used_screen_texture = false;
scene_state.used_normal_texture = false;
scene_state.used_depth_texture = false;
+ scene_state.used_lightmap = false;
}
uint32_t lightmap_captures_used = 0;
@@ -994,6 +995,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
if (uses_lightmap) {
surf->sort.uses_lightmap = 1;
+ scene_state.used_lightmap = true;
}
if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_SUBSURFACE_SCATTERING) {
@@ -1628,6 +1630,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
if (rb->get_use_taa() || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) {
color_pass_flags |= COLOR_PASS_FLAG_MOTION_VECTORS;
+ scene_shader.enable_advanced_shader_group();
}
if (p_render_data->voxel_gi_instances->size() > 0) {
@@ -1647,6 +1650,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
if (p_render_data->scene_data->view_count > 1) {
color_pass_flags |= COLOR_PASS_FLAG_MULTIVIEW;
+ // Try enabling here in case is_xr_enabled() returns false.
+ scene_shader.shader.enable_group(SceneShaderForwardClustered::SHADER_GROUP_MULTIVIEW);
}
color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags);
@@ -1712,6 +1717,11 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR;
color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags);
}
+
+ if (using_sss || using_separate_specular || scene_state.used_lightmap || using_voxelgi) {
+ scene_shader.enable_advanced_shader_group(p_render_data->scene_data->view_count > 1);
+ }
+
RID radiance_texture;
bool draw_sky = false;
bool draw_sky_fog_only = false;
@@ -2484,6 +2494,8 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform
render_data.cluster_max_elements = 32;
render_data.instances = &p_instances;
+ scene_shader.enable_advanced_shader_group();
+
_update_render_base_uniform_set();
_setup_environment(&render_data, true, Vector2(1, 1), false, Color());
@@ -2533,6 +2545,8 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance
render_data.cluster_max_elements = 32;
render_data.instances = &p_instances;
+ scene_shader.enable_advanced_shader_group();
+
_update_render_base_uniform_set();
_setup_environment(&render_data, true, Vector2(1, 1), false, Color());
@@ -3321,6 +3335,10 @@ void RenderForwardClustered::sdfgi_update(const Ref<RenderSceneBuffers> &p_rende
return;
}
+ // Ensure advanced shaders are available if SDFGI is used.
+ // Call here as this is the first entry point for SDFGI.
+ scene_shader.enable_advanced_shader_group();
+
static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 };
uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge];
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index 155e8b4d19..a89c77c652 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -321,6 +321,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
bool used_normal_texture = false;
bool used_depth_texture = false;
bool used_sss = false;
+ bool used_lightmap = false;
struct ShadowPass {
uint32_t element_from;
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 377aab1354..298a6c0752 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -301,7 +301,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
if (k == PIPELINE_VERSION_COLOR_PASS) {
for (int l = 0; l < PIPELINE_COLOR_PASS_FLAG_COUNT; l++) {
- if (!shader_singleton->valid_color_pass_pipelines.has(l)) {
+ if (!shader_singleton->valid_color_pass_pipelines[l]) {
continue;
}
@@ -476,16 +476,16 @@ void SceneShaderForwardClustered::init(const String p_defines) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
{
- Vector<String> shader_versions;
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_DEPTH_PASS_DP
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); // SHADER_VERSION_DEPTH_PASS_WITH_SDF
- shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW
- shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW
- shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW
+ Vector<ShaderRD::VariantDefine> shader_versions;
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n", true)); // SHADER_VERSION_DEPTH_PASS
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n", true)); // SHADER_VERSION_DEPTH_PASS_DP
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", true)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_SDF
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n", false)); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW
Vector<String> color_pass_flags = {
"\n#define MODE_SEPARATE_SPECULAR\n", // SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR
@@ -501,54 +501,38 @@ void SceneShaderForwardClustered::init(const String p_defines) {
version += color_pass_flags[j];
}
}
- shader_versions.push_back(version);
+
+ // Assign a group based on what features this pass contains.
+ ShaderGroup group = SHADER_GROUP_BASE;
+ bool advanced_group = (i & SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR) || (i & SHADER_COLOR_PASS_FLAG_LIGHTMAP) || (i & SHADER_COLOR_PASS_FLAG_MOTION_VECTORS);
+ bool multiview_group = i & SHADER_COLOR_PASS_FLAG_MULTIVIEW;
+ if (advanced_group && multiview_group) {
+ group = SHADER_GROUP_ADVANCED_MULTIVIEW;
+ } else if (advanced_group) {
+ group = SHADER_GROUP_ADVANCED;
+ } else if (multiview_group) {
+ group = SHADER_GROUP_MULTIVIEW;
+ }
+
+ shader_versions.push_back(ShaderRD::VariantDefine(group, version, false));
}
shader.initialize(shader_versions, p_defines);
- if (!RendererCompositorRD::get_singleton()->is_xr_enabled()) {
- shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_MULTIVIEW, false);
- shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW, false);
- shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW, false);
-
- // Disable Color Passes
- for (int i = 0; i < SHADER_COLOR_PASS_FLAG_COUNT; i++) {
- // Selectively disable any shader pass that includes Multiview.
- if ((i & SHADER_COLOR_PASS_FLAG_MULTIVIEW)) {
- shader.set_variant_enabled(i + SHADER_VERSION_COLOR_PASS, false);
- }
- }
+ if (RendererCompositorRD::get_singleton()->is_xr_enabled()) {
+ shader.enable_group(SHADER_GROUP_MULTIVIEW);
}
}
- valid_color_pass_pipelines.insert(0);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
+ // Set flag to true if a combination is valid.
+ // The only invalid combinations are those that include both TRANSPARENT and SEPARATE_SPECULAR.
+ for (int i = 0; i < PIPELINE_COLOR_PASS_FLAG_COUNT; i++) {
+ if ((i & PIPELINE_COLOR_PASS_FLAG_TRANSPARENT) && (i & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR)) {
+ valid_color_pass_pipelines[i] = false;
+ } else {
+ valid_color_pass_pipelines[i] = true;
+ }
+ }
material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_shader_funcs);
material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_material_funcs);
@@ -854,3 +838,11 @@ void SceneShaderForwardClustered::set_default_specialization_constants(const Vec
}
}
}
+
+void SceneShaderForwardClustered::enable_advanced_shader_group(bool p_needs_multiview) {
+ if (p_needs_multiview || RendererCompositorRD::get_singleton()->is_xr_enabled()) {
+ shader.enable_group(SHADER_GROUP_ADVANCED_MULTIVIEW);
+ } else {
+ shader.enable_group(SHADER_GROUP_ADVANCED);
+ }
+}
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
index bdafc4c27e..761b74defa 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
@@ -41,6 +41,13 @@ private:
static SceneShaderForwardClustered *singleton;
public:
+ enum ShaderGroup {
+ SHADER_GROUP_BASE, // Always compiled at the beginning.
+ SHADER_GROUP_ADVANCED,
+ SHADER_GROUP_MULTIVIEW,
+ SHADER_GROUP_ADVANCED_MULTIVIEW,
+ };
+
enum ShaderVersion {
SHADER_VERSION_DEPTH_PASS,
SHADER_VERSION_DEPTH_PASS_DP,
@@ -78,8 +85,8 @@ public:
};
enum PipelineColorPassFlags {
- PIPELINE_COLOR_PASS_FLAG_TRANSPARENT = 1 << 0,
- PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1,
+ PIPELINE_COLOR_PASS_FLAG_TRANSPARENT = 1 << 0, // Can't combine with SEPARATE_SPECULAR.
+ PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1, // Can't combine with TRANSPARENT.
PIPELINE_COLOR_PASS_FLAG_LIGHTMAP = 1 << 2,
PIPELINE_COLOR_PASS_FLAG_MULTIVIEW = 1 << 3,
PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 4,
@@ -242,12 +249,13 @@ public:
ShaderData *debug_shadow_splits_material_shader_ptr = nullptr;
Vector<RD::PipelineSpecializationConstant> default_specialization_constants;
- HashSet<uint32_t> valid_color_pass_pipelines;
+ bool valid_color_pass_pipelines[PIPELINE_COLOR_PASS_FLAG_COUNT];
SceneShaderForwardClustered();
~SceneShaderForwardClustered();
void init(const String p_defines);
void set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants);
+ void enable_advanced_shader_group(bool p_needs_multiview = false);
};
} // namespace RendererSceneRenderImplementation
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 6434682fc2..2053c3a9a6 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -781,24 +781,20 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
clear_color.r *= bg_energy_multiplier;
clear_color.g *= bg_energy_multiplier;
clear_color.b *= bg_energy_multiplier;
- /*
- if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment)) {
+ if (environment_get_fog_enabled(p_render_data->environment)) {
draw_sky_fog_only = true;
RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear()));
}
- */
} break;
case RS::ENV_BG_COLOR: {
clear_color = environment_get_bg_color(p_render_data->environment);
clear_color.r *= bg_energy_multiplier;
clear_color.g *= bg_energy_multiplier;
clear_color.b *= bg_energy_multiplier;
- /*
- if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment)) {
+ if (environment_get_fog_enabled(p_render_data->environment)) {
draw_sky_fog_only = true;
RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear()));
}
- */
} break;
case RS::ENV_BG_SKY: {
draw_sky = true;
@@ -908,13 +904,19 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
{
// regular forward for now
Vector<Color> c;
- c.push_back(clear_color.srgb_to_linear()); // our render buffer
- if (rb_data.is_valid()) {
- if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
- c.push_back(clear_color.srgb_to_linear()); // our resolve buffer
+ {
+ Color cc = clear_color.srgb_to_linear();
+ if (rb_data.is_valid()) {
+ cc.a = 0; // For transparent viewport backgrounds.
}
- if (using_subpass_post_process) {
- c.push_back(Color()); // our 2D buffer we're copying into
+ c.push_back(cc); // Our render buffer.
+ if (rb_data.is_valid()) {
+ if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
+ c.push_back(clear_color.srgb_to_linear()); // Our resolve buffer.
+ }
+ if (using_subpass_post_process) {
+ c.push_back(Color()); // Our 2D buffer we're copying into.
+ }
}
}
diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
index f3595c5917..c00e5f8b5e 100644
--- a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
+++ b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
@@ -89,7 +89,7 @@ void PipelineCacheRD::setup(RID p_shader, RD::RenderPrimitive p_primitive, const
ERR_FAIL_COND(p_shader.is_null());
_clear();
shader = p_shader;
- input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(p_shader);
+ input_mask = 0;
render_primitive = p_primitive;
rasterization_state = p_rasterization_state;
multisample_state = p_multisample;
diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.h b/servers/rendering/renderer_rd/pipeline_cache_rd.h
index 58c1278312..52877109f7 100644
--- a/servers/rendering/renderer_rd/pipeline_cache_rd.h
+++ b/servers/rendering/renderer_rd/pipeline_cache_rd.h
@@ -91,7 +91,11 @@ public:
return result;
}
- _FORCE_INLINE_ uint32_t get_vertex_input_mask() const {
+ _FORCE_INLINE_ uint32_t get_vertex_input_mask() {
+ if (input_mask == 0) {
+ ERR_FAIL_COND_V(shader.is_null(), 0);
+ input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader);
+ }
return input_mask;
}
void clear();
diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp
index eaa84b7614..da5597751b 100644
--- a/servers/rendering/renderer_rd/shader_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_rd.cpp
@@ -138,7 +138,7 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con
RID ShaderRD::version_create() {
//initialize() was never called
- ERR_FAIL_COND_V(variant_defines.size() == 0, RID());
+ ERR_FAIL_COND_V(group_to_variant_map.size() == 0, RID());
Version version;
version.dirty = true;
@@ -148,11 +148,20 @@ RID ShaderRD::version_create() {
return version_owner.make_rid(version);
}
+void ShaderRD::_initialize_version(Version *p_version) {
+ _clear_version(p_version);
+
+ p_version->valid = false;
+ p_version->dirty = false;
+
+ p_version->variants = memnew_arr(RID, variant_defines.size());
+}
+
void ShaderRD::_clear_version(Version *p_version) {
- //clear versions if they exist
+ // Clear versions if they exist.
if (p_version->variants) {
for (int i = 0; i < variant_defines.size(); i++) {
- if (variants_enabled[i]) {
+ if (variants_enabled[i] && group_enabled[variant_defines[i].group]) {
RD::get_singleton()->free(p_version->variants[i]);
}
}
@@ -171,7 +180,7 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c
case StageTemplate::Chunk::TYPE_VERSION_DEFINES: {
builder.append("\n"); //make sure defines begin at newline
builder.append(general_defines.get_data());
- builder.append(variant_defines[p_variant].get_data());
+ builder.append(variant_defines[p_variant].text.get_data());
for (int j = 0; j < p_version->custom_defines.size(); j++) {
builder.append(p_version->custom_defines[j].get_data());
}
@@ -211,9 +220,11 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c
}
}
-void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
- if (!variants_enabled[p_variant]) {
- return; //variant is disabled, return
+void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) {
+ uint32_t variant = group_to_variant_map[p_data->group][p_variant];
+
+ if (!variants_enabled[variant]) {
+ return; // Variant is disabled, return.
}
Vector<RD::ShaderStageSPIRVData> stages;
@@ -227,7 +238,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
//vertex stage
StringBuilder builder;
- _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX]);
+ _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_VERTEX]);
current_source = builder.as_string();
RD::ShaderStageSPIRVData stage;
@@ -245,7 +256,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
current_stage = RD::SHADER_STAGE_FRAGMENT;
StringBuilder builder;
- _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT]);
+ _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_FRAGMENT]);
current_source = builder.as_string();
RD::ShaderStageSPIRVData stage;
@@ -263,7 +274,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
current_stage = RD::SHADER_STAGE_COMPUTE;
StringBuilder builder;
- _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_COMPUTE]);
+ _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_COMPUTE]);
current_source = builder.as_string();
@@ -279,7 +290,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
if (!build_ok) {
MutexLock lock(variant_set_mutex); //properly print the errors
- ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(p_variant) + " (" + variant_defines[p_variant].get_data() + ").");
+ ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(variant) + " (" + variant_defines[variant].text.get_data() + ").");
ERR_PRINT(error);
#ifdef DEBUG_ENABLED
@@ -288,15 +299,15 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
return;
}
- Vector<uint8_t> shader_data = RD::get_singleton()->shader_compile_binary_from_spirv(stages, name + ":" + itos(p_variant));
+ Vector<uint8_t> shader_data = RD::get_singleton()->shader_compile_binary_from_spirv(stages, name + ":" + itos(variant));
ERR_FAIL_COND(shader_data.size() == 0);
- RID shader = RD::get_singleton()->shader_create_from_bytecode(shader_data);
{
MutexLock lock(variant_set_mutex);
- p_version->variants[p_variant] = shader;
- p_version->variant_data[p_variant] = shader_data;
+
+ p_data->version->variants[variant] = RD::get_singleton()->shader_create_from_bytecode(shader_data, p_data->version->variants[variant]);
+ p_data->version->variant_data[variant] = shader_data;
}
}
@@ -384,9 +395,9 @@ String ShaderRD::_version_get_sha1(Version *p_version) const {
static const char *shader_file_header = "GDSC";
static const uint32_t cache_file_version = 3;
-bool ShaderRD::_load_from_cache(Version *p_version) {
+bool ShaderRD::_load_from_cache(Version *p_version, int p_group) {
String sha1 = _version_get_sha1(p_version);
- String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
+ String path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + ".cache";
Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ);
if (f.is_null()) {
@@ -404,12 +415,13 @@ bool ShaderRD::_load_from_cache(Version *p_version) {
uint32_t variant_count = f->get_32();
- ERR_FAIL_COND_V(variant_count != (uint32_t)variant_defines.size(), false); //should not happen but check
+ ERR_FAIL_COND_V(variant_count != (uint32_t)group_to_variant_map[p_group].size(), false); //should not happen but check
for (uint32_t i = 0; i < variant_count; i++) {
+ int variant_id = group_to_variant_map[p_group][i];
uint32_t variant_size = f->get_32();
- ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[i], false);
- if (!variants_enabled[i]) {
+ ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[variant_id], false);
+ if (!variants_enabled[variant_id]) {
continue;
}
Vector<uint8_t> variant_bytes;
@@ -419,25 +431,28 @@ bool ShaderRD::_load_from_cache(Version *p_version) {
ERR_FAIL_COND_V(br != variant_size, false);
- p_version->variant_data[i] = variant_bytes;
+ p_version->variant_data[variant_id] = variant_bytes;
}
for (uint32_t i = 0; i < variant_count; i++) {
- if (!variants_enabled[i]) {
+ int variant_id = group_to_variant_map[p_group][i];
+ if (!variants_enabled[variant_id]) {
MutexLock lock(variant_set_mutex);
- p_version->variants[i] = RID();
+ p_version->variants[variant_id] = RID();
continue;
}
- RID shader = RD::get_singleton()->shader_create_from_bytecode(p_version->variant_data[i]);
- if (shader.is_null()) {
- for (uint32_t j = 0; j < i; j++) {
- RD::get_singleton()->free(p_version->variants[i]);
- }
- ERR_FAIL_COND_V(shader.is_null(), false);
- }
{
MutexLock lock(variant_set_mutex);
- p_version->variants[i] = shader;
+ RID shader = RD::get_singleton()->shader_create_from_bytecode(p_version->variant_data[variant_id], p_version->variants[variant_id]);
+ if (shader.is_null()) {
+ for (uint32_t j = 0; j < i; j++) {
+ int variant_free_id = group_to_variant_map[p_group][j];
+ RD::get_singleton()->free(p_version->variants[variant_free_id]);
+ }
+ ERR_FAIL_COND_V(shader.is_null(), false);
+ }
+
+ p_version->variants[variant_id] = shader;
}
}
@@ -447,66 +462,85 @@ bool ShaderRD::_load_from_cache(Version *p_version) {
return true;
}
-void ShaderRD::_save_to_cache(Version *p_version) {
+void ShaderRD::_save_to_cache(Version *p_version, int p_group) {
String sha1 = _version_get_sha1(p_version);
- String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
+ String path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + ".cache";
Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE);
ERR_FAIL_COND(f.is_null());
f->store_buffer((const uint8_t *)shader_file_header, 4);
- f->store_32(cache_file_version); //file version
- uint32_t variant_count = variant_defines.size();
- f->store_32(variant_count); //variant count
-
+ f->store_32(cache_file_version); // File version.
+ uint32_t variant_count = group_to_variant_map[p_group].size();
+ f->store_32(variant_count); // Variant count.
for (uint32_t i = 0; i < variant_count; i++) {
- f->store_32(p_version->variant_data[i].size()); //stage count
- f->store_buffer(p_version->variant_data[i].ptr(), p_version->variant_data[i].size());
+ int variant_id = group_to_variant_map[p_group][i];
+ f->store_32(p_version->variant_data[variant_id].size()); // Stage count.
+ f->store_buffer(p_version->variant_data[variant_id].ptr(), p_version->variant_data[variant_id].size());
}
}
-void ShaderRD::_compile_version(Version *p_version) {
- _clear_version(p_version);
+void ShaderRD::_allocate_placeholders(Version *p_version, int p_group) {
+ for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
+ int variant_id = group_to_variant_map[p_group][i];
+ RID shader = RD::get_singleton()->shader_create_placeholder();
+ {
+ MutexLock lock(variant_set_mutex);
+ p_version->variants[variant_id] = shader;
+ }
+ }
+}
- p_version->valid = false;
- p_version->dirty = false;
+// Try to compile all variants for a given group.
+// Will skip variants that are disabled.
+void ShaderRD::_compile_version(Version *p_version, int p_group) {
+ if (!group_enabled[p_group]) {
+ return;
+ }
- p_version->variants = memnew_arr(RID, variant_defines.size());
typedef Vector<uint8_t> ShaderStageData;
p_version->variant_data = memnew_arr(ShaderStageData, variant_defines.size());
+ p_version->dirty = false;
+
if (shader_cache_dir_valid) {
- if (_load_from_cache(p_version)) {
+ if (_load_from_cache(p_version, p_group)) {
+ print_line("loaded from cache!");
return;
}
}
-#if 1
+ CompileData compile_data;
+ compile_data.version = p_version;
+ compile_data.group = p_group;
- WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, p_version, variant_defines.size(), -1, true, SNAME("ShaderCompilation"));
+#if 1
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, &compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
#else
- for (int i = 0; i < variant_defines.size(); i++) {
- _compile_variant(i, p_version);
+ for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
+ _compile_variant(i, &compile_data);
}
#endif
bool all_valid = true;
- for (int i = 0; i < variant_defines.size(); i++) {
- if (!variants_enabled[i]) {
- continue; //disabled
+
+ for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
+ int variant_id = group_to_variant_map[p_group][i];
+ if (!variants_enabled[variant_id]) {
+ continue; // Disabled.
}
- if (p_version->variants[i].is_null()) {
+ if (p_version->variants[variant_id].is_null()) {
all_valid = false;
break;
}
}
if (!all_valid) {
- //clear versions if they exist
+ // Clear versions if they exist.
for (int i = 0; i < variant_defines.size(); i++) {
- if (!variants_enabled[i]) {
- continue; //disabled
+ if (!variants_enabled[i] || !group_enabled[variant_defines[i].group]) {
+ continue; // Disabled.
}
if (!p_version->variants[i].is_null()) {
RD::get_singleton()->free(p_version->variants[i]);
@@ -520,8 +554,8 @@ void ShaderRD::_compile_version(Version *p_version) {
p_version->variant_data = nullptr;
return;
} else if (shader_cache_dir_valid) {
- //save shader cache
- _save_to_cache(p_version);
+ // Save shader cache.
+ _save_to_cache(p_version, p_group);
}
memdelete_arr(p_version->variant_data); //clear stages
@@ -550,7 +584,14 @@ void ShaderRD::version_set_code(RID p_version, const HashMap<String, String> &p_
version->dirty = true;
if (version->initialize_needed) {
- _compile_version(version);
+ _initialize_version(version);
+ for (int i = 0; i < group_enabled.size(); i++) {
+ if (!group_enabled[i]) {
+ _allocate_placeholders(version, i);
+ continue;
+ }
+ _compile_version(version, i);
+ }
version->initialize_needed = false;
}
}
@@ -576,7 +617,14 @@ void ShaderRD::version_set_compute_code(RID p_version, const HashMap<String, Str
version->dirty = true;
if (version->initialize_needed) {
- _compile_version(version);
+ _initialize_version(version);
+ for (int i = 0; i < group_enabled.size(); i++) {
+ if (!group_enabled[i]) {
+ _allocate_placeholders(version, i);
+ continue;
+ }
+ _compile_version(version, i);
+ }
version->initialize_needed = false;
}
}
@@ -586,7 +634,14 @@ bool ShaderRD::version_is_valid(RID p_version) {
ERR_FAIL_COND_V(!version, false);
if (version->dirty) {
- _compile_version(version);
+ _initialize_version(version);
+ for (int i = 0; i < group_enabled.size(); i++) {
+ if (!group_enabled[i]) {
+ _allocate_placeholders(version, i);
+ continue;
+ }
+ _compile_version(version, i);
+ }
}
return version->valid;
@@ -615,6 +670,29 @@ bool ShaderRD::is_variant_enabled(int p_variant) const {
return variants_enabled[p_variant];
}
+void ShaderRD::enable_group(int p_group) {
+ ERR_FAIL_INDEX(p_group, group_enabled.size());
+
+ if (group_enabled[p_group]) {
+ // Group already enabled, do nothing.
+ return;
+ }
+
+ group_enabled.write[p_group] = true;
+
+ // Compile all versions again to include the new group.
+ List<RID> all_versions;
+ version_owner.get_owned_list(&all_versions);
+ for (int i = 0; i < all_versions.size(); i++) {
+ Version *version = version_owner.get_or_null(all_versions[i]);
+ _compile_version(version, p_group);
+ }
+}
+
+bool ShaderRD::is_group_enabled(int p_group) const {
+ return group_enabled[p_group];
+}
+
bool ShaderRD::shader_cache_cleanup_on_start = false;
ShaderRD::ShaderRD() {
@@ -639,24 +717,38 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String
general_defines = p_general_defines.utf8();
+ // When initialized this way, there is just one group and its always enabled.
+ group_to_variant_map.insert(0, LocalVector<int>{});
+ group_enabled.push_back(true);
+
for (int i = 0; i < p_variant_defines.size(); i++) {
- variant_defines.push_back(p_variant_defines[i].utf8());
+ variant_defines.push_back(VariantDefine(0, p_variant_defines[i], true));
variants_enabled.push_back(true);
+ group_to_variant_map[0].push_back(i);
}
if (!shader_cache_dir.is_empty()) {
+ group_sha256.resize(1);
+ _initialize_cache();
+ }
+}
+
+void ShaderRD::_initialize_cache() {
+ for (const KeyValue<int, LocalVector<int>> &E : group_to_variant_map) {
StringBuilder hash_build;
hash_build.append("[base_hash]");
hash_build.append(base_sha256);
hash_build.append("[general_defines]");
hash_build.append(general_defines.get_data());
- for (int i = 0; i < variant_defines.size(); i++) {
- hash_build.append("[variant_defines:" + itos(i) + "]");
- hash_build.append(variant_defines[i].get_data());
+ hash_build.append("[group_id]");
+ hash_build.append(itos(E.key));
+ for (uint32_t i = 0; i < E.value.size(); i++) {
+ hash_build.append("[variant_defines:" + itos(E.value[i]) + "]");
+ hash_build.append(variant_defines[E.value[i]].text.get_data());
}
- base_sha256 = hash_build.as_string().sha256_text();
+ group_sha256[E.key] = hash_build.as_string().sha256_text();
Ref<DirAccess> d = DirAccess::open(shader_cache_dir);
ERR_FAIL_COND(d.is_null());
@@ -666,17 +758,58 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String
d->change_dir(name);
}
- //erase other versions?
+ // Erase other versions?
if (shader_cache_cleanup_on_start) {
}
//
- if (d->change_dir(base_sha256) != OK) {
- Error err = d->make_dir(base_sha256);
+ if (d->change_dir(group_sha256[E.key]) != OK) {
+ Error err = d->make_dir(group_sha256[E.key]);
ERR_FAIL_COND(err != OK);
}
shader_cache_dir_valid = true;
- print_verbose("Shader '" + name + "' SHA256: " + base_sha256);
+ print_verbose("Shader '" + name + "' (group " + itos(E.key) + ") SHA256: " + group_sha256[E.key]);
+ }
+}
+
+// Same as above, but allows specifying shader compilation groups.
+void ShaderRD::initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines) {
+ ERR_FAIL_COND(variant_defines.size());
+ ERR_FAIL_COND(p_variant_defines.size() == 0);
+
+ general_defines = p_general_defines.utf8();
+
+ int max_group_id = 0;
+
+ for (int i = 0; i < p_variant_defines.size(); i++) {
+ // Fill variant array.
+ variant_defines.push_back(p_variant_defines[i]);
+ variants_enabled.push_back(true);
+
+ // Map variant array index to group id, so we can iterate over groups later.
+ if (!group_to_variant_map.has(p_variant_defines[i].group)) {
+ group_to_variant_map.insert(p_variant_defines[i].group, LocalVector<int>{});
+ }
+ group_to_variant_map[p_variant_defines[i].group].push_back(i);
+
+ // Track max size.
+ if (p_variant_defines[i].group > max_group_id) {
+ max_group_id = p_variant_defines[i].group;
+ }
+ }
+
+ // Set all to groups to false, then enable those that should be default.
+ group_enabled.resize_zeroed(max_group_id + 1);
+ bool *enabled_ptr = group_enabled.ptrw();
+ for (int i = 0; i < p_variant_defines.size(); i++) {
+ if (p_variant_defines[i].default_enabled) {
+ enabled_ptr[p_variant_defines[i].group] = true;
+ }
+ }
+
+ if (!shader_cache_dir.is_empty()) {
+ group_sha256.resize(max_group_id + 1);
+ _initialize_cache();
}
}
diff --git a/servers/rendering/renderer_rd/shader_rd.h b/servers/rendering/renderer_rd/shader_rd.h
index d0871ca16c..01eb99f7a2 100644
--- a/servers/rendering/renderer_rd/shader_rd.h
+++ b/servers/rendering/renderer_rd/shader_rd.h
@@ -41,10 +41,26 @@
#include "servers/rendering_server.h"
class ShaderRD {
+public:
+ struct VariantDefine {
+ int group = 0;
+ CharString text;
+ bool default_enabled = true;
+ VariantDefine(){};
+ VariantDefine(int p_group, const String &p_text, bool p_default_enabled) {
+ group = p_group;
+ default_enabled = p_default_enabled;
+ text = p_text.utf8();
+ }
+ };
+
+private:
//versions
CharString general_defines;
- Vector<CharString> variant_defines;
+ Vector<VariantDefine> variant_defines;
Vector<bool> variants_enabled;
+ HashMap<int, LocalVector<int>> group_to_variant_map;
+ Vector<bool> group_enabled;
struct Version {
CharString uniforms;
@@ -55,7 +71,7 @@ class ShaderRD {
Vector<CharString> custom_defines;
Vector<uint8_t> *variant_data = nullptr;
- RID *variants = nullptr; //same size as version defines
+ RID *variants = nullptr; // Same size as variant defines.
bool valid;
bool dirty;
@@ -64,10 +80,17 @@ class ShaderRD {
Mutex variant_set_mutex;
- void _compile_variant(uint32_t p_variant, Version *p_version);
+ struct CompileData {
+ Version *version;
+ int group = 0;
+ };
+
+ void _compile_variant(uint32_t p_variant, const CompileData *p_data);
+ void _initialize_version(Version *p_version);
void _clear_version(Version *p_version);
- void _compile_version(Version *p_version);
+ void _compile_version(Version *p_version, int p_group);
+ void _allocate_placeholders(Version *p_version, int p_group);
RID_Owner<Version> version_owner;
@@ -97,6 +120,7 @@ class ShaderRD {
CharString base_compute_defines;
String base_sha256;
+ LocalVector<String> group_sha256;
static String shader_cache_dir;
static bool shader_cache_cleanup_on_start;
@@ -119,8 +143,9 @@ class ShaderRD {
void _add_stage(const char *p_code, StageType p_stage_type);
String _version_get_sha1(Version *p_version) const;
- bool _load_from_cache(Version *p_version);
- void _save_to_cache(Version *p_version);
+ bool _load_from_cache(Version *p_version, int p_group);
+ void _save_to_cache(Version *p_version, int p_group);
+ void _initialize_cache();
protected:
ShaderRD();
@@ -140,7 +165,14 @@ public:
ERR_FAIL_COND_V(!version, RID());
if (version->dirty) {
- _compile_version(version);
+ _initialize_version(version);
+ for (int i = 0; i < group_enabled.size(); i++) {
+ if (!group_enabled[i]) {
+ _allocate_placeholders(version, i);
+ continue;
+ }
+ _compile_version(version, i);
+ }
}
if (!version->valid) {
@@ -154,9 +186,14 @@ public:
bool version_free(RID p_version);
+ // Enable/disable variants for things that you know won't be used at engine initialization time .
void set_variant_enabled(int p_variant, bool p_enabled);
bool is_variant_enabled(int p_variant) const;
+ // Enable/disable groups for things that might be enabled at run time.
+ void enable_group(int p_group);
+ bool is_group_enabled(int p_group) const;
+
static void set_shader_cache_dir(const String &p_dir);
static void set_shader_cache_save_compressed(bool p_enable);
static void set_shader_cache_save_compressed_zstd(bool p_enable);
@@ -165,6 +202,8 @@ public:
RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version);
void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = "");
+ void initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines = "");
+
virtual ~ShaderRD();
};
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 7bfa23bfa5..1b0a3e9d0f 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -754,7 +754,9 @@ void RenderingDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("shader_compile_spirv_from_source", "shader_source", "allow_cache"), &RenderingDevice::_shader_compile_spirv_from_source, DEFVAL(true));
ClassDB::bind_method(D_METHOD("shader_compile_binary_from_spirv", "spirv_data", "name"), &RenderingDevice::_shader_compile_binary_from_spirv, DEFVAL(""));
ClassDB::bind_method(D_METHOD("shader_create_from_spirv", "spirv_data", "name"), &RenderingDevice::_shader_create_from_spirv, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("shader_create_from_bytecode", "binary_data"), &RenderingDevice::shader_create_from_bytecode);
+ ClassDB::bind_method(D_METHOD("shader_create_from_bytecode", "binary_data", "placeholder_rid"), &RenderingDevice::shader_create_from_bytecode, DEFVAL(RID()));
+ ClassDB::bind_method(D_METHOD("shader_create_placeholder"), &RenderingDevice::shader_create_placeholder);
+
ClassDB::bind_method(D_METHOD("shader_get_vertex_input_attribute_mask", "shader"), &RenderingDevice::shader_get_vertex_input_attribute_mask);
ClassDB::bind_method(D_METHOD("uniform_buffer_create", "size_bytes", "data"), &RenderingDevice::uniform_buffer_create, DEFVAL(Vector<uint8_t>()));
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 5edeb109e2..58da3df577 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -394,13 +394,13 @@ public:
enum BarrierMask {
BARRIER_MASK_VERTEX = 1,
- BARRIER_MASK_FRAGMENT = 2,
- BARRIER_MASK_COMPUTE = 4,
- BARRIER_MASK_TRANSFER = 8,
+ BARRIER_MASK_FRAGMENT = 8,
+ BARRIER_MASK_COMPUTE = 2,
+ BARRIER_MASK_TRANSFER = 4,
- BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 3,
- BARRIER_MASK_ALL_BARRIERS = BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER, // 7
- BARRIER_MASK_NO_BARRIER = 16,
+ BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 9,
+ BARRIER_MASK_ALL_BARRIERS = 0x7FFF, // all flags set
+ BARRIER_MASK_NO_BARRIER = 0x8000,
};
/*****************/
@@ -734,7 +734,8 @@ public:
virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "") = 0;
virtual RID shader_create_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "");
- virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary) = 0;
+ virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID()) = 0;
+ virtual RID shader_create_placeholder() = 0;
virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader) = 0;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index ff6bf3a6ba..c4464cb180 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -386,6 +386,12 @@ void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) {
}
}
+void RenderingServerDefault::_call_on_render_thread(const Callable &p_callable) {
+ Variant ret;
+ Callable::CallError ce;
+ p_callable.callp(nullptr, 0, ret, ce);
+}
+
RenderingServerDefault::RenderingServerDefault(bool p_create_thread) :
command_queue(p_create_thread) {
RenderingServer::init();
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index c79d3a64cf..e9b40eb082 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -97,6 +97,8 @@ class RenderingServerDefault : public RenderingServer {
void _free(RID p_rid);
+ void _call_on_render_thread(const Callable &p_callable);
+
public:
//if editor is redrawing when it shouldn't, enable this and put a breakpoint in _changes_changed()
//#define DEBUG_CHANGES
@@ -991,6 +993,15 @@ public:
virtual void init() override;
virtual void finish() override;
+ virtual void call_on_render_thread(const Callable &p_callable) override {
+ if (Thread::get_caller_id() == server_thread) {
+ command_queue.flush_if_pending();
+ _call_on_render_thread(p_callable);
+ } else {
+ command_queue.push(this, &RenderingServerDefault::_call_on_render_thread, p_callable);
+ }
+ }
+
/* TESTING */
virtual double get_frame_setup_time_cpu() const override;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 4a95dc1963..48b38cf2b7 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2811,6 +2811,8 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_draw", "swap_buffers", "frame_step"), &RenderingServer::draw, DEFVAL(true), DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("get_rendering_device"), &RenderingServer::get_rendering_device);
ClassDB::bind_method(D_METHOD("create_local_rendering_device"), &RenderingServer::create_local_rendering_device);
+
+ ClassDB::bind_method(D_METHOD("call_on_render_thread", "callable"), &RenderingServer::call_on_render_thread);
}
void RenderingServer::mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry3D::MeshData &p_mesh_data) {
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 9ea55c31b4..618ceb3091 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1602,6 +1602,8 @@ public:
bool is_render_loop_enabled() const;
void set_render_loop_enabled(bool p_enabled);
+ virtual void call_on_render_thread(const Callable &p_callable) = 0;
+
RenderingServer();
virtual ~RenderingServer();
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index a117f518de..92ab166ae8 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -115,6 +115,16 @@ TEST_CASE("[Image] Saving and loading") {
image_bmp->load_bmp_from_buffer(data_bmp) == OK,
"The BMP image should load successfully.");
+ // Load DDS
+ Ref<Image> image_dds = memnew(Image());
+ Ref<FileAccess> f_dds = FileAccess::open(TestUtils::get_data_path("images/icon.dds"), FileAccess::READ, &err);
+ PackedByteArray data_dds;
+ data_dds.resize(f_dds->get_length() + 1);
+ f_dds->get_buffer(data_dds.ptrw(), f_dds->get_length());
+ CHECK_MESSAGE(
+ image_dds->load_dds_from_buffer(data_dds) == OK,
+ "The DDS image should load successfully.");
+
// Load JPG
Ref<Image> image_jpg = memnew(Image());
Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err);
diff --git a/tests/data/images/icon.dds b/tests/data/images/icon.dds
new file mode 100644
index 0000000000..8a9de402cb
--- /dev/null
+++ b/tests/data/images/icon.dds
Binary files differ