summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/gdscript.cpp40
-rw-r--r--modules/gdscript/gdscript.h26
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp2
-rw-r--r--modules/gdscript/gdscript_cache.cpp3
-rw-r--r--modules/gdscript/gdscript_compiler.cpp147
-rw-r--r--modules/gdscript/gdscript_compiler.h35
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp52
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h11
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp1
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp174
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.h19
-rw-r--r--modules/lightmapper_rd/lm_common_inc.glsl21
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl149
-rwxr-xr-xmodules/mono/build_scripts/build_assemblies.py28
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/.gitignore2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/NestedClass.cs22
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs77
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs10
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs10
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs10
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs10
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs10
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs1
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs34
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs29
-rw-r--r--modules/navigation/nav_region.cpp4
-rw-r--r--modules/openxr/SCsub1
30 files changed, 806 insertions, 156 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 58aa7a08d4..6245cc85a0 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1386,6 +1386,36 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
}
#endif
+thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local;
+
+GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
+ UpdatableFuncPtrElement result = {};
+
+ {
+ MutexLock lock(func_ptrs_to_update_thread_local.mutex);
+ result.element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr);
+ result.mutex = &func_ptrs_to_update_thread_local.mutex;
+
+ if (likely(func_ptrs_to_update_thread_local.initialized)) {
+ return result;
+ }
+
+ func_ptrs_to_update_thread_local.initialized = true;
+ }
+
+ MutexLock lock(func_ptrs_to_update_mutex);
+ func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local);
+
+ return result;
+}
+
+void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element) {
+ ERR_FAIL_NULL(p_func_ptr_element.element);
+ ERR_FAIL_NULL(p_func_ptr_element.mutex);
+ MutexLock lock(*p_func_ptr_element.mutex);
+ p_func_ptr_element.element->erase();
+}
+
void GDScript::clear(ClearData *p_clear_data) {
if (clearing) {
return;
@@ -1403,6 +1433,16 @@ void GDScript::clear(ClearData *p_clear_data) {
is_root = true;
}
+ {
+ MutexLock outer_lock(func_ptrs_to_update_mutex);
+ for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
+ MutexLock inner_lock(updatable->mutex);
+ for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
+ *func_ptr_ptr = nullptr;
+ }
+ }
+ }
+
RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
for (GDScript *E : must_clear_dependencies) {
clear_data->scripts.insert(E);
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index d335ec85ee..04b0a1d786 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -86,6 +86,8 @@ class GDScript : public Script {
friend class GDScriptAnalyzer;
friend class GDScriptCompiler;
friend class GDScriptDocGen;
+ friend class GDScriptLambdaCallable;
+ friend class GDScriptLambdaSelfCallable;
friend class GDScriptLanguage;
friend struct GDScriptUtilityFunctionsDefinitions;
@@ -108,6 +110,30 @@ class GDScript : public Script {
HashMap<StringName, MethodInfo> _signals;
Dictionary rpc_config;
+ struct LambdaInfo {
+ int capture_count;
+ bool use_self;
+ };
+
+ HashMap<GDScriptFunction *, LambdaInfo> lambda_info;
+
+ // List is used here because a ptr to elements are stored, so the memory locations need to be stable
+ struct UpdatableFuncPtr {
+ List<GDScriptFunction **> ptrs;
+ Mutex mutex;
+ bool initialized = false;
+ };
+ struct UpdatableFuncPtrElement {
+ List<GDScriptFunction **>::Element *element = nullptr;
+ Mutex *mutex = nullptr;
+ };
+ static thread_local UpdatableFuncPtr func_ptrs_to_update_thread_local;
+ List<UpdatableFuncPtr *> func_ptrs_to_update;
+ Mutex func_ptrs_to_update_mutex;
+
+ UpdatableFuncPtrElement _add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr);
+ static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element);
+
#ifdef TOOLS_ENABLED
// For static data storage during hot-reloading.
HashMap<StringName, MemberInfo> old_static_variables_indices;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index f3f95ba50b..2439e5760c 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -4343,7 +4343,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
} else if (base_type.kind != GDScriptParser::DataType::BUILTIN && !index_type.is_variant()) {
if (index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME) {
- push_error(vformat(R"(Only String or StringName can be used as index for type "%s", but received a "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index);
+ push_error(vformat(R"(Only "String" or "StringName" can be used as index for type "%s", but received "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index);
}
}
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 18609d0b80..26f01ec218 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -287,7 +287,8 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
if (script.is_null()) {
script = get_shallow_script(p_path, r_error);
- if (r_error) {
+ // Only exit early if script failed to load, otherwise let reload report errors.
+ if (script.is_null()) {
return script;
}
}
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index bf648abc9e..7980f020b8 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1371,6 +1371,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
return GDScriptCodeGenerator::Address();
}
+ main_script->lambda_info.insert(function, { lambda->captures.size(), lambda->use_self });
gen->write_lambda(result, function, captures, lambda->use_self);
for (int i = 0; i < captures.size(); i++) {
@@ -2631,6 +2632,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
p_script->implicit_ready = nullptr;
p_script->static_initializer = nullptr;
p_script->rpc_config.clear();
+ p_script->lambda_info.clear();
p_script->clearing = false;
@@ -3040,6 +3042,128 @@ void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::Cl
}
}
+GDScriptCompiler::FunctionLambdaInfo GDScriptCompiler::_get_function_replacement_info(GDScriptFunction *p_func, int p_index, int p_depth, GDScriptFunction *p_parent_func) {
+ FunctionLambdaInfo info;
+ info.function = p_func;
+ info.parent = p_parent_func;
+ info.script = p_parent_func;
+ info.name = p_func->get_name();
+ info.line = p_func->_initial_line;
+ info.index = p_index;
+ info.depth = p_depth;
+ info.capture_count = 0;
+ info.use_self = false;
+ info.arg_count = p_func->_argument_count;
+ info.default_arg_count = p_func->_default_arg_count;
+ info.sublambdas = _get_function_lambda_replacement_info(p_func, p_depth, p_parent_func);
+
+ GDScript::LambdaInfo *extra_info = main_script->lambda_info.getptr(p_func);
+ if (extra_info != nullptr) {
+ info.capture_count = extra_info->capture_count;
+ info.use_self = extra_info->use_self;
+ }
+
+ return info;
+}
+
+Vector<GDScriptCompiler::FunctionLambdaInfo> GDScriptCompiler::_get_function_lambda_replacement_info(GDScriptFunction *p_func, int p_depth, GDScriptFunction *p_parent_func) {
+ Vector<FunctionLambdaInfo> result;
+ // Only scrape the lambdas inside p_func.
+ for (int i = 0; i < p_func->lambdas.size(); ++i) {
+ result.push_back(_get_function_replacement_info(p_func->lambdas[i], i, p_depth + 1, p_func));
+ }
+ return result;
+}
+
+GDScriptCompiler::ScriptLambdaInfo GDScriptCompiler::_get_script_lambda_replacement_info(GDScript *p_script) {
+ ScriptLambdaInfo info;
+
+ if (p_script->implicit_initializer) {
+ info.implicit_initializer_info = _get_function_lambda_replacement_info(p_script->implicit_initializer);
+ }
+ if (p_script->implicit_ready) {
+ info.implicit_ready_info = _get_function_lambda_replacement_info(p_script->implicit_ready);
+ }
+ if (p_script->static_initializer) {
+ info.static_initializer_info = _get_function_lambda_replacement_info(p_script->static_initializer);
+ }
+
+ for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {
+ info.member_function_infos.insert(E.key, _get_function_lambda_replacement_info(E.value));
+ }
+
+ for (const KeyValue<StringName, Ref<GDScript>> &KV : p_script->get_subclasses()) {
+ info.subclass_info.insert(KV.key, _get_script_lambda_replacement_info(KV.value.ptr()));
+ }
+
+ return info;
+}
+
+bool GDScriptCompiler::_do_function_infos_match(const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) {
+ if (p_new_info == nullptr) {
+ return false;
+ }
+
+ if (p_new_info->capture_count != p_old_info.capture_count || p_new_info->use_self != p_old_info.use_self) {
+ return false;
+ }
+
+ int old_required_arg_count = p_old_info.arg_count - p_old_info.default_arg_count;
+ int new_required_arg_count = p_new_info->arg_count - p_new_info->default_arg_count;
+ if (new_required_arg_count > old_required_arg_count || p_new_info->arg_count < old_required_arg_count) {
+ return false;
+ }
+
+ return true;
+}
+
+void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) {
+ ERR_FAIL_COND(r_replacements.has(p_old_info.function));
+ if (!_do_function_infos_match(p_old_info, p_new_info)) {
+ p_new_info = nullptr;
+ }
+
+ r_replacements.insert(p_old_info.function, p_new_info != nullptr ? p_new_info->function : nullptr);
+ _get_function_ptr_replacements(r_replacements, p_old_info.sublambdas, p_new_info != nullptr ? &p_new_info->sublambdas : nullptr);
+}
+
+void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const Vector<FunctionLambdaInfo> &p_old_infos, const Vector<FunctionLambdaInfo> *p_new_infos) {
+ for (int i = 0; i < p_old_infos.size(); ++i) {
+ const FunctionLambdaInfo &old_info = p_old_infos[i];
+ const FunctionLambdaInfo *new_info = nullptr;
+ if (p_new_infos != nullptr && p_new_infos->size() == p_old_infos.size()) {
+ // For now only attempt if the size is the same.
+ new_info = &p_new_infos->get(i);
+ }
+ _get_function_ptr_replacements(r_replacements, old_info, new_info);
+ }
+}
+
+void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const ScriptLambdaInfo &p_old_info, const ScriptLambdaInfo *p_new_info) {
+ _get_function_ptr_replacements(r_replacements, p_old_info.implicit_initializer_info, p_new_info != nullptr ? &p_new_info->implicit_initializer_info : nullptr);
+ _get_function_ptr_replacements(r_replacements, p_old_info.implicit_ready_info, p_new_info != nullptr ? &p_new_info->implicit_ready_info : nullptr);
+ _get_function_ptr_replacements(r_replacements, p_old_info.static_initializer_info, p_new_info != nullptr ? &p_new_info->static_initializer_info : nullptr);
+
+ for (const KeyValue<StringName, Vector<FunctionLambdaInfo>> &old_kv : p_old_info.member_function_infos) {
+ _get_function_ptr_replacements(r_replacements, old_kv.value, p_new_info != nullptr ? p_new_info->member_function_infos.getptr(old_kv.key) : nullptr);
+ }
+ for (int i = 0; i < p_old_info.other_function_infos.size(); ++i) {
+ const FunctionLambdaInfo &old_other_info = p_old_info.other_function_infos[i];
+ const FunctionLambdaInfo *new_other_info = nullptr;
+ if (p_new_info != nullptr && p_new_info->other_function_infos.size() == p_old_info.other_function_infos.size()) {
+ // For now only attempt if the size is the same.
+ new_other_info = &p_new_info->other_function_infos[i];
+ }
+ // Needs to be called on all old lambdas, even if there's no replacement.
+ _get_function_ptr_replacements(r_replacements, old_other_info, new_other_info);
+ }
+ for (const KeyValue<StringName, ScriptLambdaInfo> &old_kv : p_old_info.subclass_info) {
+ const ScriptLambdaInfo &old_subinfo = old_kv.value;
+ const ScriptLambdaInfo *new_subinfo = p_new_info != nullptr ? p_new_info->subclass_info.getptr(old_kv.key) : nullptr;
+ _get_function_ptr_replacements(r_replacements, old_subinfo, new_subinfo);
+ }
+}
+
Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
err_line = -1;
err_column = -1;
@@ -3050,6 +3174,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
source = p_script->get_path();
+ ScriptLambdaInfo old_lambda_info = _get_script_lambda_replacement_info(p_script);
+
// Create scripts for subclasses beforehand so they can be referenced
make_scripts(p_script, root, p_keep_state);
@@ -3065,6 +3191,27 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
return err;
}
+ ScriptLambdaInfo new_lambda_info = _get_script_lambda_replacement_info(p_script);
+
+ HashMap<GDScriptFunction *, GDScriptFunction *> func_ptr_replacements;
+ _get_function_ptr_replacements(func_ptr_replacements, old_lambda_info, &new_lambda_info);
+
+ {
+ MutexLock outer_lock(main_script->func_ptrs_to_update_mutex);
+ for (GDScript::UpdatableFuncPtr *updatable : main_script->func_ptrs_to_update) {
+ MutexLock inner_lock(updatable->mutex);
+ for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
+ GDScriptFunction **replacement = func_ptr_replacements.getptr(*func_ptr_ptr);
+ if (replacement != nullptr) {
+ *func_ptr_ptr = *replacement;
+ } else {
+ // Probably a lambda from another reload, ignore.
+ *func_ptr_ptr = nullptr;
+ }
+ }
+ }
+ }
+
if (has_static_data && !root->annotated_static_unload) {
GDScriptCache::add_static_script(p_script);
}
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 099bd00a2e..fd6b22f527 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -44,6 +44,34 @@ class GDScriptCompiler {
HashSet<GDScript *> parsing_classes;
GDScript *main_script = nullptr;
+ struct FunctionLambdaInfo {
+ GDScriptFunction *function;
+ GDScriptFunction *parent;
+ Ref<GDScript> script;
+ StringName name;
+ int line;
+ int index;
+ int depth;
+ //uint64_t code_hash;
+ //int code_size;
+ int capture_count;
+ int use_self;
+ int arg_count;
+ int default_arg_count;
+ //Vector<GDScriptDataType> argument_types;
+ //GDScriptDataType return_type;
+ Vector<FunctionLambdaInfo> sublambdas;
+ };
+
+ struct ScriptLambdaInfo {
+ Vector<FunctionLambdaInfo> implicit_initializer_info;
+ Vector<FunctionLambdaInfo> implicit_ready_info;
+ Vector<FunctionLambdaInfo> static_initializer_info;
+ HashMap<StringName, Vector<FunctionLambdaInfo>> member_function_infos;
+ Vector<FunctionLambdaInfo> other_function_infos;
+ HashMap<StringName, ScriptLambdaInfo> subclass_info;
+ };
+
struct CodeGen {
GDScript *script = nullptr;
const GDScriptParser::ClassNode *class_node = nullptr;
@@ -137,6 +165,13 @@ class GDScriptCompiler {
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
Error _prepare_compilation(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ FunctionLambdaInfo _get_function_replacement_info(GDScriptFunction *p_func, int p_index = -1, int p_depth = 0, GDScriptFunction *p_parent_func = nullptr);
+ Vector<FunctionLambdaInfo> _get_function_lambda_replacement_info(GDScriptFunction *p_func, int p_depth = 0, GDScriptFunction *p_parent_func = nullptr);
+ ScriptLambdaInfo _get_script_lambda_replacement_info(GDScript *p_script);
+ bool _do_function_infos_match(const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info);
+ void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info);
+ void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const Vector<FunctionLambdaInfo> &p_old_infos, const Vector<FunctionLambdaInfo> *p_new_infos);
+ void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const ScriptLambdaInfo &p_old_info, const ScriptLambdaInfo *p_new_info);
int err_line = 0;
int err_column = 0;
StringName source;
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index 9d0fce0928..547f5607d3 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -44,11 +44,18 @@ bool GDScriptLambdaCallable::compare_less(const CallableCustom *p_a, const Calla
return p_a < p_b;
}
+bool GDScriptLambdaCallable::is_valid() const {
+ return CallableCustom::is_valid() && function != nullptr;
+}
+
uint32_t GDScriptLambdaCallable::hash() const {
return h;
}
String GDScriptLambdaCallable::get_as_text() const {
+ if (function == nullptr) {
+ return "<invalid lambda>";
+ }
if (function->get_name() != StringName()) {
return function->get_name().operator String() + "(lambda)";
}
@@ -74,6 +81,12 @@ StringName GDScriptLambdaCallable::get_method() const {
void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
int captures_amount = captures.size();
+ if (function == nullptr) {
+ r_return_value = Variant();
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
+ }
+
if (captures_amount > 0) {
Vector<const Variant *> args;
args.resize(p_argcount + captures_amount);
@@ -127,11 +140,19 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V
}
GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+ ERR_FAIL_NULL(p_script.ptr());
+ ERR_FAIL_NULL(p_function);
script = p_script;
function = p_function;
captures = p_captures;
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
+
+ updatable_func_ptr_element = p_script->_add_func_ptr_to_update(&function);
+}
+
+GDScriptLambdaCallable::~GDScriptLambdaCallable() {
+ GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
}
bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -144,11 +165,18 @@ bool GDScriptLambdaSelfCallable::compare_less(const CallableCustom *p_a, const C
return p_a < p_b;
}
+bool GDScriptLambdaSelfCallable::is_valid() const {
+ return CallableCustom::is_valid() && function != nullptr;
+}
+
uint32_t GDScriptLambdaSelfCallable::hash() const {
return h;
}
String GDScriptLambdaSelfCallable::get_as_text() const {
+ if (function == nullptr) {
+ return "<invalid lambda>";
+ }
if (function->get_name() != StringName()) {
return function->get_name().operator String() + "(lambda)";
}
@@ -178,6 +206,12 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
int captures_amount = captures.size();
+ if (function == nullptr) {
+ r_return_value = Variant();
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
+ }
+
if (captures_amount > 0) {
Vector<const Variant *> args;
args.resize(p_argcount + captures_amount);
@@ -231,18 +265,36 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
}
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+ ERR_FAIL_NULL(p_self.ptr());
+ ERR_FAIL_NULL(p_function);
reference = p_self;
object = p_self.ptr();
function = p_function;
captures = p_captures;
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
+
+ GDScript *gds = p_function->get_script();
+ if (gds != nullptr) {
+ updatable_func_ptr_element = gds->_add_func_ptr_to_update(&function);
+ }
}
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+ ERR_FAIL_NULL(p_self);
+ ERR_FAIL_NULL(p_function);
object = p_self;
function = p_function;
captures = p_captures;
h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
+
+ GDScript *gds = p_function->get_script();
+ if (gds != nullptr) {
+ updatable_func_ptr_element = gds->_add_func_ptr_to_update(&function);
+ }
+}
+
+GDScriptLambdaSelfCallable::~GDScriptLambdaSelfCallable() {
+ GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
}
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index 1c7a18fb9d..ee7d547544 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -31,12 +31,13 @@
#ifndef GDSCRIPT_LAMBDA_CALLABLE_H
#define GDSCRIPT_LAMBDA_CALLABLE_H
+#include "gdscript.h"
+
#include "core/object/ref_counted.h"
#include "core/templates/vector.h"
#include "core/variant/callable.h"
#include "core/variant/variant.h"
-class GDScript;
class GDScriptFunction;
class GDScriptInstance;
@@ -44,6 +45,7 @@ class GDScriptLambdaCallable : public CallableCustom {
GDScriptFunction *function = nullptr;
Ref<GDScript> script;
uint32_t h;
+ GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
Vector<Variant> captures;
@@ -51,6 +53,7 @@ class GDScriptLambdaCallable : public CallableCustom {
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
public:
+ bool is_valid() const override;
uint32_t hash() const override;
String get_as_text() const override;
CompareEqualFunc get_compare_equal_func() const override;
@@ -60,7 +63,7 @@ public:
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
- virtual ~GDScriptLambdaCallable() = default;
+ virtual ~GDScriptLambdaCallable();
};
// Lambda callable that references a particular object, so it can use `self` in the body.
@@ -69,6 +72,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom {
Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference.
Object *object = nullptr; // For non RefCounted objects, use a direct pointer.
uint32_t h;
+ GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
Vector<Variant> captures;
@@ -76,6 +80,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom {
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
public:
+ bool is_valid() const override;
uint32_t hash() const override;
String get_as_text() const override;
CompareEqualFunc get_compare_equal_func() const override;
@@ -85,7 +90,7 @@ public:
GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
- virtual ~GDScriptLambdaSelfCallable() = default;
+ virtual ~GDScriptLambdaSelfCallable();
};
#endif // GDSCRIPT_LAMBDA_CALLABLE_H
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
index a6d2388a91..df014d3cfe 100644
--- a/modules/gdscript/gdscript_rpc_callable.cpp
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -73,6 +73,7 @@ void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Vari
}
GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_method) {
+ ERR_FAIL_NULL(p_object);
object = p_object;
method = p_method;
h = method.hash();
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index feb9a2274e..fe919953c1 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -124,7 +124,7 @@ void LightmapperRD::add_probe(const Vector3 &p_position) {
probe_positions.push_back(probe);
}
-void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[3], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size) {
+void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[3], uint32_t p_triangle_index, LocalVector<TriangleSort> &p_triangles_sort, uint32_t p_grid_size) {
int half_size = p_size / 2;
for (int i = 0; i < 8; i++) {
@@ -159,13 +159,69 @@ void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Ve
TriangleSort ts;
ts.cell_index = n.x + (n.y * p_grid_size) + (n.z * p_grid_size * p_grid_size);
ts.triangle_index = p_triangle_index;
- triangles.push_back(ts);
+ ts.triangle_aabb.position = p_points[0];
+ ts.triangle_aabb.size = Vector3();
+ ts.triangle_aabb.expand_to(p_points[1]);
+ ts.triangle_aabb.expand_to(p_points[2]);
+ p_triangles_sort.push_back(ts);
} else {
- _plot_triangle_into_triangle_index_list(half_size, n, aabb, p_points, p_triangle_index, triangles, p_grid_size);
+ _plot_triangle_into_triangle_index_list(half_size, n, aabb, p_points, p_triangle_index, p_triangles_sort, p_grid_size);
}
}
}
+void LightmapperRD::_sort_triangle_clusters(uint32_t p_cluster_size, uint32_t p_cluster_index, uint32_t p_index_start, uint32_t p_count, LocalVector<TriangleSort> &p_triangle_sort, LocalVector<ClusterAABB> &p_cluster_aabb) {
+ if (p_count == 0) {
+ return;
+ }
+
+ // Compute AABB for all triangles in the range.
+ SortArray<TriangleSort, TriangleSortAxis<0>> triangle_sorter_x;
+ SortArray<TriangleSort, TriangleSortAxis<1>> triangle_sorter_y;
+ SortArray<TriangleSort, TriangleSortAxis<2>> triangle_sorter_z;
+ AABB cluster_aabb = p_triangle_sort[p_index_start].triangle_aabb;
+ for (uint32_t i = 1; i < p_count; i++) {
+ cluster_aabb.merge_with(p_triangle_sort[p_index_start + i].triangle_aabb);
+ }
+
+ if (p_count > p_cluster_size) {
+ int longest_axis_index = cluster_aabb.get_longest_axis_index();
+ switch (longest_axis_index) {
+ case 0:
+ triangle_sorter_x.sort(&p_triangle_sort[p_index_start], p_count);
+ break;
+ case 1:
+ triangle_sorter_y.sort(&p_triangle_sort[p_index_start], p_count);
+ break;
+ case 2:
+ triangle_sorter_z.sort(&p_triangle_sort[p_index_start], p_count);
+ break;
+ default:
+ DEV_ASSERT(false && "Invalid axis returned by AABB.");
+ break;
+ }
+
+ uint32_t left_cluster_count = next_power_of_2(p_count / 2);
+ left_cluster_count = MAX(left_cluster_count, p_cluster_size);
+ left_cluster_count = MIN(left_cluster_count, p_count);
+ _sort_triangle_clusters(p_cluster_size, p_cluster_index, p_index_start, left_cluster_count, p_triangle_sort, p_cluster_aabb);
+
+ if (left_cluster_count < p_count) {
+ uint32_t cluster_index_right = p_cluster_index + (left_cluster_count / p_cluster_size);
+ _sort_triangle_clusters(p_cluster_size, cluster_index_right, p_index_start + left_cluster_count, p_count - left_cluster_count, p_triangle_sort, p_cluster_aabb);
+ }
+ } else {
+ ClusterAABB &aabb = p_cluster_aabb[p_cluster_index];
+ Vector3 aabb_end = cluster_aabb.get_end();
+ aabb.min_bounds[0] = cluster_aabb.position.x;
+ aabb.min_bounds[1] = cluster_aabb.position.y;
+ aabb.min_bounds[2] = cluster_aabb.position.z;
+ aabb.max_bounds[0] = aabb_end.x;
+ aabb.max_bounds[1] = aabb_end.y;
+ aabb.max_bounds[2] = aabb_end.z;
+ }
+}
+
Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata) {
Vector<Size2i> sizes;
@@ -281,7 +337,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
return BAKE_OK;
}
-void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &p_probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
+void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, uint32_t p_cluster_size, Vector<Probe> &p_probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &r_triangle_indices_buffer, RID &r_cluster_indices_buffer, RID &r_cluster_aabbs_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
HashMap<Vertex, uint32_t, VertexHash> vertex_map;
//fill triangles array and vertex array
@@ -433,31 +489,70 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
//sort it
triangle_sort.sort();
+ LocalVector<uint32_t> cluster_indices;
+ LocalVector<ClusterAABB> cluster_aabbs;
Vector<uint32_t> triangle_indices;
triangle_indices.resize(triangle_sort.size());
Vector<uint32_t> grid_indices;
grid_indices.resize(grid_size * grid_size * grid_size * 2);
memset(grid_indices.ptrw(), 0, grid_indices.size() * sizeof(uint32_t));
- Vector<bool> solid;
- solid.resize(grid_size * grid_size * grid_size);
- memset(solid.ptrw(), 0, solid.size() * sizeof(bool));
{
- uint32_t *tiw = triangle_indices.ptrw();
+ // Fill grid with cell indices.
uint32_t last_cell = 0xFFFFFFFF;
uint32_t *giw = grid_indices.ptrw();
- bool *solidw = solid.ptrw();
+ uint32_t cluster_count = 0;
+ uint32_t solid_cell_count = 0;
for (uint32_t i = 0; i < triangle_sort.size(); i++) {
uint32_t cell = triangle_sort[i].cell_index;
if (cell != last_cell) {
- //cell changed, update pointer to indices
- giw[cell * 2 + 1] = i;
- solidw[cell] = true;
+ giw[cell * 2 + 1] = solid_cell_count;
+ solid_cell_count++;
}
- tiw[i] = triangle_sort[i].triangle_index;
- giw[cell * 2]++; //update counter
+
+ if ((giw[cell * 2] % p_cluster_size) == 0) {
+ // Add an extra cluster every time the triangle counter reaches a multiple of the cluster size.
+ cluster_count++;
+ }
+
+ giw[cell * 2]++;
last_cell = cell;
}
+
+ // Build fixed-size triangle clusters for all the cells to speed up the traversal. A cell can hold multiple clusters that each contain a fixed
+ // amount of triangles and an AABB. The tracer will check against the AABBs first to know whether it needs to visit the cell's triangles.
+ //
+ // The building algorithm will divide the triangles recursively contained inside each cell, sorting by the longest axis of the AABB on each step.
+ //
+ // - If the amount of triangles is less or equal to the cluster size, the AABB will be stored and the algorithm stops.
+ //
+ // - The division by two is increased to the next power of two of half the amount of triangles (with cluster size as the minimum value) to
+ // ensure the first half always fills the cluster.
+
+ cluster_indices.resize(solid_cell_count * 2);
+ cluster_aabbs.resize(cluster_count);
+
+ uint32_t i = 0;
+ uint32_t cluster_index = 0;
+ uint32_t solid_cell_index = 0;
+ uint32_t *tiw = triangle_indices.ptrw();
+ while (i < triangle_sort.size()) {
+ cluster_indices[solid_cell_index * 2] = cluster_index;
+ cluster_indices[solid_cell_index * 2 + 1] = i;
+
+ uint32_t cell = triangle_sort[i].cell_index;
+ uint32_t triangle_count = giw[cell * 2];
+ uint32_t cell_cluster_count = (triangle_count + p_cluster_size - 1) / p_cluster_size;
+ _sort_triangle_clusters(p_cluster_size, cluster_index, i, triangle_count, triangle_sort, cluster_aabbs);
+
+ for (uint32_t j = 0; j < triangle_count; j++) {
+ tiw[i + j] = triangle_sort[i + j].triangle_index;
+ }
+
+ i += triangle_count;
+ cluster_index += cell_cluster_count;
+ solid_cell_index++;
+ }
}
#if 0
for (int i = 0; i < grid_size; i++) {
@@ -507,7 +602,13 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
triangle_buffer = rd->storage_buffer_create(tb.size(), tb);
Vector<uint8_t> tib = triangle_indices.to_byte_array();
- triangle_cell_indices_buffer = rd->storage_buffer_create(tib.size(), tib);
+ r_triangle_indices_buffer = rd->storage_buffer_create(tib.size(), tib);
+
+ Vector<uint8_t> cib = cluster_indices.to_byte_array();
+ r_cluster_indices_buffer = rd->storage_buffer_create(cib.size(), cib);
+
+ Vector<uint8_t> cab = cluster_aabbs.to_byte_array();
+ r_cluster_aabbs_buffer = rd->storage_buffer_create(cab.size(), cab);
Vector<uint8_t> lb = lights.to_byte_array();
if (lb.size() == 0) {
@@ -1020,24 +1121,29 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RID vertex_buffer;
RID triangle_buffer;
RID lights_buffer;
- RID triangle_cell_indices_buffer;
+ RID triangle_indices_buffer;
+ RID cluster_indices_buffer;
+ RID cluster_aabbs_buffer;
RID grid_texture;
RID seams_buffer;
RID probe_positions_buffer;
Vector<int> slice_seam_count;
-#define FREE_BUFFERS \
- rd->free(bake_parameters_buffer); \
- rd->free(vertex_buffer); \
- rd->free(triangle_buffer); \
- rd->free(lights_buffer); \
- rd->free(triangle_cell_indices_buffer); \
- rd->free(grid_texture); \
- rd->free(seams_buffer); \
+#define FREE_BUFFERS \
+ rd->free(bake_parameters_buffer); \
+ rd->free(vertex_buffer); \
+ rd->free(triangle_buffer); \
+ rd->free(lights_buffer); \
+ rd->free(triangle_indices_buffer); \
+ rd->free(cluster_indices_buffer); \
+ rd->free(cluster_aabbs_buffer); \
+ rd->free(grid_texture); \
+ rd->free(seams_buffer); \
rd->free(probe_positions_buffer);
- _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata);
+ const uint32_t cluster_size = 16;
+ _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, cluster_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, lights_buffer, triangle_indices_buffer, cluster_indices_buffer, cluster_aabbs_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata);
// Create global bake parameters buffer.
BakeParameters bake_parameters;
@@ -1133,7 +1239,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 3;
- u.append_id(triangle_cell_indices_buffer);
+ u.append_id(triangle_indices_buffer);
base_uniforms.push_back(u);
}
{
@@ -1185,6 +1291,20 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
u.append_id(sampler);
base_uniforms.push_back(u);
}
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 11;
+ u.append_id(cluster_indices_buffer);
+ base_uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 12;
+ u.append_id(cluster_aabbs_buffer);
+ base_uniforms.push_back(u);
+ }
}
RID raster_base_uniform = rd->uniform_set_create(base_uniforms, rasterize_shader, 0);
@@ -1230,6 +1350,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Ref<RDShaderFile> compute_shader;
String defines = "";
+ defines += "\n#define CLUSTER_SIZE " + uitos(cluster_size) + "\n";
+
if (p_bake_sh) {
defines += "\n#define USE_SH_LIGHTMAPS\n";
}
diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h
index 8c1c4deba6..5414048ddc 100644
--- a/modules/lightmapper_rd/lightmapper_rd.h
+++ b/modules/lightmapper_rd/lightmapper_rd.h
@@ -192,6 +192,13 @@ class LightmapperRD : public Lightmapper {
}
};
+ struct ClusterAABB {
+ float min_bounds[3];
+ float pad0 = 0.0f;
+ float max_bounds[3];
+ float pad1 = 0.0f;
+ };
+
Vector<MeshInstance> mesh_instances;
Vector<Light> lights;
@@ -199,12 +206,22 @@ class LightmapperRD : public Lightmapper {
struct TriangleSort {
uint32_t cell_index = 0;
uint32_t triangle_index = 0;
+ AABB triangle_aabb;
+
bool operator<(const TriangleSort &p_triangle_sort) const {
return cell_index < p_triangle_sort.cell_index; //sorting by triangle index in this case makes no sense
}
};
+ template <int T>
+ struct TriangleSortAxis {
+ bool operator()(const TriangleSort &p_a, const TriangleSort &p_b) const {
+ return p_a.triangle_aabb.get_center()[T] < p_b.triangle_aabb.get_center()[T];
+ }
+ };
+
void _plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[3], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size);
+ void _sort_triangle_clusters(uint32_t p_cluster_size, uint32_t p_cluster_index, uint32_t p_index_start, uint32_t p_count, LocalVector<TriangleSort> &p_triangle_sort, LocalVector<ClusterAABB> &p_cluster_aabb);
struct RasterPushConstant {
float atlas_size[2] = {};
@@ -250,7 +267,7 @@ class LightmapperRD : public Lightmapper {
};
BakeError _blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata);
- void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
+ void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, uint32_t p_cluster_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &r_triangle_indices_buffer, RID &r_cluster_indices_buffer, RID &r_cluster_aabbs_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform);
BakeError _dilate(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices);
diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl
index c91f06d0f3..98d11b9e69 100644
--- a/modules/lightmapper_rd/lm_common_inc.glsl
+++ b/modules/lightmapper_rd/lm_common_inc.glsl
@@ -42,15 +42,22 @@ struct Triangle {
uint pad1;
};
+struct ClusterAABB {
+ vec3 min_bounds;
+ uint pad0;
+ vec3 max_bounds;
+ uint pad1;
+};
+
layout(set = 0, binding = 2, std430) restrict readonly buffer Triangles {
Triangle data[];
}
triangles;
-layout(set = 0, binding = 3, std430) restrict readonly buffer GridIndices {
+layout(set = 0, binding = 3, std430) restrict readonly buffer TriangleIndices {
uint data[];
}
-grid_indices;
+triangle_indices;
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_OMNI 1
@@ -104,6 +111,16 @@ layout(set = 0, binding = 9) uniform texture2DArray emission_tex;
layout(set = 0, binding = 10) uniform sampler linear_sampler;
+layout(set = 0, binding = 11, std430) restrict readonly buffer ClusterIndices {
+ uint data[];
+}
+cluster_indices;
+
+layout(set = 0, binding = 12, std430) restrict readonly buffer ClusterAABBs {
+ ClusterAABB data[];
+}
+cluster_aabbs;
+
// Fragment action constants
const uint FA_NONE = 0;
const uint FA_SMOOTHEN_POSITION = 1;
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index 572e6d55d8..a2a480043a 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -119,6 +119,17 @@ const uint RAY_FRONT = 1;
const uint RAY_BACK = 2;
const uint RAY_ANY = 3;
+bool ray_box_test(vec3 p_from, vec3 p_inv_dir, vec3 p_box_min, vec3 p_box_max) {
+ vec3 t0 = (p_box_min - p_from) * p_inv_dir;
+ vec3 t1 = (p_box_max - p_from) * p_inv_dir;
+ vec3 tmin = min(t0, t1), tmax = max(t0, t1);
+ return max(tmin.x, max(tmin.y, tmin.z)) <= min(tmax.x, min(tmax.y, tmax.z));
+}
+
+#if CLUSTER_SIZE > 32
+#define CLUSTER_TRIANGLE_ITERATION
+#endif
+
uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out vec3 r_normal, out uint r_triangle, out vec3 r_barycentric) {
// World coordinates.
vec3 rel = p_to - p_from;
@@ -142,60 +153,106 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
uint iters = 0;
while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(bake_params.grid_size))) && (iters < 1000)) {
uvec2 cell_data = texelFetch(usampler3D(grid, linear_sampler), icell, 0).xy;
- if (cell_data.x > 0) { //triangles here
+ uint triangle_count = cell_data.x;
+ if (triangle_count > 0) {
uint hit = RAY_MISS;
float best_distance = 1e20;
-
- for (uint i = 0; i < cell_data.x; i++) {
- uint tidx = grid_indices.data[cell_data.y + i];
-
- // Ray-Box test.
- Triangle triangle = triangles.data[tidx];
- vec3 t0 = (triangle.min_bounds - p_from) * inv_dir;
- vec3 t1 = (triangle.max_bounds - p_from) * inv_dir;
- vec3 tmin = min(t0, t1), tmax = max(t0, t1);
-
- if (max(tmin.x, max(tmin.y, tmin.z)) > min(tmax.x, min(tmax.y, tmax.z))) {
- continue; // Ray-Box test failed.
- }
-
- // Prepare triangle vertices.
- vec3 vtx0 = vertices.data[triangle.indices.x].position;
- vec3 vtx1 = vertices.data[triangle.indices.y].position;
- vec3 vtx2 = vertices.data[triangle.indices.z].position;
- vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2)));
- bool backface = dot(normal, dir) >= 0.0;
- float distance;
- vec3 barycentric;
- if (ray_hits_triangle(p_from, dir, rel_len, vtx0, vtx1, vtx2, distance, barycentric)) {
- if (p_any_hit) {
- // Return early if any hit was requested.
- return RAY_ANY;
+ uint cluster_start = cluster_indices.data[cell_data.y * 2];
+ uint cell_triangle_start = cluster_indices.data[cell_data.y * 2 + 1];
+ uint cluster_count = (triangle_count + CLUSTER_SIZE - 1) / CLUSTER_SIZE;
+ uint cluster_base_index = 0;
+ while (cluster_base_index < cluster_count) {
+ // To minimize divergence, all Ray-AABB tests on the clusters contained in the cell are performed
+ // before checking against the triangles. We do this 32 clusters at a time and store the intersected
+ // clusters on each bit of the 32-bit integer.
+ uint cluster_test_count = min(32, cluster_count - cluster_base_index);
+ uint cluster_hits = 0;
+ for (uint i = 0; i < cluster_test_count; i++) {
+ uint cluster_index = cluster_start + cluster_base_index + i;
+ ClusterAABB cluster_aabb = cluster_aabbs.data[cluster_index];
+ if (ray_box_test(p_from, inv_dir, cluster_aabb.min_bounds, cluster_aabb.max_bounds)) {
+ cluster_hits |= (1 << i);
}
+ }
- vec3 position = p_from + dir * distance;
- vec3 hit_cell = (position - bake_params.to_cell_offset) * bake_params.to_cell_size;
- if (icell != ivec3(hit_cell)) {
- // It's possible for the ray to hit a triangle in a position outside the bounds of the cell
- // if it's large enough to cover multiple ones. The hit must be ignored if this is the case.
- continue;
- }
+ // Check the triangles in any of the clusters that were intersected by toggling off the bits in the
+ // 32-bit integer counter until no bits are left.
+ while (cluster_hits > 0) {
+ uint cluster_index = findLSB(cluster_hits);
+ cluster_hits &= ~(1 << cluster_index);
+ cluster_index += cluster_base_index;
+
+ // Do the same divergence execution trick with triangles as well.
+ uint triangle_base_index = 0;
+#ifdef CLUSTER_TRIANGLE_ITERATION
+ while (triangle_base_index < triangle_count)
+#endif
+ {
+ uint triangle_start_index = cell_triangle_start + cluster_index * CLUSTER_SIZE + triangle_base_index;
+ uint triangle_test_count = min(CLUSTER_SIZE, triangle_count - triangle_base_index);
+ uint triangle_hits = 0;
+ for (uint i = 0; i < triangle_test_count; i++) {
+ uint triangle_index = triangle_indices.data[triangle_start_index + i];
+ if (ray_box_test(p_from, inv_dir, triangles.data[triangle_index].min_bounds, triangles.data[triangle_index].max_bounds)) {
+ triangle_hits |= (1 << i);
+ }
+ }
- if (!backface) {
- // The case of meshes having both a front and back face in the same plane is more common than expected.
- // If this is a front-face, bias it closer to the ray origin, so it always wins over the back-face.
- distance = max(bake_params.bias, distance - bake_params.bias);
- }
+ while (triangle_hits > 0) {
+ uint cluster_triangle_index = findLSB(triangle_hits);
+ triangle_hits &= ~(1 << cluster_triangle_index);
+ cluster_triangle_index += triangle_start_index;
+
+ uint triangle_index = triangle_indices.data[cluster_triangle_index];
+ Triangle triangle = triangles.data[triangle_index];
+
+ // Gather the triangle vertex positions.
+ vec3 vtx0 = vertices.data[triangle.indices.x].position;
+ vec3 vtx1 = vertices.data[triangle.indices.y].position;
+ vec3 vtx2 = vertices.data[triangle.indices.z].position;
+ vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2)));
+ bool backface = dot(normal, dir) >= 0.0;
+ float distance;
+ vec3 barycentric;
+ if (ray_hits_triangle(p_from, dir, rel_len, vtx0, vtx1, vtx2, distance, barycentric)) {
+ if (p_any_hit) {
+ // Return early if any hit was requested.
+ return RAY_ANY;
+ }
+
+ vec3 position = p_from + dir * distance;
+ vec3 hit_cell = (position - bake_params.to_cell_offset) * bake_params.to_cell_size;
+ if (icell != ivec3(hit_cell)) {
+ // It's possible for the ray to hit a triangle in a position outside the bounds of the cell
+ // if it's large enough to cover multiple ones. The hit must be ignored if this is the case.
+ continue;
+ }
+
+ if (!backface) {
+ // The case of meshes having both a front and back face in the same plane is more common than
+ // expected, so if this is a front-face, bias it closer to the ray origin, so it always wins
+ // over the back-face.
+ distance = max(bake_params.bias, distance - bake_params.bias);
+ }
+
+ if (distance < best_distance) {
+ hit = backface ? RAY_BACK : RAY_FRONT;
+ best_distance = distance;
+ r_distance = distance;
+ r_normal = normal;
+ r_triangle = triangle_index;
+ r_barycentric = barycentric;
+ }
+ }
+ }
- if (distance < best_distance) {
- hit = backface ? RAY_BACK : RAY_FRONT;
- best_distance = distance;
- r_distance = distance;
- r_normal = normal;
- r_triangle = tidx;
- r_barycentric = barycentric;
+#ifdef CLUSTER_TRIANGLE_ITERATION
+ triangle_base_index += CLUSTER_SIZE;
+#endif
}
}
+
+ cluster_base_index += 32;
}
if (hit != RAY_MISS) {
diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py
index 580f51c973..90a517cc40 100755
--- a/modules/mono/build_scripts/build_assemblies.py
+++ b/modules/mono/build_scripts/build_assemblies.py
@@ -316,9 +316,35 @@ def generate_sdk_package_versions():
f.write(props)
f.close()
+ # Also write the versioned docs URL to a constant for the Source Generators.
+
+ constants = """namespace Godot.SourceGenerators
+{{
+ partial class Common
+ {{
+ public const string VersionDocsUrl = "https://docs.godotengine.org/en/{docs_branch}";
+ }}
+}}
+""".format(
+ **version_info
+ )
+
+ generators_dir = os.path.join(
+ dirname(script_path),
+ "editor",
+ "Godot.NET.Sdk",
+ "Godot.SourceGenerators",
+ "Generated",
+ )
+ os.makedirs(generators_dir, exist_ok=True)
+
+ with open(os.path.join(generators_dir, "Common.Constants.cs"), "w") as f:
+ f.write(constants)
+ f.close()
+
def build_all(msbuild_tool, module_dir, output_dir, godot_platform, dev_debug, push_nupkgs_local, precision):
- # Generate SdkPackageVersions.props
+ # Generate SdkPackageVersions.props and VersionDocsUrl constant
generate_sdk_package_versions()
# Godot API
diff --git a/modules/mono/editor/Godot.NET.Sdk/.gitignore b/modules/mono/editor/Godot.NET.Sdk/.gitignore
new file mode 100644
index 0000000000..55ec4bcc64
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/.gitignore
@@ -0,0 +1,2 @@
+# Generated sources directories
+Godot.SourceGenerators/Generated
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/NestedClass.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/NestedClass.cs
new file mode 100644
index 0000000000..3021f57115
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/NestedClass.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Godot.SourceGenerators.Sample;
+
+public partial class NestedClass : GodotObject
+{
+ public partial class NestedClass2 : GodotObject
+ {
+ public partial class NestedClass3 : GodotObject
+ {
+ [Signal]
+ public delegate void MySignalEventHandler(string str, int num);
+
+ [Export] private String field_String = "foo";
+ [Export] private String property_String { get; set; } = "foo";
+
+ private void Method()
+ {
+ }
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
index 72614dd7e0..df35091596 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -5,8 +5,10 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace Godot.SourceGenerators
{
- public static class Common
+ public static partial class Common
{
+ private static readonly string _helpLinkFormat = $"{VersionDocsUrl}/tutorials/scripting/c_sharp/diagnostics/{{0}}.html";
+
public static void ReportNonPartialGodotScriptClass(
GeneratorExecutionContext context,
ClassDeclarationSyntax cds, INamedTypeSymbol symbol
@@ -14,9 +16,9 @@ namespace Godot.SourceGenerators
{
string message =
"Missing partial modifier on declaration of type '" +
- $"{symbol.FullQualifiedNameOmitGlobal()}' which is a subclass of '{GodotClasses.GodotObject}'";
+ $"{symbol.FullQualifiedNameOmitGlobal()}' that derives from '{GodotClasses.GodotObject}'";
- string description = $"{message}. Subclasses of '{GodotClasses.GodotObject}' " +
+ string description = $"{message}. Classes that derive from '{GodotClasses.GodotObject}' " +
"must be declared with the partial modifier.";
context.ReportDiagnostic(Diagnostic.Create(
@@ -26,7 +28,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0001")),
cds.GetLocation(),
cds.SyntaxTree.FilePath));
}
@@ -46,9 +49,9 @@ namespace Godot.SourceGenerators
string message =
$"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
- $"which contains one or more subclasses of '{GodotClasses.GodotObject}'";
+ $"which contains nested classes that derive from '{GodotClasses.GodotObject}'";
- string description = $"{message}. Subclasses of '{GodotClasses.GodotObject}' and their " +
+ string description = $"{message}. Classes that derive from '{GodotClasses.GodotObject}' and their " +
"containing types must be declared with the partial modifier.";
context.ReportDiagnostic(Diagnostic.Create(
@@ -58,7 +61,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0002")),
outerTypeDeclSyntax.GetLocation(),
outerTypeDeclSyntax.SyntaxTree.FilePath));
}
@@ -85,7 +89,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0101")),
location,
location?.SourceTree?.FilePath));
}
@@ -111,7 +116,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0102")),
location,
location?.SourceTree?.FilePath));
}
@@ -139,7 +145,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD1003")),
location,
location?.SourceTree?.FilePath));
}
@@ -163,7 +170,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0104")),
location,
location?.SourceTree?.FilePath));
}
@@ -189,7 +197,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0105")),
location,
location?.SourceTree?.FilePath));
}
@@ -215,7 +224,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0106")),
location,
location?.SourceTree?.FilePath));
}
@@ -240,7 +250,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0201")),
location,
location?.SourceTree?.FilePath));
}
@@ -264,7 +275,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0202")),
location,
location?.SourceTree?.FilePath));
}
@@ -288,7 +300,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0203")),
location,
location?.SourceTree?.FilePath));
}
@@ -300,7 +313,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.");
+ "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0301"));
public static void ReportGenericTypeArgumentMustBeVariant(
SyntaxNodeAnalysisContext context,
@@ -319,7 +333,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0301")),
typeArgumentSyntax.GetLocation(),
typeArgumentSyntax.SyntaxTree.FilePath));
}
@@ -331,7 +346,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
+ "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0302"));
public static void ReportGenericTypeParameterMustBeVariantAnnotated(
SyntaxNodeAnalysisContext context,
@@ -349,7 +365,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0302")),
typeArgumentSyntax.GetLocation(),
typeArgumentSyntax.SyntaxTree.FilePath));
}
@@ -361,7 +378,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
+ "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0303"));
public static void ReportTypeArgumentParentSymbolUnhandled(
SyntaxNodeAnalysisContext context,
@@ -380,7 +398,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0303")),
typeArgumentSyntax.GetLocation(),
typeArgumentSyntax.SyntaxTree.FilePath));
}
@@ -388,11 +407,12 @@ namespace Godot.SourceGenerators
public static readonly DiagnosticDescriptor GlobalClassMustDeriveFromGodotObjectRule =
new DiagnosticDescriptor(id: "GD0401",
title: "The class must derive from GodotObject or a derived class",
- messageFormat: "The class '{0}' must derive from GodotObject or a derived class.",
+ messageFormat: "The class '{0}' must derive from GodotObject or a derived class",
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute.");
+ "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0401"));
public static void ReportGlobalClassMustDeriveFromGodotObject(
SyntaxNodeAnalysisContext context,
@@ -410,7 +430,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0401")),
classSyntax.GetLocation(),
classSyntax.SyntaxTree.FilePath));
}
@@ -422,7 +443,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute.");
+ "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0401"));
public static void ReportGlobalClassMustNotBeGeneric(
SyntaxNodeAnalysisContext context,
@@ -440,7 +462,8 @@ namespace Godot.SourceGenerators
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- description),
+ description,
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0402")),
classSyntax.GetLocation(),
classSyntax.SyntaxTree.FilePath));
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
index 7b643914bb..7232e4d7d7 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -105,16 +105,20 @@ namespace Godot.SourceGenerators
if (isInnerClass)
{
var containingType = symbol.ContainingType;
+ AppendPartialContainingTypeDeclarations(containingType);
- while (containingType != null)
+ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
{
+ if (containingType == null)
+ return;
+
+ AppendPartialContainingTypeDeclarations(containingType.ContainingType);
+
source.Append("partial ");
source.Append(containingType.GetDeclarationKeyword());
source.Append(" ");
source.Append(containingType.NameWithTypeParameters());
source.Append("\n{\n");
-
- containingType = containingType.ContainingType;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index 219ab7aa44..de44ada6de 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -91,16 +91,20 @@ namespace Godot.SourceGenerators
if (isInnerClass)
{
var containingType = symbol.ContainingType;
+ AppendPartialContainingTypeDeclarations(containingType);
- while (containingType != null)
+ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
{
+ if (containingType == null)
+ return;
+
+ AppendPartialContainingTypeDeclarations(containingType.ContainingType);
+
source.Append("partial ");
source.Append(containingType.GetDeclarationKeyword());
source.Append(" ");
source.Append(containingType.NameWithTypeParameters());
source.Append("\n{\n");
-
- containingType = containingType.ContainingType;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
index 4df16d05f0..fc0bfbf084 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -88,16 +88,20 @@ namespace Godot.SourceGenerators
if (isInnerClass)
{
var containingType = symbol.ContainingType;
+ AppendPartialContainingTypeDeclarations(containingType);
- while (containingType != null)
+ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
{
+ if (containingType == null)
+ return;
+
+ AppendPartialContainingTypeDeclarations(containingType.ContainingType);
+
source.Append("partial ");
source.Append(containingType.GetDeclarationKeyword());
source.Append(" ");
source.Append(containingType.NameWithTypeParameters());
source.Append("\n{\n");
-
- containingType = containingType.ContainingType;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
index 9de99414b6..0bc58c2b47 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
@@ -91,16 +91,20 @@ namespace Godot.SourceGenerators
if (isInnerClass)
{
var containingType = symbol.ContainingType;
+ AppendPartialContainingTypeDeclarations(containingType);
- while (containingType != null)
+ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
{
+ if (containingType == null)
+ return;
+
+ AppendPartialContainingTypeDeclarations(containingType.ContainingType);
+
source.Append("partial ");
source.Append(containingType.GetDeclarationKeyword());
source.Append(" ");
source.Append(containingType.NameWithTypeParameters());
source.Append("\n{\n");
-
- containingType = containingType.ContainingType;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 8f2774d5ae..5409d1a961 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -100,16 +100,20 @@ namespace Godot.SourceGenerators
if (isInnerClass)
{
var containingType = symbol.ContainingType;
+ AppendPartialContainingTypeDeclarations(containingType);
- while (containingType != null)
+ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
{
+ if (containingType == null)
+ return;
+
+ AppendPartialContainingTypeDeclarations(containingType.ContainingType);
+
source.Append("partial ");
source.Append(containingType.GetDeclarationKeyword());
source.Append(" ");
source.Append(containingType.NameWithTypeParameters());
source.Append("\n{\n");
-
- containingType = containingType.ContainingType;
}
}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
index 6b000cc89b..750e11777d 100644
--- a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
@@ -128,16 +128,20 @@ using Godot.NativeInterop;
if (isInnerClass)
{
var containingType = symbol.ContainingType;
+ AppendPartialContainingTypeDeclarations(containingType);
- while (containingType != null)
+ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
{
+ if (containingType == null)
+ return;
+
+ AppendPartialContainingTypeDeclarations(containingType.ContainingType);
+
source.Append("partial ");
source.Append(containingType.GetDeclarationKeyword());
source.Append(" ");
source.Append(containingType.NameWithTypeParameters());
source.Append("\n{\n");
-
- containingType = containingType.ContainingType;
}
}
@@ -303,16 +307,20 @@ using Godot.NativeInterop;
if (isInnerClass)
{
var containingType = symbol.ContainingType;
+ AppendPartialContainingTypeDeclarations(containingType);
- while (containingType != null)
+ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
{
+ if (containingType == null)
+ return;
+
+ AppendPartialContainingTypeDeclarations(containingType.ContainingType);
+
source.Append("partial ");
source.Append(containingType.GetDeclarationKeyword());
source.Append(" ");
source.Append(containingType.NameWithTypeParameters());
source.Append("\n{\n");
-
- containingType = containingType.ContainingType;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index a7712db737..b4f7b82f60 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -239,7 +239,7 @@ namespace Godot
/// <summary>
/// Returns the basis's rotation in the form of Euler angles.
- /// The Euler order depends on the [param order] parameter,
+ /// The Euler order depends on the <paramref name="order"/> parameter,
/// by default it uses the YXZ convention: when decomposing,
/// first Z, then X, and Y last. The returned vector contains
/// the rotation angles in the format (X angle, Y angle, Z angle).
@@ -1037,10 +1037,11 @@ namespace Godot
}
/// <summary>
- /// Returns a Vector3 transformed (multiplied) by the transposed basis matrix.
- ///
- /// Note: This results in a multiplication by the inverse of the
- /// basis matrix only if it represents a rotation-reflection.
+ /// Returns a Vector3 transformed (multiplied) by the inverse basis matrix,
+ /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not).
+ /// <c>vector * basis</c> is equivalent to <c>basis.Transposed() * vector</c>. See <see cref="Transposed"/>.
+ /// For transforming by inverse of a non-orthonormal basis (e.g. with scaling) <c>basis.Inverse() * vector</c> can be used instead. See <see cref="Inverse"/>.
/// </summary>
/// <param name="vector">A Vector3 to inversely transform.</param>
/// <param name="basis">The basis matrix transformation to apply.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
index a80d202ef2..155e90ff24 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
@@ -905,7 +905,8 @@ namespace Godot
}
/// <summary>
- /// Returns a Vector4 transformed (multiplied) by the inverse projection.
+ /// Returns a Vector4 transformed (multiplied) by the transpose of the projection.
+ /// For transforming by inverse of a projection <c>projection.Inverse() * vector</c> can be used instead. See <see cref="Inverse"/>.
/// </summary>
/// <param name="proj">The projection to apply.</param>
/// <param name="vector">A Vector4 to transform.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
index 39e1b7e4b8..3d45913586 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -644,6 +644,7 @@ namespace Godot
/// <summary>
/// Returns a Vector3 rotated (multiplied) by the inverse quaternion.
+ /// <c>vector * quaternion</c> is equivalent to <c>quaternion.Inverse() * vector</c>. See <see cref="Inverse"/>.
/// </summary>
/// <param name="vector">A Vector3 to inversely rotate.</param>
/// <param name="quaternion">The quaternion to rotate by.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 0e3e54a0c2..386f587464 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -126,7 +126,7 @@ namespace Godot
/// <summary>
/// Returns the inverse of the transform, under the assumption that
- /// the transformation is composed of rotation, scaling, and translation.
+ /// the basis is invertible (must have non-zero determinant).
/// </summary>
/// <seealso cref="Inverse"/>
/// <returns>The inverse transformation matrix.</returns>
@@ -180,11 +180,12 @@ namespace Godot
}
/// <summary>
- /// Returns a vector transformed (multiplied) by the inverse basis matrix.
+ /// Returns a vector transformed (multiplied) by the inverse basis matrix,
+ /// under the assumption that the basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not).
/// This method does not account for translation (the <see cref="Origin"/> vector).
- ///
- /// Note: This results in a multiplication by the inverse of the
- /// basis matrix only if it represents a rotation-reflection.
+ /// <c>transform.BasisXformInv(vector)</c> is equivalent to <c>transform.Inverse().BasisXform(vector)</c>. See <see cref="Inverse"/>.
+ /// For non-orthonormal transforms (e.g. with scaling) <c>transform.AffineInverse().BasisXform(vector)</c> can be used instead. See <see cref="AffineInverse"/>.
/// </summary>
/// <seealso cref="BasisXform(Vector2)"/>
/// <param name="v">A vector to inversely transform.</param>
@@ -213,8 +214,9 @@ namespace Godot
/// <summary>
/// Returns the inverse of the transform, under the assumption that
- /// the transformation is composed of rotation and translation
- /// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
+ /// the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not). Use <see cref="AffineInverse"/> for
+ /// non-orthonormal transforms (e.g. with scaling).
/// </summary>
/// <returns>The inverse matrix.</returns>
public readonly Transform2D Inverse()
@@ -480,7 +482,11 @@ namespace Godot
}
/// <summary>
- /// Returns a Vector2 transformed (multiplied) by the inverse transformation matrix.
+ /// Returns a Vector2 transformed (multiplied) by the inverse transformation matrix,
+ /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not).
+ /// <c>vector * transform</c> is equivalent to <c>transform.Inverse() * vector</c>. See <see cref="Inverse"/>.
+ /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * vector</c> can be used instead. See <see cref="AffineInverse"/>.
/// </summary>
/// <param name="vector">A Vector2 to inversely transform.</param>
/// <param name="transform">The transformation to apply.</param>
@@ -507,7 +513,11 @@ namespace Godot
}
/// <summary>
- /// Returns a Rect2 transformed (multiplied) by the inverse transformation matrix.
+ /// Returns a Rect2 transformed (multiplied) by the inverse transformation matrix,
+ /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not).
+ /// <c>rect * transform</c> is equivalent to <c>transform.Inverse() * rect</c>. See <see cref="Inverse"/>.
+ /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * rect</c> can be used instead. See <see cref="AffineInverse"/>.
/// </summary>
/// <param name="rect">A Rect2 to inversely transform.</param>
/// <param name="transform">The transformation to apply.</param>
@@ -541,7 +551,11 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the given Vector2[] transformed (multiplied) by the inverse transformation matrix.
+ /// Returns a copy of the given Vector2[] transformed (multiplied) by the inverse transformation matrix,
+ /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not).
+ /// <c>array * transform</c> is equivalent to <c>transform.Inverse() * array</c>. See <see cref="Inverse"/>.
+ /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * array</c> can be used instead. See <see cref="AffineInverse"/>.
/// </summary>
/// <param name="array">A Vector2[] to inversely transform.</param>
/// <param name="transform">The transformation to apply.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 7b27071df1..2d09259dcb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -105,7 +105,7 @@ namespace Godot
/// <summary>
/// Returns the inverse of the transform, under the assumption that
- /// the transformation is composed of rotation, scaling, and translation.
+ /// the basis is invertible (must have non-zero determinant).
/// </summary>
/// <seealso cref="Inverse"/>
/// <returns>The inverse transformation matrix.</returns>
@@ -144,8 +144,9 @@ namespace Godot
/// <summary>
/// Returns the inverse of the transform, under the assumption that
- /// the transformation is composed of rotation and translation
- /// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
+ /// the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not). Use <see cref="AffineInverse"/> for
+ /// non-orthonormal transforms (e.g. with scaling).
/// </summary>
/// <returns>The inverse matrix.</returns>
public readonly Transform3D Inverse()
@@ -426,10 +427,11 @@ namespace Godot
}
/// <summary>
- /// Returns a Vector3 transformed (multiplied) by the transposed transformation matrix.
- ///
- /// Note: This results in a multiplication by the inverse of the
- /// transformation matrix only if it represents a rotation-reflection.
+ /// Returns a Vector3 transformed (multiplied) by the inverse transformation matrix,
+ /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not).
+ /// <c>vector * transform</c> is equivalent to <c>transform.Inverse() * vector</c>. See <see cref="Inverse"/>.
+ /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * vector</c> can be used instead. See <see cref="AffineInverse"/>.
/// </summary>
/// <param name="vector">A Vector3 to inversely transform.</param>
/// <param name="transform">The transformation to apply.</param>
@@ -482,7 +484,11 @@ namespace Godot
}
/// <summary>
- /// Returns an AABB transformed (multiplied) by the inverse transformation matrix.
+ /// Returns an AABB transformed (multiplied) by the inverse transformation matrix,
+ /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not).
+ /// <c>aabb * transform</c> is equivalent to <c>transform.Inverse() * aabb</c>. See <see cref="Inverse"/>.
+ /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * aabb</c> can be used instead. See <see cref="AffineInverse"/>.
/// </summary>
/// <param name="aabb">An AABB to inversely transform.</param>
/// <param name="transform">The transformation to apply.</param>
@@ -523,6 +529,7 @@ namespace Godot
/// <summary>
/// Returns a Plane transformed (multiplied) by the inverse transformation matrix.
+ /// <c>plane * transform</c> is equivalent to <c>transform.AffineInverse() * plane</c>. See <see cref="AffineInverse"/>.
/// </summary>
/// <param name="plane">A Plane to inversely transform.</param>
/// <param name="transform">The transformation to apply.</param>
@@ -568,7 +575,11 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the given Vector3[] transformed (multiplied) by the inverse transformation matrix.
+ /// Returns a copy of the given Vector3[] transformed (multiplied) by the inverse transformation matrix,
+ /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
+ /// is fine, scaling/skew is not).
+ /// <c>array * transform</c> is equivalent to <c>transform.Inverse() * array</c>. See <see cref="Inverse"/>.
+ /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * array</c> can be used instead. See <see cref="AffineInverse"/>.
/// </summary>
/// <param name="array">A Vector3[] to inversely transform.</param>
/// <param name="transform">The transformation to apply.</param>
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index 3675aae518..09697c7be0 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -125,11 +125,11 @@ void NavRegion::update_polygons() {
#ifdef DEBUG_ENABLED
if (!Math::is_equal_approx(double(map->get_cell_size()), double(mesh->get_cell_size()))) {
- ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_size()), double(mesh->get_cell_size())));
+ ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(mesh->get_cell_size()), double(map->get_cell_size())));
}
if (!Math::is_equal_approx(double(map->get_cell_height()), double(mesh->get_cell_height()))) {
- ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_height()), double(mesh->get_cell_height())));
+ ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings.", double(mesh->get_cell_height()), double(map->get_cell_height())));
}
if (map && Math::rad_to_deg(map->get_up().angle_to(transform.basis.get_column(1))) >= 90.0f) {
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index 73a3723ea4..2ccfe46062 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -71,6 +71,7 @@ if env["builtin_openxr"]:
# Build the engine using object files
khrloader_obj = []
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table_core.c")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp")