summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorYuri Sizov <11782833+YuriSizov@users.noreply.github.com>2023-03-27 20:14:47 +0200
committerGitHub <noreply@github.com>2023-03-27 20:14:47 +0200
commit19501f8eb19481b029f67ecf78e711d42f2fc431 (patch)
tree933ea3320b35bce6ba65ab1e1d1aa8ad662c90d4 /modules
parentcacf49999e3fb37281d66cc591ca8bebc5712d4d (diff)
parent843f5adbc523ad2511322b4f09b5ce5a3fb9e225 (diff)
Merge pull request #75397 from YuriSizov/4.0-cherrypicks
Cherry-picks for the 4.0 branch (future 4.0.2) - 1st batch
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml3
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp20
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp2
-rw-r--r--modules/gridmap/grid_map.cpp8
-rw-r--r--modules/mono/csharp_script.cpp132
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props8
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj1
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props1
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs13
-rw-r--r--modules/mono/editor/bindings_generator.cpp26
-rw-r--r--modules/mono/editor/bindings_generator.h5
-rw-r--r--modules/mono/godotsharp_dirs.cpp22
-rw-r--r--modules/navigation/nav_map.cpp6
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp2
-rw-r--r--modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp2
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h4
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.cpp41
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.h6
-rw-r--r--modules/openxr/openxr_api.cpp8
-rw-r--r--modules/text_server_adv/text_server_adv.cpp81
-rw-r--r--modules/text_server_adv/text_server_adv.h2
-rw-r--r--modules/text_server_fb/text_server_fb.cpp81
-rw-r--r--modules/text_server_fb/text_server_fb.h2
23 files changed, 307 insertions, 169 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index effc0aab96..13d223b0d9 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -18,12 +18,13 @@
<param index="2" name="b8" type="int" />
<param index="3" name="a8" type="int" default="255" />
<description>
- Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value.
+ Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value. Using [method Color8] instead of the standard [Color] constructor is useful when you need to match exact color values in an [Image].
[codeblock]
var red = Color8(255, 0, 0) # Same as Color(1, 0, 0).
var dark_blue = Color8(0, 0, 51) # Same as Color(0, 0, 0.2).
var my_color = Color8(306, 255, 0, 102) # Same as Color(1.2, 1, 0, 0.4).
[/codeblock]
+ [b]Note:[/b] Due to the lower precision of [method Color8] compared to the standard [Color] constructor, a color created with [method Color8] will generally not be equal to the same color created with the standard [Color] constructor. Use [method Color.is_equal_approx] for comparisons to avoid issues with floating-point precision error.
</description>
</method>
<method name="assert">
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 5883ec863d..bba11363d5 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -294,17 +294,27 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
!(str[j] == '_' && (prev_is_digit || str[j - 1] == 'b' || str[j - 1] == 'x' || str[j - 1] == '.')) &&
!(str[j] == 'e' && (prev_is_digit || str[j - 1] == '_')) &&
!(str[j] == '.' && (prev_is_digit || (!prev_is_binary_op && (j > 0 && (str[j - 1] == '-' || str[j - 1] == '+' || str[j - 1] == '~'))))) &&
- !((str[j] == '-' || str[j] == '+' || str[j] == '~') && !prev_is_binary_op && str[j - 1] != 'e')) {
- /* This condition continues Number highlighting in special cases.
- 1st row: '+' or '-' after scientific notation;
+ !((str[j] == '-' || str[j] == '+' || str[j] == '~') && !is_binary_op && !prev_is_binary_op && str[j - 1] != 'e')) {
+ /* This condition continues number highlighting in special cases.
+ 1st row: '+' or '-' after scientific notation (like 3e-4);
2nd row: '_' as a numeric separator;
3rd row: Scientific notation 'e' and floating points;
4th row: Floating points inside the number, or leading if after a unary mathematical operator;
- 5th row: Multiple unary mathematical operators */
+ 5th row: Multiple unary mathematical operators (like ~-7)*/
in_number = false;
}
- } else if (!is_binary_op && (str[j] == '-' || str[j] == '+' || str[j] == '~' || (str[j] == '.' && str[j + 1] != '.' && (j == 0 || (j > 0 && str[j - 1] != '.'))))) {
+ } else if (str[j] == '.' && !is_binary_op && is_digit(str[j + 1]) && (j == 0 || (j > 0 && str[j - 1] != '.'))) {
+ // Start number highlighting from leading decimal points (like .42)
in_number = true;
+ } else if ((str[j] == '-' || str[j] == '+' || str[j] == '~') && !is_binary_op) {
+ // Only start number highlighting on unary operators if a digit follows them.
+ int non_op = j + 1;
+ while (str[non_op] == '-' || str[non_op] == '+' || str[non_op] == '~') {
+ non_op++;
+ }
+ if (is_digit(str[non_op]) || (str[non_op] == '.' && non_op < line_length && is_digit(str[non_op + 1]))) {
+ in_number = true;
+ }
}
if (!in_word && is_unicode_identifier_start(str[j]) && !in_number) {
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index 214d4d8ec2..541a95ab6f 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -913,7 +913,7 @@ void GridMapEditor::update_palette() {
}
void GridMapEditor::edit(GridMap *p_gridmap) {
- if (node) {
+ if (node && node->is_connected("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids))) {
node->disconnect("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids));
}
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 3c0bd56e86..db8c645558 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -557,10 +557,14 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
}
//erase navigation
- for (const KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_ids) {
- NavigationServer3D::get_singleton()->free(E.value.region);
+ for (KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_ids) {
+ if (E.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->free(E.value.region);
+ E.value.region = RID();
+ }
if (E.value.navigation_mesh_debug_instance.is_valid()) {
RS::get_singleton()->free(E.value.navigation_mesh_debug_instance);
+ E.value.navigation_mesh_debug_instance = RID();
}
}
g.navigation_cell_ids.clear();
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 1dbf6b3471..a77b1d83ad 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -334,8 +334,8 @@ bool CSharpLanguage::is_using_templates() {
}
Ref<Script> CSharpLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {
- Ref<CSharpScript> script;
- script.instantiate();
+ Ref<CSharpScript> scr;
+ scr.instantiate();
String class_name_no_spaces = p_class_name.replace(" ", "_");
String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces);
@@ -344,8 +344,8 @@ Ref<Script> CSharpLanguage::make_template(const String &p_template, const String
.replace("_BASE_", base_class_name)
.replace("_CLASS_", class_name_no_spaces)
.replace("_TS_", _get_indentation());
- script->set_source_code(processed_template);
- return script;
+ scr->set_source_code(processed_template);
+ return scr;
}
Vector<ScriptLanguage::ScriptTemplate> CSharpLanguage::get_built_in_templates(StringName p_object) {
@@ -780,28 +780,28 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// As scripts are going to be reloaded, must proceed without locking here
- for (Ref<CSharpScript> &script : scripts) {
+ for (Ref<CSharpScript> &scr : scripts) {
// If someone removes a script from a node, deletes the script, builds, adds a script to the
// same node, then builds again, the script might have no path and also no script_class. In
// that case, we can't (and don't need to) reload it.
- if (script->get_path().is_empty() && !script->valid) {
+ if (scr->get_path().is_empty() && !scr->valid) {
continue;
}
- to_reload.push_back(script);
+ to_reload.push_back(scr);
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
- for (Object *obj : script->instances) {
- script->pending_reload_instances.insert(obj->get_instance_id());
+ for (Object *obj : scr->instances) {
+ scr->pending_reload_instances.insert(obj->get_instance_id());
// Since this script instance wasn't a placeholder, add it to the list of placeholders
// that will have to be eventually replaced with a script instance in case it turns into one.
// This list is not cleared after the reload and the collected instances only leave
// the list if the script is instantiated or if it was a tool script but becomes a
// non-tool script in a rebuild.
- script->pending_replace_placeholders.insert(obj->get_instance_id());
+ scr->pending_replace_placeholders.insert(obj->get_instance_id());
RefCounted *rc = Object::cast_to<RefCounted>(obj);
if (rc) {
@@ -810,9 +810,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
#ifdef TOOLS_ENABLED
- for (PlaceHolderScriptInstance *script_instance : script->placeholders) {
- Object *obj = script_instance->get_owner();
- script->pending_reload_instances.insert(obj->get_instance_id());
+ for (PlaceHolderScriptInstance *instance : scr->placeholders) {
+ Object *obj = instance->get_owner();
+ scr->pending_reload_instances.insert(obj->get_instance_id());
RefCounted *rc = Object::cast_to<RefCounted>(obj);
if (rc) {
@@ -822,9 +822,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#endif
// Save state and remove script from instances
- RBMap<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
+ RBMap<ObjectID, CSharpScript::StateBackup> &owners_map = scr->pending_reload_state;
- for (Object *obj : script->instances) {
+ for (Object *obj : scr->instances) {
ERR_CONTINUE(!obj->get_script_instance());
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
@@ -849,14 +849,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
// After the state of all instances is saved, clear scripts and script instances
- for (Ref<CSharpScript> &script : scripts) {
- while (script->instances.begin()) {
- Object *obj = *script->instances.begin();
+ for (Ref<CSharpScript> &scr : scripts) {
+ while (scr->instances.begin()) {
+ Object *obj = *scr->instances.begin();
obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload)
}
- script->was_tool_before_reload = script->tool;
- script->_clear();
+ scr->was_tool_before_reload = scr->tool;
+ scr->_clear();
}
// Do domain reload
@@ -901,44 +901,44 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
List<Ref<CSharpScript>> to_reload_state;
- for (Ref<CSharpScript> &script : to_reload) {
+ for (Ref<CSharpScript> &scr : to_reload) {
#ifdef TOOLS_ENABLED
- script->exports_invalidated = true;
+ scr->exports_invalidated = true;
#endif
- if (!script->get_path().is_empty()) {
- script->reload(p_soft_reload);
+ if (!scr->get_path().is_empty()) {
+ scr->reload(p_soft_reload);
- if (!script->valid) {
- script->pending_reload_instances.clear();
- script->pending_reload_state.clear();
+ if (!scr->valid) {
+ scr->pending_reload_instances.clear();
+ scr->pending_reload_state.clear();
continue;
}
} else {
- bool success = GDMonoCache::managed_callbacks.ScriptManagerBridge_TryReloadRegisteredScriptWithClass(script.ptr());
+ bool success = GDMonoCache::managed_callbacks.ScriptManagerBridge_TryReloadRegisteredScriptWithClass(scr.ptr());
if (!success) {
// Couldn't reload
- script->pending_reload_instances.clear();
- script->pending_reload_state.clear();
+ scr->pending_reload_instances.clear();
+ scr->pending_reload_state.clear();
continue;
}
}
- StringName native_name = script->get_instance_base_type();
+ StringName native_name = scr->get_instance_base_type();
{
- for (const ObjectID &obj_id : script->pending_reload_instances) {
+ for (const ObjectID &obj_id : scr->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
if (!obj) {
- script->pending_reload_state.erase(obj_id);
+ scr->pending_reload_state.erase(obj_id);
continue;
}
if (!ClassDB::is_parent_class(obj->get_class_name(), native_name)) {
// No longer inherits the same compatible type, can't reload
- script->pending_reload_state.erase(obj_id);
+ scr->pending_reload_state.erase(obj_id);
continue;
}
@@ -946,11 +946,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Check if the script must be instantiated or kept as a placeholder
// when the script may not be a tool (see #65266)
- bool replace_placeholder = script->pending_replace_placeholders.has(obj->get_instance_id());
- if (!script->is_tool() && script->was_tool_before_reload) {
+ bool replace_placeholder = scr->pending_replace_placeholders.has(obj->get_instance_id());
+ if (!scr->is_tool() && scr->was_tool_before_reload) {
// The script was a tool before the rebuild so the removal was intentional.
replace_placeholder = false;
- script->pending_replace_placeholders.erase(obj->get_instance_id());
+ scr->pending_replace_placeholders.erase(obj->get_instance_id());
}
#ifdef TOOLS_ENABLED
@@ -959,20 +959,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Non-placeholder script instances are removed in godot_icall_Object_Disposed.
CRASH_COND(!si->is_placeholder());
- if (replace_placeholder || script->is_tool() || ScriptServer::is_scripting_enabled()) {
+ if (replace_placeholder || scr->is_tool() || ScriptServer::is_scripting_enabled()) {
// Replace placeholder with a script instance.
- CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
+ CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id];
// Backup placeholder script instance state before replacing it with a script instance.
si->get_property_state(state_backup.properties);
- ScriptInstance *script_instance = script->instance_create(obj);
+ ScriptInstance *instance = scr->instance_create(obj);
- if (script_instance) {
- script->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
- script->pending_replace_placeholders.erase(obj->get_instance_id());
- obj->set_script_instance(script_instance);
+ if (instance) {
+ scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
+ scr->pending_replace_placeholders.erase(obj->get_instance_id());
+ obj->set_script_instance(instance);
}
}
@@ -983,18 +983,18 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#endif
// Re-create the script instance.
- if (replace_placeholder || script->is_tool() || ScriptServer::is_scripting_enabled()) {
+ if (replace_placeholder || scr->is_tool() || ScriptServer::is_scripting_enabled()) {
// Create script instance or replace placeholder with a script instance.
- ScriptInstance *script_instance = script->instance_create(obj);
+ ScriptInstance *instance = scr->instance_create(obj);
- if (script_instance) {
- script->pending_replace_placeholders.erase(obj->get_instance_id());
- obj->set_script_instance(script_instance);
+ if (instance) {
+ scr->pending_replace_placeholders.erase(obj->get_instance_id());
+ obj->set_script_instance(instance);
continue;
}
}
// The script instance could not be instantiated or wasn't in the list of placeholders to replace.
- obj->set_script(script);
+ obj->set_script(scr);
#if DEBUG_ENABLED
// If we reached here, the instantiated script must be a placeholder.
CRASH_COND(!obj->get_script_instance()->is_placeholder());
@@ -1002,21 +1002,21 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
}
- to_reload_state.push_back(script);
+ to_reload_state.push_back(scr);
}
- for (Ref<CSharpScript> &script : to_reload_state) {
- for (const ObjectID &obj_id : script->pending_reload_instances) {
+ for (Ref<CSharpScript> &scr : to_reload_state) {
+ for (const ObjectID &obj_id : scr->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
if (!obj) {
- script->pending_reload_state.erase(obj_id);
+ scr->pending_reload_state.erase(obj_id);
continue;
}
ERR_CONTINUE(!obj->get_script_instance());
- CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
+ CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id];
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
@@ -1033,8 +1033,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
}
- script->pending_reload_instances.clear();
- script->pending_reload_state.clear();
+ scr->pending_reload_instances.clear();
+ scr->pending_reload_state.clear();
}
// Deserialize managed callables
@@ -2144,8 +2144,8 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
_update_exports_values(values, propnames);
if (changed) {
- for (PlaceHolderScriptInstance *script_instance : placeholders) {
- script_instance->update(propnames, values);
+ for (PlaceHolderScriptInstance *instance : placeholders) {
+ instance->update(propnames, values);
}
} else {
p_instance_to_update->update(propnames, values);
@@ -2711,28 +2711,28 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
// TODO ignore anything inside bin/ and obj/ in tools builds?
- Ref<CSharpScript> script;
+ Ref<CSharpScript> scr;
if (GDMonoCache::godot_api_cache_updated) {
- GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &script);
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &scr);
} else {
- script = Ref<CSharpScript>(memnew(CSharpScript));
+ scr = Ref<CSharpScript>(memnew(CSharpScript));
}
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
- Error err = script->load_source_code(p_path);
+ Error err = scr->load_source_code(p_path);
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot load C# script file '" + p_path + "'.");
#endif
- script->set_path(p_original_path);
+ scr->set_path(p_original_path);
- script->reload();
+ scr->reload();
if (r_error) {
*r_error = OK;
}
- return script;
+ return scr;
}
void ResourceFormatLoaderCSharpScript::get_recognized_extensions(List<String> *p_extensions) const {
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index 0d0889c491..894053c5de 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -14,6 +14,7 @@
<GodotProjectDir Condition=" '$(GodotProjectDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
<GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
+ <GodotProjectDirBase64 Condition=" $([MSBuild]::VersionGreaterThanOrEquals($(MSBuildAssemblyVersion), '17.3')) ">$([MSBuild]::ConvertToBase64('$(GodotProjectDir)'))</GodotProjectDirBase64>
<!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. -->
<BaseOutputPath>$(GodotProjectDir).godot\mono\temp\bin\</BaseOutputPath>
@@ -29,6 +30,13 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
+ <Target Condition=" $([MSBuild]::VersionLessThan($(MSBuildAssemblyVersion), '17.3')) " Name="GodotProjectDir" BeforeTargets="Build">
+ <Error Text="Cannot build from path containing '%23', move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%23'))" /> <!-- # -->
+ <Error Text="Cannot build from path containing '%3B', move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%3B'))" /> <!-- ; -->
+ <Error Text="Cannot build from path containing newlines, move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%0A'))" /> <!-- \n -->
+ <Error Text="Cannot build from path containing newlines, move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%0D'))" /> <!-- \r -->
+ </Target>
+
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
<PropertyGroup>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
index 8e78e0385d..3f569ebac3 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
@@ -7,6 +7,7 @@
<PropertyGroup>
<!-- $(GodotProjectDir) would normally be defined by the Godot.NET.Sdk -->
<GodotProjectDir>$(MSBuildProjectDirectory)</GodotProjectDir>
+ <GodotProjectDirBase64 Condition=" $([MSBuild]::VersionGreaterThanOrEquals($(MSBuildAssemblyVersion), '17.3')) ">$([MSBuild]::ConvertToBase64('$(GodotProjectDir)'))</GodotProjectDirBase64>
<!-- For compiling GetGodotPropertyDefaultValues. -->
<DefineConstants>$(DefineConstants);TOOLS</DefineConstants>
</PropertyGroup>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
index 7881ed0a8c..2a8ae7f958 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
@@ -2,6 +2,7 @@
<ItemGroup>
<!-- $(GodotProjectDir) is defined by Godot.NET.Sdk -->
<CompilerVisibleProperty Include="GodotProjectDir" />
+ <CompilerVisibleProperty Include="GodotProjectDirBase64" />
<CompilerVisibleProperty Include="GodotSourceGenerators" />
<CompilerVisibleProperty Include="IsGodotToolsProject" />
</ItemGroup>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
index eae7e41da8..d14e3c3781 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
@@ -22,10 +22,17 @@ namespace Godot.SourceGenerators
// NOTE: NotNullWhen diagnostics don't work on projects targeting .NET Standard 2.0
// ReSharper disable once ReplaceWithStringIsNullOrEmpty
- if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out string? godotProjectDir)
- || godotProjectDir!.Length == 0)
+ if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDirBase64", out string? godotProjectDir) || godotProjectDir!.Length == 0)
{
- throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
+ if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out godotProjectDir) || godotProjectDir!.Length == 0)
+ {
+ throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
+ }
+ }
+ else
+ {
+ // Workaround for https://github.com/dotnet/roslyn/issues/51692
+ godotProjectDir = Encoding.UTF8.GetString(Convert.FromBase64String(godotProjectDir));
}
Dictionary<INamedTypeSymbol, IEnumerable<ClassDeclarationSyntax>> godotClasses = context
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 83101c1443..6690a3badb 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -523,7 +523,10 @@ void BindingsGenerator::_append_xml_method(StringBuilder &p_xml_output, const Ty
p_xml_output.append(target_imethod->proxy_name);
p_xml_output.append("\"/>");
} else {
- ERR_PRINT("Cannot resolve method reference in documentation: '" + p_link_target + "'.");
+ if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
+ ERR_PRINT("Cannot resolve method reference in documentation: '" + p_link_target + "'.");
+ }
+
_append_xml_undeclared(p_xml_output, p_link_target);
}
}
@@ -563,7 +566,10 @@ void BindingsGenerator::_append_xml_member(StringBuilder &p_xml_output, const Ty
p_xml_output.append(target_iprop->proxy_name);
p_xml_output.append("\"/>");
} else {
- ERR_PRINT("Cannot resolve member reference in documentation: '" + p_link_target + "'.");
+ if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
+ ERR_PRINT("Cannot resolve member reference in documentation: '" + p_link_target + "'.");
+ }
+
_append_xml_undeclared(p_xml_output, p_link_target);
}
}
@@ -591,7 +597,10 @@ void BindingsGenerator::_append_xml_signal(StringBuilder &p_xml_output, const Ty
p_xml_output.append(target_isignal->proxy_name);
p_xml_output.append("\"/>");
} else {
- ERR_PRINT("Cannot resolve signal reference in documentation: '" + p_link_target + "'.");
+ if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
+ ERR_PRINT("Cannot resolve signal reference in documentation: '" + p_link_target + "'.");
+ }
+
_append_xml_undeclared(p_xml_output, p_link_target);
}
}
@@ -613,7 +622,10 @@ void BindingsGenerator::_append_xml_enum(StringBuilder &p_xml_output, const Type
p_xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any
p_xml_output.append("\"/>");
} else {
- ERR_PRINT("Cannot resolve enum reference in documentation: '" + p_link_target + "'.");
+ if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
+ ERR_PRINT("Cannot resolve enum reference in documentation: '" + p_link_target + "'.");
+ }
+
_append_xml_undeclared(p_xml_output, p_link_target);
}
}
@@ -673,7 +685,10 @@ void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const
// Also search in @GlobalScope as a last resort if no class was specified
_append_xml_constant_in_global_scope(p_xml_output, p_target_cname, p_link_target);
} else {
- ERR_PRINT("Cannot resolve constant reference in documentation: '" + p_link_target + "'.");
+ if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
+ ERR_PRINT("Cannot resolve constant reference in documentation: '" + p_link_target + "'.");
+ }
+
_append_xml_undeclared(p_xml_output, p_link_target);
}
}
@@ -2936,6 +2951,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
if (method_has_ptr_parameter(method_info)) {
// Pointers are not supported.
+ itype.ignored_members.insert(method_info.name);
continue;
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 5c266ed31f..eac281ddb4 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -408,6 +408,7 @@ class BindingsGenerator {
List<PropertyInterface> properties;
List<MethodInterface> methods;
List<SignalInterface> signals_;
+ HashSet<String> ignored_members;
bool has_virtual_methods = false;
@@ -471,6 +472,10 @@ class BindingsGenerator {
return nullptr;
}
+ bool is_intentionally_ignored(const String &p_name) const {
+ return ignored_members.has(p_name);
+ }
+
private:
static DocData::ClassDoc *_get_type_doc(TypeInterface &itype) {
String doc_name = itype.name.begins_with("_") ? itype.name.substr(1) : itype.name;
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index d08dedcfcb..bff7d04b55 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -66,23 +66,25 @@ String _get_mono_user_dir() {
if (EditorPaths::get_singleton()) {
return EditorPaths::get_singleton()->get_data_dir().path_join("mono");
} else {
- String settings_path;
+ String settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name());
// Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir.
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
-
- // On macOS, look outside .app bundle, since .app bundle is read-only.
- if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) {
- exe_dir = exe_dir.path_join("../../..").simplify_path();
- }
-
Ref<DirAccess> d = DirAccess::create_for_path(exe_dir);
-
if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
// contain yourself
settings_path = exe_dir.path_join("editor_data");
- } else {
- settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name());
+ }
+
+ // On macOS, look outside .app bundle, since .app bundle is read-only.
+ // Note: This will not work if Gatekeeper path randomization is active.
+ if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) {
+ exe_dir = exe_dir.path_join("../../..").simplify_path();
+ d = DirAccess::create_for_path(exe_dir);
+ if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
+ // contain yourself
+ settings_path = exe_dir.path_join("editor_data");
+ }
}
return settings_path.path_join("mono");
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index b1674c8fc5..4dcdbd6446 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -894,9 +894,9 @@ void NavMap::sync() {
if (agents_dirty) {
// cannot use LocalVector here as RVO library expects std::vector to build KdTree
std::vector<RVO::Agent *> raw_agents;
- raw_agents.reserve(agents.size());
- for (NavAgent *agent : agents) {
- raw_agents.push_back(agent->get_agent());
+ raw_agents.reserve(controlled_agents.size());
+ for (NavAgent *controlled_agent : controlled_agents) {
+ raw_agents.push_back(controlled_agent->get_agent());
}
rvo.buildAgentTree(raw_agents);
}
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index 669c395b3e..d2f6be2233 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -178,6 +178,7 @@ void OpenXRActionMap::create_default_action_sets() {
Ref<OpenXRAction> grip = action_set->add_new_action("grip", "Grip", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_click = action_set->add_new_action("grip_click", "Grip click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_touch = action_set->add_new_action("grip_touch", "Grip touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
+ Ref<OpenXRAction> grip_force = action_set->add_new_action("grip_force", "Grip force", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary = action_set->add_new_action("primary", "Primary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary_click = action_set->add_new_action("primary_click", "Primary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary_touch = action_set->add_new_action("primary_touch", "Primary joystick/thumbstick/trackpad touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
@@ -349,6 +350,7 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // this should do a float to bool conversion
+ profile->add_new_binding(grip_force, "/user/hand/left/input/squeeze/force,/user/hand/right/input/squeeze/force"); // grip force seems to be unique to the Valve Index
// primary on our index controller is our thumbstick
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
diff --git a/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp b/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp
index 1118b53d65..70879c6b6b 100644
--- a/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp
+++ b/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp
@@ -376,7 +376,9 @@ void OpenXRInteractionProfileMetaData::_register_core_metadata() {
register_io_path("/interaction_profiles/valve/index_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
register_io_path("/interaction_profiles/valve/index_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/valve/index_controller", "Squeeze force", "/user/hand/left", "/user/hand/left/input/squeeze/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path("/interaction_profiles/valve/index_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/valve/index_controller", "Squeeze force", "/user/hand/right", "/user/hand/right/input/squeeze/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index 84279635b5..2c855c3cde 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -80,7 +80,9 @@ public:
// this happens right before physics process and normal processing is run.
// This is when controller data is queried and made available to game logic.
virtual void on_process() {}
- virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewport.
+ virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewports.
+ virtual void on_pre_draw_viewport(RID p_render_target) {} // `on_pre_draw_viewport` is called right before we start rendering this viewport
+ virtual void on_post_draw_viewport(RID p_render_target) {} // `on_port_draw_viewport` is called right after we start rendering this viewport (note that on Vulkan draw commands may only be queued)
virtual void on_state_idle() {} // `on_state_idle` is called when the OpenXR session state is changed to idle.
virtual void on_state_ready() {} // `on_state_ready` is called when the OpenXR session state is changed to ready, this means OpenXR is ready to setup our session.
diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
index 0d201161f1..20ccfe3906 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -37,6 +37,28 @@
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"
+// OpenXR requires us to submit sRGB textures so that it recognises the content
+// as being in sRGB color space. We do fall back on "normal" textures but this
+// will likely result in incorrect colors as OpenXR will double the sRGB conversion.
+// All major XR runtimes support sRGB textures.
+
+// In OpenGL output of the fragment shader is assumed to be in the color space of
+// the developers choice, however a linear to sRGB HW conversion can be enabled
+// through enabling GL_FRAMEBUFFER_SRGB if an sRGB color attachment is used.
+// This is a global setting.
+// See: https://www.khronos.org/opengl/wiki/Framebuffer
+
+// In OpenGLES output of the fragment shader is assumed to be in linear color space
+// and will be converted by default to sRGB if an sRGB color attachment is used.
+// The extension GL_EXT_sRGB_write_control was introduced to enable turning this
+// feature off.
+// See: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_sRGB_write_control.txt
+
+// On OpenGLES this is not defined in our standard headers..
+#ifndef GL_FRAMEBUFFER_SRGB
+#define GL_FRAMEBUFFER_SRGB 0x8DB9
+#endif
+
HashMap<String, bool *> OpenXROpenGLExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
@@ -157,8 +179,8 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
}
void OpenXROpenGLExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
- p_usable_swap_chains.push_back(GL_RGBA8);
p_usable_swap_chains.push_back(GL_SRGB8_ALPHA8);
+ p_usable_swap_chains.push_back(GL_RGBA8);
}
void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_depth_formats) {
@@ -168,6 +190,23 @@ void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_d
p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT24);
}
+void OpenXROpenGLExtension::on_pre_draw_viewport(RID p_render_target) {
+ if (srgb_ext_is_available) {
+ hw_linear_to_srgb_is_enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB);
+ if (hw_linear_to_srgb_is_enabled) {
+ // Disable this.
+ glDisable(GL_FRAMEBUFFER_SRGB);
+ }
+ }
+}
+
+void OpenXROpenGLExtension::on_post_draw_viewport(RID p_render_target) {
+ if (srgb_ext_is_available && hw_linear_to_srgb_is_enabled) {
+ // Re-enable this.
+ glEnable(GL_FRAMEBUFFER_SRGB);
+ }
+}
+
bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
ERR_FAIL_NULL_V(texture_storage, false);
diff --git a/modules/openxr/extensions/openxr_opengl_extension.h b/modules/openxr/extensions/openxr_opengl_extension.h
index 03a640cbfb..29a9f35c51 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.h
+++ b/modules/openxr/extensions/openxr_opengl_extension.h
@@ -79,6 +79,9 @@ public:
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
+ virtual void on_pre_draw_viewport(RID p_render_target) override;
+ virtual void on_post_draw_viewport(RID p_render_target) override;
+
virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override;
virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) override;
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override;
@@ -103,6 +106,9 @@ private:
Vector<RID> texture_rids;
};
+ bool srgb_ext_is_available = true;
+ bool hw_linear_to_srgb_is_enabled = false;
+
bool check_graphics_api_support(XrVersion p_desired_version);
#ifdef ANDROID_ENABLED
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index ddb3114b59..af59fe7dde 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -1815,6 +1815,10 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
}
}
+ for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
+ wrapper->on_pre_draw_viewport(p_render_target);
+ }
+
return true;
}
@@ -1839,7 +1843,9 @@ void OpenXRAPI::post_draw_viewport(RID p_render_target) {
return;
}
- // Nothing to do here at this point in time...
+ for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
+ wrapper->on_post_draw_viewport(p_render_target);
+ }
};
void OpenXRAPI::end_frame() {
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 3463cb5d9d..3cf6288fef 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -386,6 +386,8 @@ int64_t TextServerAdvanced::_get_features() const {
void TextServerAdvanced::_free_rid(const RID &p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
+ MutexLock ftlock(ft_mutex);
+
FontAdvanced *fd = font_owner.get_or_null(p_rid);
{
MutexLock lock(fd->mutex);
@@ -1321,45 +1323,48 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
// Init dynamic font.
#ifdef MODULE_FREETYPE_ENABLED
int error = 0;
- if (!ft_library) {
- error = FT_Init_FreeType(&ft_library);
- if (error != 0) {
- memdelete(fd);
- ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
- }
+ {
+ MutexLock ftlock(ft_mutex);
+ if (!ft_library) {
+ error = FT_Init_FreeType(&ft_library);
+ if (error != 0) {
+ memdelete(fd);
+ ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
#ifdef MODULE_SVG_ENABLED
- FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
+ FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
#endif
- }
-
- memset(&fd->stream, 0, sizeof(FT_StreamRec));
- fd->stream.base = (unsigned char *)p_font_data->data_ptr;
- fd->stream.size = p_font_data->data_size;
- fd->stream.pos = 0;
-
- FT_Open_Args fargs;
- memset(&fargs, 0, sizeof(FT_Open_Args));
- fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
- fargs.memory_size = p_font_data->data_size;
- fargs.flags = FT_OPEN_MEMORY;
- fargs.stream = &fd->stream;
+ }
- int max_index = 0;
- FT_Face tmp_face = nullptr;
- error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
- if (tmp_face && error == 0) {
- max_index = tmp_face->num_faces - 1;
- }
- if (tmp_face) {
- FT_Done_Face(tmp_face);
- }
+ memset(&fd->stream, 0, sizeof(FT_StreamRec));
+ fd->stream.base = (unsigned char *)p_font_data->data_ptr;
+ fd->stream.size = p_font_data->data_size;
+ fd->stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
+ fargs.memory_size = p_font_data->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &fd->stream;
+
+ int max_index = 0;
+ FT_Face tmp_face = nullptr;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (tmp_face && error == 0) {
+ max_index = tmp_face->num_faces - 1;
+ }
+ if (tmp_face) {
+ FT_Done_Face(tmp_face);
+ }
- error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
- if (error) {
- FT_Done_Face(fd->face);
- fd->face = nullptr;
- memdelete(fd);
- ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
+ error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
+ if (error) {
+ FT_Done_Face(fd->face);
+ fd->face = nullptr;
+ memdelete(fd);
+ ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
+ }
}
if (p_font_data->msdf) {
@@ -1788,6 +1793,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
}
_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontAdvanced *p_font_data) {
+ MutexLock ftlock(ft_mutex);
+
for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : p_font_data->cache) {
memdelete(E.value);
}
@@ -1894,6 +1901,8 @@ int64_t TextServerAdvanced::_font_get_face_count(const RID &p_font_rid) const {
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &stream;
+ MutexLock ftlock(ft_mutex);
+
FT_Face tmp_face = nullptr;
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
if (error == 0) {
@@ -2285,6 +2294,7 @@ void TextServerAdvanced::_font_clear_size_cache(const RID &p_font_rid) {
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
+ MutexLock ftlock(ft_mutex);
for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
memdelete(E.value);
}
@@ -2296,6 +2306,7 @@ void TextServerAdvanced::_font_remove_size_cache(const RID &p_font_rid, const Ve
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
+ MutexLock ftlock(ft_mutex);
if (fd->cache.has(p_size)) {
memdelete(fd->cache[p_size]);
fd->cache.erase(p_size);
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index f092fa8cca..ce08cf7694 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -616,6 +616,8 @@ class TextServerAdvanced : public TextServerExtension {
_FORCE_INLINE_ void _add_featuers(const Dictionary &p_source, Vector<hb_feature_t> &r_ftrs);
+ Mutex ft_mutex;
+
// HarfBuzz bitmap font interface.
static hb_font_funcs_t *funcs;
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 15124ad488..8687726287 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -113,6 +113,8 @@ int64_t TextServerFallback::_get_features() const {
void TextServerFallback::_free_rid(const RID &p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
+ MutexLock ftlock(ft_mutex);
+
FontFallback *fd = font_owner.get_or_null(p_rid);
{
MutexLock lock(fd->mutex);
@@ -760,45 +762,48 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
// Init dynamic font.
#ifdef MODULE_FREETYPE_ENABLED
int error = 0;
- if (!ft_library) {
- error = FT_Init_FreeType(&ft_library);
- if (error != 0) {
- memdelete(fd);
- ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
- }
+ {
+ MutexLock ftlock(ft_mutex);
+ if (!ft_library) {
+ error = FT_Init_FreeType(&ft_library);
+ if (error != 0) {
+ memdelete(fd);
+ ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
#ifdef MODULE_SVG_ENABLED
- FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
+ FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
#endif
- }
-
- memset(&fd->stream, 0, sizeof(FT_StreamRec));
- fd->stream.base = (unsigned char *)p_font_data->data_ptr;
- fd->stream.size = p_font_data->data_size;
- fd->stream.pos = 0;
-
- FT_Open_Args fargs;
- memset(&fargs, 0, sizeof(FT_Open_Args));
- fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
- fargs.memory_size = p_font_data->data_size;
- fargs.flags = FT_OPEN_MEMORY;
- fargs.stream = &fd->stream;
+ }
- int max_index = 0;
- FT_Face tmp_face = nullptr;
- error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
- if (tmp_face && error == 0) {
- max_index = tmp_face->num_faces - 1;
- }
- if (tmp_face) {
- FT_Done_Face(tmp_face);
- }
+ memset(&fd->stream, 0, sizeof(FT_StreamRec));
+ fd->stream.base = (unsigned char *)p_font_data->data_ptr;
+ fd->stream.size = p_font_data->data_size;
+ fd->stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
+ fargs.memory_size = p_font_data->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &fd->stream;
+
+ int max_index = 0;
+ FT_Face tmp_face = nullptr;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (tmp_face && error == 0) {
+ max_index = tmp_face->num_faces - 1;
+ }
+ if (tmp_face) {
+ FT_Done_Face(tmp_face);
+ }
- error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
- if (error) {
- FT_Done_Face(fd->face);
- fd->face = nullptr;
- memdelete(fd);
- ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
+ error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
+ if (error) {
+ FT_Done_Face(fd->face);
+ fd->face = nullptr;
+ memdelete(fd);
+ ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
+ }
}
if (p_font_data->msdf) {
@@ -909,6 +914,8 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
}
_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontFallback *p_font_data) {
+ MutexLock ftlock(ft_mutex);
+
for (const KeyValue<Vector2i, FontForSizeFallback *> &E : p_font_data->cache) {
memdelete(E.value);
}
@@ -1012,6 +1019,8 @@ int64_t TextServerFallback::_font_get_face_count(const RID &p_font_rid) const {
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &stream;
+ MutexLock ftlock(ft_mutex);
+
FT_Face tmp_face = nullptr;
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
if (error == 0) {
@@ -1393,6 +1402,7 @@ void TextServerFallback::_font_clear_size_cache(const RID &p_font_rid) {
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
+ MutexLock ftlock(ft_mutex);
for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
memdelete(E.value);
}
@@ -1404,6 +1414,7 @@ void TextServerFallback::_font_remove_size_cache(const RID &p_font_rid, const Ve
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
+ MutexLock ftlock(ft_mutex);
if (fd->cache.has(p_size)) {
memdelete(fd->cache[p_size]);
fd->cache.erase(p_size);
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 12ed21ee95..d9e471154d 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -531,6 +531,8 @@ class TextServerFallback : public TextServerExtension {
void _realign(ShapedTextDataFallback *p_sd) const;
+ Mutex ft_mutex;
+
protected:
static void _bind_methods(){};