diff options
Diffstat (limited to 'modules')
97 files changed, 1248 insertions, 976 deletions
diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub deleted file mode 100644 index 967a511e1e..0000000000 --- a/modules/denoise/SCsub +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python - -import resource_to_cpp - -Import("env") -Import("env_modules") - -env_oidn = env_modules.Clone() - -# Thirdparty source files - -thirdparty_obj = [] - -thirdparty_dir = "#thirdparty/oidn/" -thirdparty_sources = [ - "core/api.cpp", - "core/device.cpp", - "core/filter.cpp", - "core/network.cpp", - "core/autoencoder.cpp", - "core/transfer_function.cpp", - "weights/rtlightmap_hdr.gen.cpp", - "mkl-dnn/src/common/batch_normalization.cpp", - "mkl-dnn/src/common/concat.cpp", - "mkl-dnn/src/common/convolution.cpp", - "mkl-dnn/src/common/convolution_pd.cpp", - "mkl-dnn/src/common/deconvolution.cpp", - "mkl-dnn/src/common/eltwise.cpp", - "mkl-dnn/src/common/engine.cpp", - "mkl-dnn/src/common/inner_product.cpp", - "mkl-dnn/src/common/inner_product_pd.cpp", - "mkl-dnn/src/common/lrn.cpp", - "mkl-dnn/src/common/memory.cpp", - "mkl-dnn/src/common/memory_desc_wrapper.cpp", - "mkl-dnn/src/common/mkldnn_debug.cpp", - "mkl-dnn/src/common/mkldnn_debug_autogenerated.cpp", - "mkl-dnn/src/common/pooling.cpp", - "mkl-dnn/src/common/primitive.cpp", - "mkl-dnn/src/common/primitive_attr.cpp", - "mkl-dnn/src/common/primitive_desc.cpp", - "mkl-dnn/src/common/primitive_exec_types.cpp", - "mkl-dnn/src/common/primitive_iterator.cpp", - "mkl-dnn/src/common/query.cpp", - "mkl-dnn/src/common/reorder.cpp", - "mkl-dnn/src/common/rnn.cpp", - "mkl-dnn/src/common/scratchpad.cpp", - "mkl-dnn/src/common/shuffle.cpp", - "mkl-dnn/src/common/softmax.cpp", - "mkl-dnn/src/common/stream.cpp", - "mkl-dnn/src/common/sum.cpp", - "mkl-dnn/src/common/utils.cpp", - "mkl-dnn/src/common/verbose.cpp", - "mkl-dnn/src/cpu/cpu_barrier.cpp", - "mkl-dnn/src/cpu/cpu_concat.cpp", - "mkl-dnn/src/cpu/cpu_engine.cpp", - "mkl-dnn/src/cpu/cpu_memory.cpp", - "mkl-dnn/src/cpu/cpu_reducer.cpp", - "mkl-dnn/src/cpu/cpu_reorder.cpp", - "mkl-dnn/src/cpu/cpu_sum.cpp", - "mkl-dnn/src/cpu/jit_avx2_conv_kernel_f32.cpp", - "mkl-dnn/src/cpu/jit_avx2_convolution.cpp", - "mkl-dnn/src/cpu/jit_avx512_common_conv_kernel.cpp", - "mkl-dnn/src/cpu/jit_avx512_common_conv_winograd_kernel_f32.cpp", - "mkl-dnn/src/cpu/jit_avx512_common_convolution.cpp", - "mkl-dnn/src/cpu/jit_avx512_common_convolution_winograd.cpp", - "mkl-dnn/src/cpu/jit_avx512_core_fp32_wino_conv_2x3.cpp", - "mkl-dnn/src/cpu/jit_avx512_core_fp32_wino_conv_4x3.cpp", - "mkl-dnn/src/cpu/jit_avx512_core_fp32_wino_conv_4x3_kernel.cpp", - "mkl-dnn/src/cpu/jit_sse42_conv_kernel_f32.cpp", - "mkl-dnn/src/cpu/jit_sse42_convolution.cpp", - "mkl-dnn/src/cpu/jit_transpose_src_utils.cpp", - "mkl-dnn/src/cpu/jit_uni_eltwise.cpp", - "mkl-dnn/src/cpu/jit_uni_pool_kernel_f32.cpp", - "mkl-dnn/src/cpu/jit_uni_pooling.cpp", - "mkl-dnn/src/cpu/jit_uni_reorder.cpp", - "mkl-dnn/src/cpu/jit_uni_reorder_utils.cpp", - "mkl-dnn/src/cpu/jit_utils/jit_utils.cpp", - "mkl-dnn/src/cpu/jit_utils/jitprofiling/jitprofiling.c", - "common/platform.cpp", - "common/thread.cpp", - "common/tensor.cpp", -] -thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - -thirdparty_include_dirs = [ - "", - "include", - "mkl-dnn/include", - "mkl-dnn/src", - "mkl-dnn/src/common", - "mkl-dnn/src/cpu/xbyak", - "mkl-dnn/src/cpu", -] -thirdparty_include_dirs = [thirdparty_dir + file for file in thirdparty_include_dirs] - - -env_oidn.Prepend(CPPPATH=thirdparty_include_dirs) -env_oidn.Append( - CPPDEFINES=[ - "MKLDNN_THR=MKLDNN_THR_SEQ", - "OIDN_STATIC_LIB", - "__STDC_CONSTANT_MACROS", - "__STDC_LIMIT_MACROS", - "DISABLE_VERBOSE", - "MKLDNN_ENABLE_CONCURRENT_EXEC", - ] -) -env_oidn.AppendUnique(CPPDEFINES=["NDEBUG"]) # No assert() even in debug builds. - -env_thirdparty = env_oidn.Clone() -env_thirdparty.disable_warnings() - -if env["disable_exceptions"]: - # OIDN hard-requires exceptions, so we re-enable them here. - if env.msvc and ("_HAS_EXCEPTIONS", 0) in env_thirdparty["CPPDEFINES"]: - env_thirdparty["CPPDEFINES"].remove(("_HAS_EXCEPTIONS", 0)) - env_thirdparty.AppendUnique(CCFLAGS=["/EHsc"]) - elif not env.msvc and "-fno-exceptions" in env_thirdparty["CCFLAGS"]: - env_thirdparty["CCFLAGS"].remove("-fno-exceptions") - -env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) -env.modules_sources += thirdparty_obj - -weights_in_path = thirdparty_dir + "weights/rtlightmap_hdr.tza" -weights_out_path = thirdparty_dir + "weights/rtlightmap_hdr.gen.cpp" - -env_thirdparty.Depends(weights_out_path, weights_in_path) -env_thirdparty.CommandNoCache(weights_out_path, weights_in_path, resource_to_cpp.tza_to_cpp) - -# Godot source files - -module_obj = [] - -env_oidn.add_source_files(module_obj, "*.cpp") -env.modules_sources += module_obj - -# Needed to force rebuilding the module files when the thirdparty library is updated. -env.Depends(module_obj, thirdparty_obj) diff --git a/modules/denoise/config.py b/modules/denoise/config.py deleted file mode 100644 index 27d2ffbf86..0000000000 --- a/modules/denoise/config.py +++ /dev/null @@ -1,12 +0,0 @@ -def can_build(env, platform): - # Thirdparty dependency OpenImage Denoise includes oneDNN library - # and the version we use only supports x86_64. - # It's also only relevant for tools build and desktop platforms, - # as doing lightmap generation and denoising on Android or Web - # would be a bit far-fetched. - desktop_platforms = ["linuxbsd", "macos", "windows"] - return env.editor_build and platform in desktop_platforms and env["arch"] == "x86_64" - - -def configure(env): - pass diff --git a/modules/denoise/denoise_wrapper.cpp b/modules/denoise/denoise_wrapper.cpp deleted file mode 100644 index 87f02cb4c6..0000000000 --- a/modules/denoise/denoise_wrapper.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************/ -/* denoise_wrapper.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "denoise_wrapper.h" - -#include <OpenImageDenoise/oidn.h> - -#include <stdio.h> - -void *oidn_denoiser_init() { - OIDNDeviceImpl *device = oidnNewDevice(OIDN_DEVICE_TYPE_CPU); - oidnCommitDevice(device); - return device; -} - -bool oidn_denoise(void *deviceptr, float *p_floats, int p_width, int p_height) { - OIDNDeviceImpl *device = (OIDNDeviceImpl *)deviceptr; - OIDNFilter filter = oidnNewFilter(device, "RTLightmap"); - oidnSetSharedFilterImage(filter, "color", (void *)p_floats, OIDN_FORMAT_FLOAT3, p_width, p_height, 0, 0, 0); - oidnSetSharedFilterImage(filter, "output", (void *)p_floats, OIDN_FORMAT_FLOAT3, p_width, p_height, 0, 0, 0); - oidnSetFilter1b(filter, "hdr", true); - //oidnSetFilter1f(filter, "hdrScale", 1.0f); - oidnCommitFilter(filter); - oidnExecuteFilter(filter); - - const char *msg; - bool success = true; - if (oidnGetDeviceError(device, &msg) != OIDN_ERROR_NONE) { - printf("LightmapDenoiser: %s\n", msg); - success = false; - } - - oidnReleaseFilter(filter); - return success; -} - -void oidn_denoiser_finish(void *device) { - oidnReleaseDevice((OIDNDeviceImpl *)device); -} diff --git a/modules/denoise/denoise_wrapper.h b/modules/denoise/denoise_wrapper.h deleted file mode 100644 index d4bf154a5d..0000000000 --- a/modules/denoise/denoise_wrapper.h +++ /dev/null @@ -1,38 +0,0 @@ -/**************************************************************************/ -/* denoise_wrapper.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef DENOISE_WRAPPER_H -#define DENOISE_WRAPPER_H - -void *oidn_denoiser_init(); -bool oidn_denoise(void *device, float *p_floats, int p_width, int p_height); -void oidn_denoiser_finish(void *device); - -#endif // DENOISE_WRAPPER_H diff --git a/modules/denoise/lightmap_denoiser.cpp b/modules/denoise/lightmap_denoiser.cpp deleted file mode 100644 index 72764036e1..0000000000 --- a/modules/denoise/lightmap_denoiser.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************/ -/* lightmap_denoiser.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "lightmap_denoiser.h" - -#include "denoise_wrapper.h" - -#include "core/io/image.h" - -LightmapDenoiser *LightmapDenoiserOIDN::create_oidn_denoiser() { - return memnew(LightmapDenoiserOIDN); -} - -void LightmapDenoiserOIDN::make_default_denoiser() { - create_function = create_oidn_denoiser; -} - -Ref<Image> LightmapDenoiserOIDN::denoise_image(const Ref<Image> &p_image) { - Ref<Image> img = p_image->duplicate(); - - img->convert(Image::FORMAT_RGBF); - - Vector<uint8_t> data = img->get_data(); - if (!oidn_denoise(device, (float *)data.ptrw(), img->get_width(), img->get_height())) { - return p_image; - } - - img->set_data(img->get_width(), img->get_height(), false, img->get_format(), data); - return img; -} - -LightmapDenoiserOIDN::LightmapDenoiserOIDN() { - device = oidn_denoiser_init(); -} - -LightmapDenoiserOIDN::~LightmapDenoiserOIDN() { - oidn_denoiser_finish(device); -} diff --git a/modules/denoise/lightmap_denoiser.h b/modules/denoise/lightmap_denoiser.h deleted file mode 100644 index 8f658ab096..0000000000 --- a/modules/denoise/lightmap_denoiser.h +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************/ -/* lightmap_denoiser.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef LIGHTMAP_DENOISER_H -#define LIGHTMAP_DENOISER_H - -#include "core/object/class_db.h" -#include "scene/3d/lightmapper.h" - -struct OIDNDeviceImpl; - -class LightmapDenoiserOIDN : public LightmapDenoiser { - GDCLASS(LightmapDenoiserOIDN, LightmapDenoiser); - -protected: - void *device = nullptr; - -public: - static LightmapDenoiser *create_oidn_denoiser(); - - Ref<Image> denoise_image(const Ref<Image> &p_image) override; - - static void make_default_denoiser(); - - LightmapDenoiserOIDN(); - ~LightmapDenoiserOIDN(); -}; - -#endif // LIGHTMAP_DENOISER_H diff --git a/modules/denoise/register_types.cpp b/modules/denoise/register_types.cpp deleted file mode 100644 index a4264b07c5..0000000000 --- a/modules/denoise/register_types.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************/ -/* register_types.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "register_types.h" - -#include "lightmap_denoiser.h" - -#include "core/config/engine.h" - -void initialize_denoise_module(ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { - return; - } - - LightmapDenoiserOIDN::make_default_denoiser(); -} - -void uninitialize_denoise_module(ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { - return; - } -} diff --git a/modules/denoise/register_types.h b/modules/denoise/register_types.h deleted file mode 100644 index 239877a5c7..0000000000 --- a/modules/denoise/register_types.h +++ /dev/null @@ -1,39 +0,0 @@ -/**************************************************************************/ -/* register_types.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef DENOISE_REGISTER_TYPES_H -#define DENOISE_REGISTER_TYPES_H - -#include "modules/register_module_types.h" - -void initialize_denoise_module(ModuleInitializationLevel p_level); -void uninitialize_denoise_module(ModuleInitializationLevel p_level); - -#endif // DENOISE_REGISTER_TYPES_H diff --git a/modules/denoise/resource_to_cpp.py b/modules/denoise/resource_to_cpp.py deleted file mode 100644 index a89eda9117..0000000000 --- a/modules/denoise/resource_to_cpp.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python - -## ======================================================================== ## -## Copyright 2009-2019 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## - -import os -from array import array - - -# Generates a C++ file from the specified binary resource file -def generate(in_path, out_path): - namespace = "oidn::weights" - scopes = namespace.split("::") - - file_name = os.path.basename(in_path) - var_name = os.path.splitext(file_name)[0] - - with open(in_path, "rb") as in_file, open(out_path, "w") as out_file: - # Header - out_file.write("// Generated from: %s\n" % file_name) - out_file.write("#include <cstddef>\n\n") - - # Open the namespaces - for s in scopes: - out_file.write("namespace %s {\n" % s) - if scopes: - out_file.write("\n") - - # Read the file - in_data = array("B", in_file.read()) - - # Write the size - out_file.write("//const size_t %s_size = %d;\n\n" % (var_name, len(in_data))) - - # Write the data - out_file.write("unsigned char %s[] = {" % var_name) - for i in range(len(in_data)): - c = in_data[i] - if i > 0: - out_file.write(",") - if (i + 1) % 20 == 1: - out_file.write("\n") - out_file.write("%d" % c) - out_file.write("\n};\n") - - # Close the namespaces - if scopes: - out_file.write("\n") - for scope in reversed(scopes): - out_file.write("} // namespace %s\n" % scope) - - -def tza_to_cpp(target, source, env): - for x in zip(source, target): - generate(str(x[0]), str(x[1])) diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 22be26fdc1..b5c80d9e2d 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -306,6 +306,9 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl while (sptr) { Vector<_GDScriptMemberSort> msort; for (const KeyValue<StringName, MemberInfo> &E : sptr->member_indices) { + if (!sptr->members.has(E.key)) { + continue; // Skip base class members. + } _GDScriptMemberSort ms; ms.index = E.value.index; ms.name = E.key; @@ -1648,15 +1651,11 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { } Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { - const GDScript *sptr = script.ptr(); - while (sptr) { - if (sptr->member_indices.has(p_name)) { - if (r_is_valid) { - *r_is_valid = true; - } - return sptr->member_indices[p_name].property_info.type; + if (script->member_indices.has(p_name)) { + if (r_is_valid) { + *r_is_valid = true; } - sptr = sptr->_base; + return script->member_indices[p_name].property_info.type; } if (r_is_valid) { @@ -1732,6 +1731,9 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const Vector<_GDScriptMemberSort> msort; for (const KeyValue<StringName, GDScript::MemberInfo> &F : sptr->member_indices) { + if (!sptr->members.has(F.key)) { + continue; // Skip base class members. + } _GDScriptMemberSort ms; ms.index = F.value.index; ms.name = F.key; @@ -2415,6 +2417,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "return", "match", "while", + "when", // These keywords are not implemented currently, but reserved for (potential) future use. // We highlight them as keywords to make errors easier to understand. "trait", @@ -2448,6 +2451,7 @@ bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const { p_keyword == "match" || p_keyword == "pass" || p_keyword == "return" || + p_keyword == "when" || p_keyword == "while"; } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 2b698d75d4..50ccfabcc1 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -94,12 +94,16 @@ class GDScript : public Script { GDScript *_base = nullptr; //fast pointer access GDScript *_owner = nullptr; //for subclasses - HashSet<StringName> members; //members are just indices to the instantiated script. - HashMap<StringName, Variant> constants; + // Members are just indices to the instantiated script. + HashMap<StringName, MemberInfo> member_indices; // Includes member info of all base GDScript classes. + HashSet<StringName> members; // Only members of the current class. + + // Only static variables of the current class. HashMap<StringName, MemberInfo> static_variables_indices; - Vector<Variant> static_variables; + Vector<Variant> static_variables; // Static variable values. + + HashMap<StringName, Variant> constants; HashMap<StringName, GDScriptFunction *> member_functions; - HashMap<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script. HashMap<StringName, Ref<GDScript>> subclasses; HashMap<StringName, MethodInfo> _signals; Dictionary rpc_config; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 55bb99133a..882c246706 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -248,7 +248,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me return ERR_PARSE_ERROR; } - if (GDScriptParser::get_builtin_type(p_member_name) != Variant::VARIANT_MAX) { + if (GDScriptParser::get_builtin_type(p_member_name) < Variant::VARIANT_MAX) { push_error(vformat(R"(The member "%s" cannot have the same name as a builtin type.)", p_member_name), p_member_node); return ERR_PARSE_ERROR; } @@ -673,11 +673,6 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type return bad_type; } result.kind = GDScriptParser::DataType::VARIANT; - } else if (first == SNAME("Object")) { - // Object is treated like a native type, not a built-in. - result.kind = GDScriptParser::DataType::NATIVE; - result.builtin_type = Variant::OBJECT; - result.native_type = SNAME("Object"); } else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) { // Built-in types. if (p_type->type_chain.size() > 1) { @@ -1676,15 +1671,42 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * StringName native_base; if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, method_flags, &native_base)) { bool valid = p_function->is_static == method_flags.has_flag(METHOD_FLAG_STATIC); - valid = valid && parent_return_type == p_function->get_datatype(); + + if (p_function->return_type != nullptr) { + // Check return type covariance. + GDScriptParser::DataType return_type = p_function->get_datatype(); + if (return_type.is_variant()) { + // `is_type_compatible()` returns `true` if one of the types is `Variant`. + // Don't allow an explicitly specified `Variant` if the parent return type is narrower. + valid = valid && parent_return_type.is_variant(); + } else if (return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) { + // `is_type_compatible()` returns `true` if target is an `Object` and source is `null`. + // Don't allow `void` if the parent return type is a hard non-`void` type. + if (parent_return_type.is_hard_type() && !(parent_return_type.kind == GDScriptParser::DataType::BUILTIN && parent_return_type.builtin_type == Variant::NIL)) { + valid = false; + } + } else { + valid = valid && is_type_compatible(parent_return_type, return_type); + } + } int par_count_diff = p_function->parameters.size() - parameters_types.size(); valid = valid && par_count_diff >= 0; valid = valid && default_value_count >= default_par_count + par_count_diff; - int i = 0; - for (const GDScriptParser::DataType &par_type : parameters_types) { - valid = valid && par_type == p_function->parameters[i++]->get_datatype(); + if (valid) { + int i = 0; + for (const GDScriptParser::DataType &parent_par_type : parameters_types) { + // Check parameter type contravariance. + GDScriptParser::DataType current_par_type = p_function->parameters[i++]->get_datatype(); + if (parent_par_type.is_variant() && parent_par_type.is_hard_type()) { + // `is_type_compatible()` returns `true` if one of the types is `Variant`. + // Don't allow narrowing a hard `Variant`. + valid = valid && current_par_type.is_variant(); + } else { + valid = valid && is_type_compatible(current_par_type, parent_par_type); + } + } } if (!valid) { @@ -1708,7 +1730,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * } parent_signature += ") -> "; - const String return_type = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant"; + const String return_type = parent_return_type.to_string_strict(); if (return_type == "null") { parent_signature += "void"; } else { @@ -2152,6 +2174,9 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { } else if (!is_type_compatible(specified_type, variable_type)) { p_for->use_conversion_assign = true; } + if (p_for->list && p_for->list->type == GDScriptParser::Node::ARRAY) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_for->list), specified_type); + } } p_for->variable->set_datatype(specified_type); } else { @@ -2219,6 +2244,10 @@ void GDScriptAnalyzer::resolve_match_branch(GDScriptParser::MatchBranchNode *p_m resolve_match_pattern(p_match_branch->patterns[i], p_match_test); } + if (p_match_branch->guard_body) { + resolve_suite(p_match_branch->guard_body); + } + resolve_suite(p_match_branch->block); decide_suite_type(p_match_branch, p_match_branch->block); @@ -2552,28 +2581,31 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr // When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed. // This function determines which type is that (if any). void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) { + GDScriptParser::DataType expected_type = p_element_type; + expected_type.unset_container_element_type(); // Nested types (like `Array[Array[int]]`) are not currently supported. + for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element_node = p_array->elements[i]; if (element_node->is_constant) { - update_const_expression_builtin_type(element_node, p_element_type, "include"); + update_const_expression_builtin_type(element_node, expected_type, "include"); } - const GDScriptParser::DataType &element_type = element_node->get_datatype(); - if (element_type.has_no_type() || element_type.is_variant() || !element_type.is_hard_type()) { + const GDScriptParser::DataType &actual_type = element_node->get_datatype(); + if (actual_type.has_no_type() || actual_type.is_variant() || !actual_type.is_hard_type()) { mark_node_unsafe(element_node); continue; } - if (!is_type_compatible(p_element_type, element_type, true, p_array)) { - if (is_type_compatible(element_type, p_element_type)) { + if (!is_type_compatible(expected_type, actual_type, true, p_array)) { + if (is_type_compatible(actual_type, expected_type)) { mark_node_unsafe(element_node); continue; } - push_error(vformat(R"(Cannot have an element of type "%s" in an array of type "Array[%s]".)", element_type.to_string(), p_element_type.to_string()), element_node); + push_error(vformat(R"(Cannot have an element of type "%s" in an array of type "Array[%s]".)", actual_type.to_string(), expected_type.to_string()), element_node); return; } } GDScriptParser::DataType array_type = p_array->get_datatype(); - array_type.set_container_element_type(p_element_type); + array_type.set_container_element_type(expected_type); p_array->set_datatype(array_type); } @@ -2899,19 +2931,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (!p_call->is_super && callee_type == GDScriptParser::Node::IDENTIFIER) { // Call to name directly. StringName function_name = p_call->function_name; - Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name); + if (function_name == SNAME("Object")) { + push_error(R"*(Invalid constructor "Object()", use "Object.new()" instead.)*", p_call); + p_call->set_datatype(call_type); + return; + } + + Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name); if (builtin_type < Variant::VARIANT_MAX) { // Is a builtin constructor. call_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; call_type.kind = GDScriptParser::DataType::BUILTIN; call_type.builtin_type = builtin_type; - if (builtin_type == Variant::OBJECT) { - call_type.kind = GDScriptParser::DataType::NATIVE; - call_type.native_type = function_name; // "Object". - } - bool safe_to_fold = true; switch (builtin_type) { // Those are stored by reference so not suited for compile-time construction. @@ -2947,7 +2980,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a switch (err.error) { case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: - push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be "%s" but is "%s".)", Variant::get_type_name(builtin_type), err.argument + 1, + push_error(vformat(R"*(Invalid argument for "%s()" constructor: argument %d should be "%s" but is "%s".)*", Variant::get_type_name(builtin_type), err.argument + 1, Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()), p_call->arguments[err.argument]); break; @@ -2963,10 +2996,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call->callee); } break; case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: - push_error(vformat(R"(Too many arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call); + push_error(vformat(R"*(Too many arguments for "%s()" constructor. Received %d but expected %d.)*", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call); break; case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: - push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call); + push_error(vformat(R"*(Too few arguments for "%s()" constructor. Received %d but expected %d.)*", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call); break; case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST: @@ -2977,21 +3010,27 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a break; } } else { - // TODO: Check constructors without constants. - // If there's one argument, try to use copy constructor (those aren't explicitly defined). if (p_call->arguments.size() == 1) { GDScriptParser::DataType arg_type = p_call->arguments[0]->get_datatype(); - if (arg_type.is_variant()) { - mark_node_unsafe(p_call->arguments[0]); - } else { + if (arg_type.is_hard_type() && !arg_type.is_variant()) { if (arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == builtin_type) { // Okay. p_call->set_datatype(call_type); return; } + } else { +#ifdef DEBUG_ENABLED + mark_node_unsafe(p_call); + // We don't know what type was expected since constructors support overloads. + // TODO: Improve this by checking for matching candidates? + parser->push_warning(p_call->arguments[0], GDScriptWarning::UNSAFE_CALL_ARGUMENT, "1", function_name, "<unknown type>", "Variant"); +#endif + p_call->set_datatype(call_type); + return; } } + List<MethodInfo> constructors; Variant::get_constructor_list(builtin_type, &constructors); bool match = false; @@ -3008,14 +3047,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a for (int i = 0; i < p_call->arguments.size(); i++) { GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true); - - if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) { + GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype(); + if (!is_type_compatible(par_type, arg_type, true)) { types_match = false; break; #ifdef DEBUG_ENABLED } else { - if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT && builtin_type != Variant::INT) { - parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name); + if (par_type.builtin_type == Variant::INT && arg_type.builtin_type == Variant::FLOAT && builtin_type != Variant::INT) { + parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, function_name); } #endif } @@ -3023,9 +3062,19 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (types_match) { for (int i = 0; i < p_call->arguments.size(); i++) { + GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true); if (p_call->arguments[i]->is_constant) { - update_const_expression_builtin_type(p_call->arguments[i], type_from_property(info.arguments[i], true), "pass"); + update_const_expression_builtin_type(p_call->arguments[i], par_type, "pass"); } +#ifdef DEBUG_ENABLED + if (!(par_type.is_variant() && par_type.is_hard_type())) { + GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype(); + if (arg_type.is_variant() || !arg_type.is_hard_type() || !is_type_compatible(arg_type, par_type, true)) { + mark_node_unsafe(p_call); + parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), function_name, par_type.to_string(), arg_type.to_string_strict()); + } + } +#endif } match = true; call_type = type_from_property(info.return_val); @@ -3331,8 +3380,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a #else push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); #endif // SUGGEST_GODOT4_RENAMES - } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) { - push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call); + } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.is_meta_type)) { + push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.to_string()), p_call); } } @@ -3820,6 +3869,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident #endif // Not a local, so check members. + if (!found_source) { reduce_identifier_from_base(p_identifier); if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) { @@ -3872,10 +3922,10 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident StringName name = p_identifier->name; p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE; - // Check globals. We make an exception for Variant::OBJECT because it's the base class for - // non-builtin types so we allow doing e.g. Object.new() + // Not a local or a member, so check globals. + Variant::Type builtin_type = GDScriptParser::get_builtin_type(name); - if (builtin_type != Variant::OBJECT && builtin_type < Variant::VARIANT_MAX) { + if (builtin_type < Variant::VARIANT_MAX) { if (can_be_builtin) { p_identifier->set_datatype(make_builtin_meta_type(builtin_type)); return; @@ -5003,21 +5053,28 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype(); if (arg_type.is_variant() || !arg_type.is_hard_type()) { +#ifdef DEBUG_ENABLED // Argument can be anything, so this is unsafe (unless the parameter is a hard variant). if (!(par_type.is_hard_type() && par_type.is_variant())) { mark_node_unsafe(p_call->arguments[i]); + parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), p_call->function_name, par_type.to_string(), arg_type.to_string_strict()); } +#endif } else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) { - // Supertypes are acceptable for dynamic compliance, but it's unsafe. - mark_node_unsafe(p_call); if (!is_type_compatible(arg_type, par_type)) { push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()), p_call->arguments[i]); +#ifdef DEBUG_ENABLED + } else { + // Supertypes are acceptable for dynamic compliance, but it's unsafe. + mark_node_unsafe(p_call); + parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), p_call->function_name, par_type.to_string(), arg_type.to_string_strict()); +#endif } #ifdef DEBUG_ENABLED } else if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) { - parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name); + parser->push_warning(p_call->arguments[i], GDScriptWarning::NARROWING_CONVERSION, p_call->function_name); #endif } } @@ -5049,7 +5106,7 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier String class_path = ScriptServer::get_global_class_path(name).get_file(); parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, vformat(R"(global class defined in "%s")", class_path)); return; - } else if (GDScriptParser::get_builtin_type(name) != Variant::VARIANT_MAX) { + } else if (GDScriptParser::get_builtin_type(name) < Variant::VARIANT_MAX) { parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in type"); return; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 97e02ac716..f417d323db 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -612,11 +612,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code arguments.push_back(arg); } - if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) { - // Construct a built-in type. - Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name); - - gen->write_construct(result, vtype, arguments); + if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) { + gen->write_construct(result, GDScriptParser::get_builtin_type(call->function_name), arguments); } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) { // Variant utility function. gen->write_call_utility(result, call->function_name, arguments); @@ -1928,6 +1925,26 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } } + // If there's a guard, check its condition too. + if (branch->guard_body != nullptr) { + // Do this first so the guard does not run unless the pattern matched. + gen->write_and_left_operand(pattern_result); + + // Don't actually use the block for the guard. + // The binds are already in the locals and we don't want to clear the result of the guard condition before we check the actual match. + GDScriptCodeGenerator::Address guard_result = _parse_expression(codegen, err, static_cast<GDScriptParser::ExpressionNode *>(branch->guard_body->statements[0])); + if (err) { + return err; + } + + gen->write_and_right_operand(guard_result); + gen->write_end_and(pattern_result); + + if (guard_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } + } + // Check if pattern did match. gen->write_if(pattern_result); @@ -2794,7 +2811,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP minfo.property_info = prop_info; p_script->member_indices[name] = minfo; - p_script->members.insert(Variant()); + p_script->members.insert(name); } break; case GDScriptParser::ClassNode::Member::FUNCTION: { diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 3e13d1525d..372c212d2b 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -140,7 +140,7 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar if (p_argcount == 0) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + r_error.expected = 1; return Variant(); } else if (p_argcount == 1) { //noooneee diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 0801582dbd..9fb1030d12 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -52,11 +52,18 @@ #include "editor/editor_settings.h" #endif +// This function is used to determine that a type is "built-in" as opposed to native +// and custom classes. So `Variant::NIL` and `Variant::OBJECT` are excluded: +// `Variant::NIL` - `null` is literal, not a type. +// `Variant::OBJECT` - `Object` should be treated as a class, not as a built-in type. static HashMap<StringName, Variant::Type> builtin_types; Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) { - if (builtin_types.is_empty()) { - for (int i = 1; i < Variant::VARIANT_MAX; i++) { - builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i; + if (unlikely(builtin_types.is_empty())) { + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + Variant::Type type = (Variant::Type)i; + if (type != Variant::NIL && type != Variant::OBJECT) { + builtin_types[Variant::get_type_name(type)] = type; + } } } @@ -2035,7 +2042,37 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { push_error(R"(No pattern found for "match" branch.)"); } - if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) { + bool has_guard = false; + if (match(GDScriptTokenizer::Token::WHEN)) { + // Pattern guard. + // Create block for guard because it also needs to access the bound variables from patterns, and we don't want to add them to the outer scope. + branch->guard_body = alloc_node<SuiteNode>(); + if (branch->patterns.size() > 0) { + for (const KeyValue<StringName, IdentifierNode *> &E : branch->patterns[0]->binds) { + SuiteNode::Local local(E.value, current_function); + local.type = SuiteNode::Local::PATTERN_BIND; + branch->guard_body->add_local(local); + } + } + + SuiteNode *parent_block = current_suite; + branch->guard_body->parent_block = parent_block; + current_suite = branch->guard_body; + + ExpressionNode *guard = parse_expression(false); + if (guard == nullptr) { + push_error(R"(Expected expression for pattern guard after "when".)"); + } else { + branch->guard_body->statements.append(guard); + } + current_suite = parent_block; + complete_extents(branch->guard_body); + + has_guard = true; + branch->has_wildcard = false; // If it has a guard, the wildcard might still not match. + } + + if (!consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":"%s after "match" %s.)", has_guard ? "" : R"( or "when")", has_guard ? "pattern guard" : "patterns"))) { complete_extents(branch); return nullptr; } @@ -3674,6 +3711,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty { nullptr, nullptr, PREC_NONE }, // PASS, { nullptr, nullptr, PREC_NONE }, // RETURN, { nullptr, nullptr, PREC_NONE }, // MATCH, + { nullptr, nullptr, PREC_NONE }, // WHEN, // Keywords { nullptr, &GDScriptParser::parse_cast, PREC_CAST }, // AS, { nullptr, nullptr, PREC_NONE }, // ASSERT, diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 3bd3696e99..2daadd7e6a 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -149,6 +149,7 @@ public: _FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; } String to_string() const; + _FORCE_INLINE_ String to_string_strict() const { return is_hard_type() ? to_string() : "Variant"; } PropertyInfo to_property_info(const String &p_name) const; _FORCE_INLINE_ void set_container_element_type(const DataType &p_type) { @@ -948,6 +949,7 @@ public: Vector<PatternNode *> patterns; SuiteNode *block = nullptr; bool has_wildcard = false; + SuiteNode *guard_body = nullptr; MatchBranchNode() { type = MATCH_BRANCH; @@ -1530,7 +1532,7 @@ public: bool is_tool() const { return _is_tool; } ClassNode *find_class(const String &p_qualified_name) const; bool has_class(const GDScriptParser::ClassNode *p_class) const; - static Variant::Type get_builtin_type(const StringName &p_type); + static Variant::Type get_builtin_type(const StringName &p_type); // Excluding `Variant::NIL` and `Variant::OBJECT`. CompletionContext get_completion_context() const { return completion_context; } CompletionCall get_completion_call() const { return completion_call; } diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 07f2b8b406..98a3a1268f 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -99,6 +99,7 @@ static const char *token_names[] = { "pass", // PASS, "return", // RETURN, "match", // MATCH, + "when", // WHEN, // Keywords "as", // AS, "assert", // ASSERT, @@ -187,6 +188,7 @@ bool GDScriptTokenizer::Token::is_identifier() const { switch (type) { case IDENTIFIER: case MATCH: // Used in String.match(). + case WHEN: // New keyword, avoid breaking existing code. // Allow constants to be treated as regular identifiers. case CONST_PI: case CONST_INF: @@ -241,6 +243,7 @@ bool GDScriptTokenizer::Token::is_node_name() const { case VAR: case VOID: case WHILE: + case WHEN: case YIELD: return true; default: @@ -531,6 +534,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::annotation() { KEYWORD("void", Token::VOID) \ KEYWORD_GROUP('w') \ KEYWORD("while", Token::WHILE) \ + KEYWORD("when", Token::WHEN) \ KEYWORD_GROUP('y') \ KEYWORD("yield", Token::YIELD) \ KEYWORD_GROUP('I') \ diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index f916407b18..6dd8a98652 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -105,6 +105,7 @@ public: PASS, RETURN, MATCH, + WHEN, // Keywords AS, ASSERT, diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp index d85b12b7fe..69a0b42d89 100644 --- a/modules/gdscript/gdscript_utility_functions.cpp +++ b/modules/gdscript/gdscript_utility_functions.cpp @@ -45,14 +45,12 @@ #define VALIDATE_ARG_COUNT(m_count) \ if (p_arg_count < m_count) { \ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \ - r_error.argument = m_count; \ r_error.expected = m_count; \ *r_ret = Variant(); \ return; \ } \ if (p_arg_count > m_count) { \ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \ - r_error.argument = m_count; \ r_error.expected = m_count; \ *r_ret = Variant(); \ return; \ @@ -119,7 +117,6 @@ struct GDScriptUtilityFunctionsDefinitions { switch (p_arg_count) { case 0: { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; r_error.expected = 1; *r_ret = Variant(); } break; @@ -223,7 +220,6 @@ struct GDScriptUtilityFunctionsDefinitions { } break; default: { r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = 3; r_error.expected = 3; *r_ret = Variant(); @@ -251,6 +247,7 @@ struct GDScriptUtilityFunctionsDefinitions { } else if (p_args[0]->get_type() != Variant::OBJECT) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; + r_error.expected = Variant::OBJECT; *r_ret = Variant(); } else { Object *obj = *p_args[0]; @@ -390,13 +387,13 @@ struct GDScriptUtilityFunctionsDefinitions { static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { if (p_arg_count < 3) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 3; + r_error.expected = 3; *r_ret = Variant(); return; } if (p_arg_count > 4) { r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = 4; + r_error.expected = 4; *r_ret = Variant(); return; } diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index be18dee2cf..5ecae08f6c 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -519,7 +519,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (p_argcount > _argument_count) { r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; r_err.expected = _argument_count; - call_depth--; return _get_default_variant_for_data_type(return_type); } else if (p_argcount < _argument_count - _default_arg_count) { diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index a0078f84d6..cabac07ef9 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -108,7 +108,7 @@ String GDScriptWarning::get_message() const { return vformat(R"(The value is cast to "%s" but has an unknown type.)", symbols[0]); case UNSAFE_CALL_ARGUMENT: CHECK_SYMBOLS(4); - return vformat(R"*(The argument %s of the function "%s()" requires a the subtype "%s" but the supertype "%s" was provided.)*", symbols[0], symbols[1], symbols[2], symbols[3]); + return vformat(R"*(The argument %s of the function "%s()" requires the subtype "%s" but the supertype "%s" was provided.)*", symbols[0], symbols[1], symbols[2], symbols[3]); case UNSAFE_VOID_RETURN: CHECK_SYMBOLS(2); return vformat(R"*(The method "%s()" returns "void" but it's trying to return a call to "%s()" that can't be ensured to also be "void".)*", symbols[0], symbols[1]); diff --git a/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.gd new file mode 100644 index 0000000000..87d1b9ea18 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.gd @@ -0,0 +1,7 @@ +# GH-73283 + +class MyClass: + pass + +func test(): + MyClass.not_existing_method() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.out b/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.out new file mode 100644 index 0000000000..7340058dd4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/call_not_existing_static_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Static function "not_existing_method()" not found in base "MyClass". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type_with_literal_array.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type_with_literal_array.gd new file mode 100644 index 0000000000..db3f3f4c72 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type_with_literal_array.gd @@ -0,0 +1,5 @@ +# GH-82021 + +func test(): + for x: String in [1, 2, 3]: + print(x) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type_with_literal_array.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type_with_literal_array.out new file mode 100644 index 0000000000..0bb654e7e2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type_with_literal_array.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot include a value of type "int" as "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_1.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_1.gd new file mode 100644 index 0000000000..fdf22f6843 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_1.gd @@ -0,0 +1,10 @@ +class A: + func f(_p: Object): + pass + +class B extends A: + func f(_p: Node): + pass + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_1.out b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_1.out new file mode 100644 index 0000000000..c6a7e40e8c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_1.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "f(Object) -> Variant". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_2.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_2.gd new file mode 100644 index 0000000000..e4094f1d76 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_2.gd @@ -0,0 +1,10 @@ +class A: + func f(_p: Variant): + pass + +class B extends A: + func f(_p: Node): # No `is_type_compatible()` misuse. + pass + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_2.out b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_2.out new file mode 100644 index 0000000000..52a6efc6fc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_2.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "f(Variant) -> Variant". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_3.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_3.gd new file mode 100644 index 0000000000..17663da4f6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_3.gd @@ -0,0 +1,10 @@ +class A: + func f(_p: int): + pass + +class B extends A: + func f(_p: float): # No implicit conversion. + pass + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_3.out b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_3.out new file mode 100644 index 0000000000..7a6207fd45 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_param_type_invalid_contravariance_3.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "f(int) -> Variant". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_1.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_1.gd new file mode 100644 index 0000000000..6dfa75ecbc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_1.gd @@ -0,0 +1,10 @@ +class A: + func f() -> Node: + return null + +class B extends A: + func f() -> Object: + return null + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_1.out b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_1.out new file mode 100644 index 0000000000..e680b2bd77 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_1.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "f() -> Node". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_2.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_2.gd new file mode 100644 index 0000000000..366494b94f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_2.gd @@ -0,0 +1,10 @@ +class A: + func f() -> Node: + return null + +class B extends A: + func f() -> Variant: # No `is_type_compatible()` misuse. + return null + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_2.out b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_2.out new file mode 100644 index 0000000000..e680b2bd77 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_2.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "f() -> Node". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_3.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_3.gd new file mode 100644 index 0000000000..2cb4e7c616 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_3.gd @@ -0,0 +1,10 @@ +class A: + func f() -> Node: + return null + +class B extends A: + func f() -> void: # No `is_type_compatible()` misuse. + return + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_3.out b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_3.out new file mode 100644 index 0000000000..e680b2bd77 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_3.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "f() -> Node". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_4.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_4.gd new file mode 100644 index 0000000000..2cabce46f5 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_4.gd @@ -0,0 +1,10 @@ +class A: + func f() -> float: + return 0.0 + +class B extends A: + func f() -> int: # No implicit conversion. + return 0 + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_4.out b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_4.out new file mode 100644 index 0000000000..72f2c493d4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_return_type_invalid_covariance_4.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "f() -> float". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/match_guard_invalid_expression.gd b/modules/gdscript/tests/scripts/analyzer/errors/match_guard_invalid_expression.gd new file mode 100644 index 0000000000..1dcb9fc36a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/match_guard_invalid_expression.gd @@ -0,0 +1,4 @@ +func test(): + match 0: + _ when a == 0: + print("a does not exist") diff --git a/modules/gdscript/tests/scripts/analyzer/errors/match_guard_invalid_expression.out b/modules/gdscript/tests/scripts/analyzer/errors/match_guard_invalid_expression.out new file mode 100644 index 0000000000..c5f0a90d6a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/match_guard_invalid_expression.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Identifier "a" not declared in the current scope. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.gd b/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.gd new file mode 100644 index 0000000000..1600c3001f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.gd @@ -0,0 +1,4 @@ +# GH-73213 + +func test(): + print(Object()) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.out b/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.out new file mode 100644 index 0000000000..27668fcd48 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/object_invalid_constructor.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid constructor "Object()", use "Object.new()" instead. diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_param_type_contravariance.gd b/modules/gdscript/tests/scripts/analyzer/features/function_param_type_contravariance.gd new file mode 100644 index 0000000000..a43c233625 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_param_type_contravariance.gd @@ -0,0 +1,20 @@ +class A: + func int_to_variant(_p: int): pass + func node_to_variant(_p: Node): pass + func node_2d_to_node(_p: Node2D): pass + + func variant_to_untyped(_p: Variant): pass + func int_to_untyped(_p: int): pass + func node_to_untyped(_p: Node): pass + +class B extends A: + func int_to_variant(_p: Variant): pass + func node_to_variant(_p: Variant): pass + func node_2d_to_node(_p: Node): pass + + func variant_to_untyped(_p): pass + func int_to_untyped(_p): pass + func node_to_untyped(_p): pass + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_param_type_contravariance.out b/modules/gdscript/tests/scripts/analyzer/features/function_param_type_contravariance.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_param_type_contravariance.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.gd b/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.gd new file mode 100644 index 0000000000..4de50b6731 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.gd @@ -0,0 +1,32 @@ +class A: + func variant_to_int() -> Variant: return 0 + func variant_to_node() -> Variant: return null + func node_to_node_2d() -> Node: return null + + func untyped_to_void(): pass + func untyped_to_variant(): pass + func untyped_to_int(): pass + func untyped_to_node(): pass + + func void_to_untyped() -> void: pass + func variant_to_untyped() -> Variant: return null + func int_to_untyped() -> int: return 0 + func node_to_untyped() -> Node: return null + +class B extends A: + func variant_to_int() -> int: return 0 + func variant_to_node() -> Node: return null + func node_to_node_2d() -> Node2D: return null + + func untyped_to_void() -> void: pass + func untyped_to_variant() -> Variant: return null + func untyped_to_int() -> int: return 0 + func untyped_to_node() -> Node: return null + + func void_to_untyped(): pass + func variant_to_untyped(): pass + func int_to_untyped(): pass + func node_to_untyped(): pass + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.out b/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd index b447180ea8..d0f895d784 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd @@ -23,6 +23,7 @@ func test() -> void: typed = variant() inferred = variant() + @warning_ignore("unsafe_call_argument") # TODO: Hard vs Weak vs Unknown. param_weak(typed) param_typed(typed) param_inferred(typed) diff --git a/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd index 5a413e2015..08e7dc590e 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd @@ -6,10 +6,12 @@ var prop = null func check_arg(arg = null) -> void: if arg != null: + @warning_ignore("unsafe_call_argument") print(check(arg)) func check_recur() -> void: if recur != null: + @warning_ignore("unsafe_call_argument") print(check(recur)) else: recur = 1 @@ -22,11 +24,13 @@ func test() -> void: if prop == null: set('prop', 1) + @warning_ignore("unsafe_call_argument") print(check(prop)) set('prop', null) var loop = null while loop != 2: if loop != null: + @warning_ignore("unsafe_call_argument") print(check(loop)) loop = 1 if loop == null else 2 diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd b/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd index 849df0921e..c1776fe1b4 100644 --- a/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd +++ b/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd @@ -14,4 +14,5 @@ func test(): func do_add_node(): var node = Node.new() node.name = "Node" + @warning_ignore("unsafe_call_argument") add_child(node) diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.gd b/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.gd new file mode 100644 index 0000000000..573060ae0f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.gd @@ -0,0 +1,37 @@ +func variant_func(x: Variant) -> void: + print(x) + +func int_func(x: int) -> void: + print(x) + +func float_func(x: float) -> void: + print(x) + +# We don't want to execute it because of errors, just analyze. +func no_exec_test(): + var untyped_int = 42 + var untyped_string = "abc" + var variant_int: Variant = 42 + var variant_string: Variant = "abc" + var typed_int: int = 42 + + variant_func(untyped_int) # No warning. + variant_func(untyped_string) # No warning. + variant_func(variant_int) # No warning. + variant_func(variant_string) # No warning. + variant_func(typed_int) # No warning. + + int_func(untyped_int) + int_func(untyped_string) + int_func(variant_int) + int_func(variant_string) + int_func(typed_int) # No warning. + + float_func(untyped_int) + float_func(untyped_string) + float_func(variant_int) + float_func(variant_string) + float_func(typed_int) # No warning. + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.out b/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.out new file mode 100644 index 0000000000..b8fcb67158 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/unsafe_call_argument.out @@ -0,0 +1,33 @@ +GDTEST_OK +>> WARNING +>> Line: 24 +>> UNSAFE_CALL_ARGUMENT +>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided. +>> WARNING +>> Line: 25 +>> UNSAFE_CALL_ARGUMENT +>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided. +>> WARNING +>> Line: 26 +>> UNSAFE_CALL_ARGUMENT +>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided. +>> WARNING +>> Line: 27 +>> UNSAFE_CALL_ARGUMENT +>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided. +>> WARNING +>> Line: 30 +>> UNSAFE_CALL_ARGUMENT +>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided. +>> WARNING +>> Line: 31 +>> UNSAFE_CALL_ARGUMENT +>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided. +>> WARNING +>> Line: 32 +>> UNSAFE_CALL_ARGUMENT +>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided. +>> WARNING +>> Line: 33 +>> UNSAFE_CALL_ARGUMENT +>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided. diff --git a/modules/gdscript/tests/scripts/parser/errors/match_guard_with_assignment.gd b/modules/gdscript/tests/scripts/parser/errors/match_guard_with_assignment.gd new file mode 100644 index 0000000000..d88b4a37c4 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/match_guard_with_assignment.gd @@ -0,0 +1,5 @@ +func test(): + var a = 0 + match a: + 0 when a = 1: + print("assignment not allowed on pattern guard") diff --git a/modules/gdscript/tests/scripts/parser/errors/match_guard_with_assignment.out b/modules/gdscript/tests/scripts/parser/errors/match_guard_with_assignment.out new file mode 100644 index 0000000000..e8f9130706 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/match_guard_with_assignment.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Assignment is not allowed inside an expression. diff --git a/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.gd b/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.gd index 7e1982597c..0c8a5d1367 100644 --- a/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.gd +++ b/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.gd @@ -14,3 +14,7 @@ func test(): var TAU = "TAU" print(TAU) + + # New keyword for pattern guards. + var when = "when" + print(when) diff --git a/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.out b/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.out index aae2ae13d5..8ac8e92ef7 100644 --- a/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.out +++ b/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.out @@ -4,3 +4,4 @@ PI INF NAN TAU +when diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd index f04f4de08d..19f6e08285 100644 --- a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd +++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd @@ -3,27 +3,32 @@ extends Node func test(): var child = Node.new() child.name = "Child" + @warning_ignore("unsafe_call_argument") add_child(child) child.owner = self var hey = Node.new() hey.name = "Hey" + @warning_ignore("unsafe_call_argument") child.add_child(hey) hey.owner = self hey.unique_name_in_owner = true var fake_hey = Node.new() fake_hey.name = "Hey" + @warning_ignore("unsafe_call_argument") add_child(fake_hey) fake_hey.owner = self var sub_child = Node.new() sub_child.name = "SubChild" + @warning_ignore("unsafe_call_argument") hey.add_child(sub_child) sub_child.owner = self var howdy = Node.new() howdy.name = "Howdy" + @warning_ignore("unsafe_call_argument") sub_child.add_child(howdy) howdy.owner = self howdy.unique_name_in_owner = true diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd index 8ba558e91d..3d9404b20b 100644 --- a/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd +++ b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd @@ -5,9 +5,11 @@ func test(): # Create the required node structure. var hello = Node.new() hello.name = "Hello" + @warning_ignore("unsafe_call_argument") add_child(hello) var world = Node.new() world.name = "World" + @warning_ignore("unsafe_call_argument") hello.add_child(world) # All the ways of writing node paths below with the `$` operator are valid. diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd index df6001c7e2..f16c768f7f 100644 --- a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd +++ b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd @@ -26,6 +26,7 @@ func test(): if true: (v as Callable).call() print() + @warning_ignore("unsafe_call_argument") other(v) print() diff --git a/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd b/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd index 59cdc7d6c2..31de73813f 100644 --- a/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd +++ b/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd @@ -2,4 +2,5 @@ func foo(x): return x + 1 func test(): + @warning_ignore("unsafe_call_argument") print(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(0))))))))))))))))))))))))) diff --git a/modules/gdscript/tests/scripts/parser/features/super.gd b/modules/gdscript/tests/scripts/parser/features/super.gd index f5ae2a74a7..33accd92a9 100644 --- a/modules/gdscript/tests/scripts/parser/features/super.gd +++ b/modules/gdscript/tests/scripts/parser/features/super.gd @@ -36,6 +36,7 @@ class SayNothing extends Say: print("howdy, see above") func say(name): + @warning_ignore("unsafe_call_argument") super(name + " super'd") print(prefix, " say nothing... or not? ", name) diff --git a/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd index 523959a016..20cc0cee2e 100644 --- a/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd +++ b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd @@ -29,6 +29,7 @@ func test(): const d = 1.1 _process(d) + @warning_ignore("unsafe_call_argument") print(is_equal_approx(ㄥ, PI + (d * PI))) func _process(Δ: float) -> void: diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd index 58b4df5a79..bc899a3a6f 100644 --- a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd @@ -21,6 +21,12 @@ func test(): var elem := e prints(var_to_str(e), var_to_str(elem)) + # GH-82021 + print("Test implicitly typed array literal.") + for e: float in [100, 200, 300]: + var elem := e + prints(var_to_str(e), var_to_str(elem)) + print("Test String-keys dictionary.") var d1 := {a = 1, b = 2, c = 3} for k: StringName in d1: diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out index f67f7d89d5..eeebdc4be5 100644 --- a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out @@ -15,6 +15,10 @@ Test typed int array. 10.0 10.0 20.0 20.0 30.0 30.0 +Test implicitly typed array literal. +100.0 100.0 +200.0 200.0 +300.0 300.0 Test String-keys dictionary. &"a" &"a" &"b" &"b" diff --git a/modules/gdscript/tests/scripts/runtime/features/match_with_pattern_guards.gd b/modules/gdscript/tests/scripts/runtime/features/match_with_pattern_guards.gd new file mode 100644 index 0000000000..4cb51f8512 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/match_with_pattern_guards.gd @@ -0,0 +1,71 @@ +var global := 0 + +func test(): + var a = 0 + var b = 1 + + match a: + 0 when b == 0: + print("does not run" if true else "") + 0 when b == 1: + print("guards work") + _: + print("does not run") + + match a: + var a_bind when b == 0: + prints("a is", a_bind, "and b is 0") + var a_bind when b == 1: + prints("a is", a_bind, "and b is 1") + _: + print("does not run") + + match a: + var a_bind when a_bind < 0: + print("a is less than zero") + var a_bind when a_bind == 0: + print("a is equal to zero") + _: + print("a is more than zero") + + match [1, 2, 3]: + [1, 2, var element] when element == 0: + print("does not run") + [1, 2, var element] when element == 3: + print("3rd element is 3") + + match a: + _ when b == 0: + print("does not run") + _ when b == 1: + print("works with wildcard too.") + _: + print("does not run") + + match a: + 0, 1 when b == 0: + print("does not run") + 0, 1 when b == 1: + print("guard with multiple patterns") + _: + print("does not run") + + match a: + 0 when b == 0: + print("does not run") + 0: + print("regular pattern after guard mismatch") + + match a: + 1 when side_effect(): + print("should not run the side effect call") + 0 when side_effect(): + print("will run the side effect call, but not this") + _: + assert(global == 1) + print("side effect only ran once") + +func side_effect(): + print("side effect") + global += 1 + return false diff --git a/modules/gdscript/tests/scripts/runtime/features/match_with_pattern_guards.out b/modules/gdscript/tests/scripts/runtime/features/match_with_pattern_guards.out new file mode 100644 index 0000000000..452d1ff4bf --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/match_with_pattern_guards.out @@ -0,0 +1,10 @@ +GDTEST_OK +guards work +a is 0 and b is 1 +a is equal to zero +3rd element is 3 +works with wildcard too. +guard with multiple patterns +regular pattern after guard mismatch +side effect +side effect only ran once diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.gd b/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.gd new file mode 100644 index 0000000000..d0cbeeab85 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.gd @@ -0,0 +1,45 @@ +# GH-82169 + +const Utils = preload("../../utils.notest.gd") + +class A: + static var test_static_var_a1 + static var test_static_var_a2 + var test_var_a1 + var test_var_a2 + static func test_static_func_a1(): pass + static func test_static_func_a2(): pass + func test_func_a1(): pass + func test_func_a2(): pass + signal test_signal_a1() + signal test_signal_a2() + +class B extends A: + static var test_static_var_b1 + static var test_static_var_b2 + var test_var_b1 + var test_var_b2 + static func test_static_func_b1(): pass + static func test_static_func_b2(): pass + func test_func_b1(): pass + func test_func_b2(): pass + signal test_signal_b1() + signal test_signal_b2() + +func test(): + var b := B.new() + for property in (B as GDScript).get_property_list(): + if str(property.name).begins_with("test_"): + print(Utils.get_property_signature(property, true)) + print("---") + for property in b.get_property_list(): + if str(property.name).begins_with("test_"): + print(Utils.get_property_signature(property)) + print("---") + for method in b.get_method_list(): + if str(method.name).begins_with("test_"): + print(Utils.get_method_signature(method)) + print("---") + for method in b.get_signal_list(): + if str(method.name).begins_with("test_"): + print(Utils.get_method_signature(method, true)) diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.out b/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.out new file mode 100644 index 0000000000..f294b66ef9 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.out @@ -0,0 +1,24 @@ +GDTEST_OK +static var test_static_var_a1: Variant +static var test_static_var_a2: Variant +static var test_static_var_b1: Variant +static var test_static_var_b2: Variant +--- +var test_var_b1: Variant +var test_var_b2: Variant +var test_var_a1: Variant +var test_var_a2: Variant +--- +static func test_static_func_b1() -> void +static func test_static_func_b2() -> void +func test_func_b1() -> void +func test_func_b2() -> void +static func test_static_func_a1() -> void +static func test_static_func_a2() -> void +func test_func_a1() -> void +func test_func_a2() -> void +--- +signal test_signal_b1() +signal test_signal_b2() +signal test_signal_a1() +signal test_signal_a2() diff --git a/modules/gdscript/tests/scripts/runtime/features/object_constructor.gd b/modules/gdscript/tests/scripts/runtime/features/object_constructor.gd new file mode 100644 index 0000000000..b875efef56 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/object_constructor.gd @@ -0,0 +1,6 @@ +# GH-73213 + +func test(): + var object := Object.new() # Not `Object()`. + print(object.get_class()) + object.free() diff --git a/modules/gdscript/tests/scripts/runtime/features/object_constructor.out b/modules/gdscript/tests/scripts/runtime/features/object_constructor.out new file mode 100644 index 0000000000..3673881576 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/object_constructor.out @@ -0,0 +1,2 @@ +GDTEST_OK +Object diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd index 2f55059334..fd1460a48f 100644 --- a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd +++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd @@ -12,6 +12,7 @@ func test(): print("end") func test_construct(v, f): + @warning_ignore("unsafe_call_argument") Vector2(v, v) # Built-in type construct. assert(not f) # Test unary operator reading from `nil`. diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables.gd b/modules/gdscript/tests/scripts/runtime/features/static_variables.gd index 8da8bb7e53..7fa76ca4b0 100644 --- a/modules/gdscript/tests/scripts/runtime/features/static_variables.gd +++ b/modules/gdscript/tests/scripts/runtime/features/static_variables.gd @@ -44,6 +44,7 @@ func test(): @warning_ignore("unsafe_method_access") var path = get_script().get_path().get_base_dir() + @warning_ignore("unsafe_call_argument") var other = load(path + "/static_variables_load.gd") prints("load.perm:", other.perm) diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd index fead2df854..1e66d8f34a 100644 --- a/modules/gdscript/tests/scripts/runtime/features/stringify.gd +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd @@ -24,7 +24,8 @@ func test(): print(StringName("hello")) print(NodePath("hello/world")) var node := Node.new() - print(RID(node)) + @warning_ignore("unsafe_call_argument") + print(RID(node)) # TODO: Why is the constructor (or implicit cast) not documented? print(node.get_name) print(node.property_list_changed) node.free() diff --git a/modules/gdscript/tests/scripts/utils.notest.gd b/modules/gdscript/tests/scripts/utils.notest.gd index fb20817117..781843b8e2 100644 --- a/modules/gdscript/tests/scripts/utils.notest.gd +++ b/modules/gdscript/tests/scripts/utils.notest.gd @@ -17,7 +17,7 @@ static func get_type(property: Dictionary, is_return: bool = false) -> String: TYPE_OBJECT: if not str(property.class_name).is_empty(): return property.class_name - return variant_get_type_name(property.type) + return type_string(property.type) static func get_property_signature(property: Dictionary, is_static: bool = false) -> String: @@ -66,88 +66,6 @@ static func get_method_signature(method: Dictionary, is_signal: bool = false) -> return result -static func variant_get_type_name(type: Variant.Type) -> String: - match type: - TYPE_NIL: - return "Nil" # `Nil` in core, `null` in GDScript. - TYPE_BOOL: - return "bool" - TYPE_INT: - return "int" - TYPE_FLOAT: - return "float" - TYPE_STRING: - return "String" - TYPE_VECTOR2: - return "Vector2" - TYPE_VECTOR2I: - return "Vector2i" - TYPE_RECT2: - return "Rect2" - TYPE_RECT2I: - return "Rect2i" - TYPE_VECTOR3: - return "Vector3" - TYPE_VECTOR3I: - return "Vector3i" - TYPE_TRANSFORM2D: - return "Transform2D" - TYPE_VECTOR4: - return "Vector4" - TYPE_VECTOR4I: - return "Vector4i" - TYPE_PLANE: - return "Plane" - TYPE_QUATERNION: - return "Quaternion" - TYPE_AABB: - return "AABB" - TYPE_BASIS: - return "Basis" - TYPE_TRANSFORM3D: - return "Transform3D" - TYPE_PROJECTION: - return "Projection" - TYPE_COLOR: - return "Color" - TYPE_STRING_NAME: - return "StringName" - TYPE_NODE_PATH: - return "NodePath" - TYPE_RID: - return "RID" - TYPE_OBJECT: - return "Object" - TYPE_CALLABLE: - return "Callable" - TYPE_SIGNAL: - return "Signal" - TYPE_DICTIONARY: - return "Dictionary" - TYPE_ARRAY: - return "Array" - TYPE_PACKED_BYTE_ARRAY: - return "PackedByteArray" - TYPE_PACKED_INT32_ARRAY: - return "PackedInt32Array" - TYPE_PACKED_INT64_ARRAY: - return "PackedInt64Array" - TYPE_PACKED_FLOAT32_ARRAY: - return "PackedFloat32Array" - TYPE_PACKED_FLOAT64_ARRAY: - return "PackedFloat64Array" - TYPE_PACKED_STRING_ARRAY: - return "PackedStringArray" - TYPE_PACKED_VECTOR2_ARRAY: - return "PackedVector2Array" - TYPE_PACKED_VECTOR3_ARRAY: - return "PackedVector3Array" - TYPE_PACKED_COLOR_ARRAY: - return "PackedColorArray" - push_error("Argument `type` is invalid. Use `TYPE_*` constants.") - return "<invalid type>" - - static func get_property_hint_name(hint: PropertyHint) -> String: match hint: PROPERTY_HINT_NONE: diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index b86a8b3cb1..467bedc4b2 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -223,6 +223,16 @@ void test(TestType p_type) { // Initialize the language for the test routine. init_language(fa->get_path_absolute().get_base_dir()); + // Load global classes. + TypedArray<Dictionary> script_classes = ProjectSettings::get_singleton()->get_global_class_list(); + for (int i = 0; i < script_classes.size(); i++) { + Dictionary c = script_classes[i]; + if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) { + continue; + } + ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"]); + } + Vector<uint8_t> buf; uint64_t flen = fa->get_length(); buf.resize(flen + 1); diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 3787d0fe5e..cb45a6589e 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -49,6 +49,67 @@ #include <shlwapi.h> #endif +static bool _get_blender_version(const String &p_path, int &r_major, int &r_minor, String *r_err = nullptr) { + String path = p_path; +#ifdef WINDOWS_ENABLED + path = path.path_join("blender.exe"); +#else + path = path.path_join("blender"); +#endif + +#if defined(MACOS_ENABLED) + if (!FileAccess::exists(path)) { + path = p_path.path_join("Blender"); + } +#endif + + if (!FileAccess::exists(path)) { + if (r_err) { + *r_err = TTR("Path does not contain a Blender installation."); + } + return false; + } + List<String> args; + args.push_back("--version"); + String pipe; + Error err = OS::get_singleton()->execute(path, args, &pipe); + if (err != OK) { + if (r_err) { + *r_err = TTR("Can't execute Blender binary."); + } + return false; + } + int bl = pipe.find("Blender "); + if (bl == -1) { + if (r_err) { + *r_err = vformat(TTR("Unexpected --version output from Blender binary at: %s."), path); + } + return false; + } + pipe = pipe.substr(bl); + pipe = pipe.replace_first("Blender ", ""); + int pp = pipe.find("."); + if (pp == -1) { + if (r_err) { + *r_err = TTR("Path supplied lacks a Blender binary."); + } + return false; + } + String v = pipe.substr(0, pp); + r_major = v.to_int(); + if (r_major < 3) { + if (r_err) { + *r_err = TTR("This Blender installation is too old for this importer (not 3.0+)."); + } + return false; + } + + int pp2 = pipe.find(".", pp + 1); + r_minor = pp2 > pp ? pipe.substr(pp + 1, pp2 - pp - 1).to_int() : 0; + + return true; +} + uint32_t EditorSceneFormatImporterBlend::get_import_flags() const { return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; } @@ -60,8 +121,13 @@ void EditorSceneFormatImporterBlend::get_extensions(List<String> *r_extensions) Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) { - // Get global paths for source and sink. + String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path"); + if (blender_major_version == -1 || blender_minor_version == -1) { + _get_blender_version(blender_path, blender_major_version, blender_minor_version, nullptr); + } + + // Get global paths for source and sink. // Escape paths to be valid Python strings to embed in the script. const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape(); const String sink = ProjectSettings::get_singleton()->get_imported_files_path().path_join( @@ -153,9 +219,17 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ parameters_map["export_tangents"] = false; } if (p_options.has(SNAME("blender/animation/group_tracks")) && p_options[SNAME("blender/animation/group_tracks")]) { - parameters_map["export_nla_strips"] = true; + if (blender_major_version > 3 || (blender_major_version == 3 && blender_minor_version >= 6)) { + parameters_map["export_animation_mode"] = "ACTIONS"; + } else { + parameters_map["export_nla_strips"] = true; + } } else { - parameters_map["export_nla_strips"] = false; + if (blender_major_version > 3 || (blender_major_version == 3 && blender_minor_version >= 6)) { + parameters_map["export_animation_mode"] = "ACTIVE_ACTIONS"; + } else { + parameters_map["export_nla_strips"] = false; + } } if (p_options.has(SNAME("blender/animation/limit_playback")) && p_options[SNAME("blender/animation/limit_playback")]) { parameters_map["export_frame_range"] = true; @@ -269,67 +343,8 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li /////////////////////////// static bool _test_blender_path(const String &p_path, String *r_err = nullptr) { - String path = p_path; -#ifdef WINDOWS_ENABLED - path = path.path_join("blender.exe"); -#else - path = path.path_join("blender"); -#endif - -#if defined(MACOS_ENABLED) - if (!FileAccess::exists(path)) { - path = path.path_join("Blender"); - } -#endif - - if (!FileAccess::exists(path)) { - if (r_err) { - *r_err = TTR("Path does not contain a Blender installation."); - } - return false; - } - List<String> args; - args.push_back("--version"); - String pipe; - Error err = OS::get_singleton()->execute(path, args, &pipe); - if (err != OK) { - if (r_err) { - *r_err = TTR("Can't execute Blender binary."); - } - return false; - } - int bl = pipe.find("Blender "); - if (bl == -1) { - if (r_err) { - *r_err = vformat(TTR("Unexpected --version output from Blender binary at: %s"), path); - } - return false; - } - pipe = pipe.substr(bl); - pipe = pipe.replace_first("Blender ", ""); - int pp = pipe.find("."); - if (pp == -1) { - if (r_err) { - *r_err = TTR("Path supplied lacks a Blender binary."); - } - return false; - } - String v = pipe.substr(0, pp); - int version = v.to_int(); - if (version < 3) { - if (r_err) { - *r_err = TTR("This Blender installation is too old for this importer (not 3.0+)."); - } - return false; - } - if (version > 3) { - if (r_err) { - *r_err = TTR("This Blender installation is too new for this importer (not 3.x)."); - } - return false; - } - - return true; + int major, minor; + return _get_blender_version(p_path, major, minor, r_err); } bool EditorFileSystemImportFormatSupportQueryBlend::is_active() const { diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h index c77a23f9f6..ec467db457 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.h +++ b/modules/gltf/editor/editor_scene_importer_blend.h @@ -43,6 +43,9 @@ class ConfirmationDialog; class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter { GDCLASS(EditorSceneFormatImporterBlend, EditorSceneFormatImporter); + int blender_major_version = -1; + int blender_minor_version = -1; + public: enum { BLEND_VISIBLE_ALL, diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index cee65d70c9..bac988630d 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -7347,6 +7347,12 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_ } Node *GLTFDocument::_generate_scene_node_tree(Ref<GLTFState> p_state) { + // Generate the skeletons and skins (if any). + Error err = _create_skeletons(p_state); + ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skeletons."); + err = _create_skins(p_state); + ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skins."); + // Generate the node tree. Node *single_root; if (p_state->extensions_used.has("GODOT_single_root")) { _generate_scene_node(p_state, 0, nullptr, nullptr); @@ -7539,14 +7545,6 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se err = _determine_skeletons(p_state); ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); - /* CREATE SKELETONS */ - err = _create_skeletons(p_state); - ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); - - /* CREATE SKINS */ - err = _create_skins(p_state); - ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); - /* PARSE MESHES (we have enough info now) */ err = _parse_meshes(p_state); ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); diff --git a/modules/lightmapper_rd/config.py b/modules/lightmapper_rd/config.py index d22f9454ed..ecc61c2d7e 100644 --- a/modules/lightmapper_rd/config.py +++ b/modules/lightmapper_rd/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return True + return env.editor_build and platform not in ["android", "ios"] def configure(env): diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 4ed730b3af..556b0b4374 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -614,25 +614,29 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int } } -LightmapperRD::BakeError LightmapperRD::_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) { +static Vector<RD::Uniform> dilate_or_denoise_common_uniforms(RID &p_source_light_tex, RID &p_dest_light_tex) { Vector<RD::Uniform> uniforms; { - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 0; - u.append_id(dest_light_tex); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 1; - u.append_id(source_light_tex); - uniforms.push_back(u); - } + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 0; + u.append_id(p_dest_light_tex); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1; + u.append_id(p_source_light_tex); + uniforms.push_back(u); } + return uniforms; +} + +LightmapperRD::BakeError LightmapperRD::_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) { + Vector<RD::Uniform> uniforms = dilate_or_denoise_common_uniforms(source_light_tex, dest_light_tex); + RID compute_shader_dilate = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("dilate")); ERR_FAIL_COND_V(compute_shader_dilate.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen RID compute_shader_dilate_pipeline = rd->compute_pipeline_create(compute_shader_dilate); @@ -667,7 +671,77 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade return BAKE_OK; } -LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) { +LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function) { + RID denoise_params_buffer = p_rd->uniform_buffer_create(sizeof(DenoiseParams)); + DenoiseParams denoise_params; + denoise_params.spatial_bandwidth = 5.0f; + denoise_params.light_bandwidth = p_denoiser_strength; + denoise_params.albedo_bandwidth = 1.0f; + denoise_params.normal_bandwidth = 0.1f; + denoise_params.filter_strength = 10.0f; + p_rd->buffer_update(denoise_params_buffer, 0, sizeof(DenoiseParams), &denoise_params); + + Vector<RD::Uniform> uniforms = dilate_or_denoise_common_uniforms(p_source_light_tex, p_dest_light_tex); + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2; + u.append_id(p_source_normal_tex); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 3; + u.append_id(denoise_params_buffer); + uniforms.push_back(u); + } + + RID compute_shader_denoise = p_rd->shader_create_from_spirv(p_compute_shader->get_spirv_stages("denoise")); + ERR_FAIL_COND_V(compute_shader_denoise.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); + + RID compute_shader_denoise_pipeline = p_rd->compute_pipeline_create(compute_shader_denoise); + RID denoise_uniform_set = p_rd->uniform_set_create(uniforms, compute_shader_denoise, 1); + + // We denoise in fixed size regions and synchronize execution to avoid GPU timeouts. + // We use a region with 1/4 the amount of pixels if we're denoising SH lightmaps, as + // all four of them are denoised in the shader in one dispatch. + const int max_region_size = p_bake_sh ? 512 : 1024; + int x_regions = (p_atlas_size.width - 1) / max_region_size + 1; + int y_regions = (p_atlas_size.height - 1) / max_region_size + 1; + for (int s = 0; s < p_atlas_slices; s++) { + p_push_constant.atlas_slice = s; + + for (int i = 0; i < x_regions; i++) { + for (int j = 0; j < y_regions; j++) { + int x = i * max_region_size; + int y = j * max_region_size; + int w = MIN((i + 1) * max_region_size, p_atlas_size.width) - x; + int h = MIN((j + 1) * max_region_size, p_atlas_size.height) - y; + p_push_constant.region_ofs[0] = x; + p_push_constant.region_ofs[1] = y; + + RD::ComputeListID compute_list = p_rd->compute_list_begin(); + p_rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_denoise_pipeline); + p_rd->compute_list_bind_uniform_set(compute_list, p_compute_base_uniform_set, 0); + p_rd->compute_list_bind_uniform_set(compute_list, denoise_uniform_set, 1); + p_rd->compute_list_set_push_constant(compute_list, &p_push_constant, sizeof(PushConstant)); + p_rd->compute_list_dispatch(compute_list, (w - 1) / 8 + 1, (h - 1) / 8 + 1, 1); + p_rd->compute_list_end(); + + p_rd->submit(); + p_rd->sync(); + } + } + } + + p_rd->free(compute_shader_denoise); + p_rd->free(denoise_params_buffer); + + return BAKE_OK; +} + +LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) { if (p_step_function) { p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true); } @@ -1419,14 +1493,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d } #endif - { - SWAP(light_accum_tex, light_accum_tex2); - BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1)); - if (unlikely(error != BAKE_OK)) { - return error; - } - } - /* DENOISE */ if (p_use_denoiser) { @@ -1434,39 +1500,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d p_step_function(0.8, RTR("Denoising"), p_bake_userdata, true); } - Ref<LightmapDenoiser> denoiser = LightmapDenoiser::create(); - if (denoiser.is_valid()) { - for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { - Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); - - Ref<Image> denoised = denoiser->denoise_image(img); - if (denoised != img) { - denoised->convert(Image::FORMAT_RGBAH); - Vector<uint8_t> ds = denoised->get_data(); - denoised.unref(); //avoid copy on write - { //restore alpha - uint32_t count = s.size() / 2; //uint16s - const uint16_t *src = (const uint16_t *)s.ptr(); - uint16_t *dst = (uint16_t *)ds.ptrw(); - for (uint32_t j = 0; j < count; j += 4) { - dst[j + 3] = src[j + 3]; - } - } - rd->texture_update(light_accum_tex, i, ds); - } - } - } - { SWAP(light_accum_tex, light_accum_tex2); - BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1)); + BakeError error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, atlas_size, atlas_slices, p_bake_sh, p_step_function); if (unlikely(error != BAKE_OK)) { return error; } } } + { + SWAP(light_accum_tex, light_accum_tex2); + BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1)); + if (unlikely(error != BAKE_OK)) { + return error; + } + } + #ifdef DEBUG_TEXTURES for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index 061c9ba000..7120a21b84 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -229,11 +229,22 @@ class LightmapperRD : public Lightmapper { Vector<Ref<Image>> bake_textures; Vector<Color> probe_values; + struct DenoiseParams { + float spatial_bandwidth; + float light_bandwidth; + float albedo_bandwidth; + float normal_bandwidth; + + float filter_strength; + float pad[3]; + }; + 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 _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); + BakeError _denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function); public: virtual void add_mesh(const MeshData &p_mesh) override; @@ -241,7 +252,7 @@ public: virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override; virtual void add_probe(const Vector3 &p_position) override; - virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr, float p_exposure_normalization = 1.0) override; + virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr, float p_exposure_normalization = 1.0) override; int get_bake_texture_count() const override; Ref<Image> get_bake_texture(int p_index) const override; diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index 4d3f2d46a4..ce33f2ed1d 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -5,6 +5,7 @@ secondary = "#define MODE_BOUNCE_LIGHT"; dilate = "#define MODE_DILATE"; unocclude = "#define MODE_UNOCCLUDE"; light_probes = "#define MODE_LIGHT_PROBES"; +denoise = "#define MODE_DENOISE"; #[compute] @@ -65,11 +66,24 @@ layout(set = 1, binding = 6) uniform texture2D environment; layout(rgba32f, set = 1, binding = 5) uniform restrict writeonly image2DArray primary_dynamic; #endif -#ifdef MODE_DILATE +#if defined(MODE_DILATE) || defined(MODE_DENOISE) layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2DArray dest_light; layout(set = 1, binding = 1) uniform texture2DArray source_light; #endif +#ifdef MODE_DENOISE +layout(set = 1, binding = 2) uniform texture2DArray source_normal; +layout(set = 1, binding = 3) uniform DenoiseParams { + float spatial_bandwidth; + float light_bandwidth; + float albedo_bandwidth; + float normal_bandwidth; + + float filter_strength; +} +denoise_params; +#endif + layout(push_constant, std430) uniform Params { ivec2 atlas_size; // x used for light probe mode total probes uint ray_count; @@ -735,4 +749,153 @@ void main() { imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), c); #endif + +#ifdef MODE_DENOISE + // Joint Non-local means (JNLM) denoiser. + // + // Based on YoctoImageDenoiser's JNLM implementation with corrections from "Nonlinearly Weighted First-order Regression for Denoising Monte Carlo Renderings". + // + // <https://github.com/ManuelPrandini/YoctoImageDenoiser/blob/06e19489dd64e47792acffde536393802ba48607/libs/yocto_extension/yocto_extension.cpp#L207> + // <https://benedikt-bitterli.me/nfor/nfor.pdf> + // + // MIT License + // + // Copyright (c) 2020 ManuelPrandini + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in all + // copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + // SOFTWARE. + // + // Most of the constants below have been hand-picked to fit the common scenarios lightmaps + // are generated with, but they can be altered freely to experiment and achieve better results. + + // Half the size of the patch window around each pixel that is weighted to compute the denoised pixel. + // A value of 1 represents a 3x3 window, a value of 2 a 5x5 window, etc. + const int HALF_PATCH_WINDOW = 4; + + // Half the size of the search window around each pixel that is denoised and weighted to compute the denoised pixel. + const int HALF_SEARCH_WINDOW = 10; + + // For all of the following sigma values, smaller values will give less weight to pixels that have a bigger distance + // in the feature being evaluated. Therefore, smaller values are likely to cause more noise to appear, but will also + // cause less features to be erased in the process. + + // Controls how much the spatial distance of the pixels influences the denoising weight. + const float SIGMA_SPATIAL = denoise_params.spatial_bandwidth; + + // Controls how much the light color distance of the pixels influences the denoising weight. + const float SIGMA_LIGHT = denoise_params.light_bandwidth; + + // Controls how much the albedo color distance of the pixels influences the denoising weight. + const float SIGMA_ALBEDO = denoise_params.albedo_bandwidth; + + // Controls how much the normal vector distance of the pixels influences the denoising weight. + const float SIGMA_NORMAL = denoise_params.normal_bandwidth; + + // Strength of the filter. The original paper recommends values around 10 to 15 times the Sigma parameter. + const float FILTER_VALUE = denoise_params.filter_strength * SIGMA_LIGHT; + + // Formula constants. + const int PATCH_WINDOW_DIMENSION = (HALF_PATCH_WINDOW * 2 + 1); + const int PATCH_WINDOW_DIMENSION_SQUARE = (PATCH_WINDOW_DIMENSION * PATCH_WINDOW_DIMENSION); + const float TWO_SIGMA_SPATIAL_SQUARE = 2.0f * SIGMA_SPATIAL * SIGMA_SPATIAL; + const float TWO_SIGMA_LIGHT_SQUARE = 2.0f * SIGMA_LIGHT * SIGMA_LIGHT; + const float TWO_SIGMA_ALBEDO_SQUARE = 2.0f * SIGMA_ALBEDO * SIGMA_ALBEDO; + const float TWO_SIGMA_NORMAL_SQUARE = 2.0f * SIGMA_NORMAL * SIGMA_NORMAL; + const float FILTER_SQUARE_TWO_SIGMA_LIGHT_SQUARE = FILTER_VALUE * FILTER_VALUE * TWO_SIGMA_LIGHT_SQUARE; + const float EPSILON = 1e-6f; + +#ifdef USE_SH_LIGHTMAPS + const uint slice_count = 4; + const uint slice_base = params.atlas_slice * slice_count; +#else + const uint slice_count = 1; + const uint slice_base = params.atlas_slice; +#endif + + for (uint i = 0; i < slice_count; i++) { + uint lightmap_slice = slice_base + i; + vec3 denoised_rgb = vec3(0.0f); + vec4 input_light = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, lightmap_slice), 0); + vec3 input_albedo = texelFetch(sampler2DArray(albedo_tex, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).rgb; + vec3 input_normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz; + if (length(input_normal) > EPSILON) { + // Compute the denoised pixel if the normal is valid. + float sum_weights = 0.0f; + vec3 input_rgb = input_light.rgb; + for (int search_y = -HALF_SEARCH_WINDOW; search_y <= HALF_SEARCH_WINDOW; search_y++) { + for (int search_x = -HALF_SEARCH_WINDOW; search_x <= HALF_SEARCH_WINDOW; search_x++) { + ivec2 search_pos = atlas_pos + ivec2(search_x, search_y); + vec3 search_rgb = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(search_pos, lightmap_slice), 0).rgb; + vec3 search_albedo = texelFetch(sampler2DArray(albedo_tex, linear_sampler), ivec3(search_pos, params.atlas_slice), 0).rgb; + vec3 search_normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(search_pos, params.atlas_slice), 0).xyz; + float patch_square_dist = 0.0f; + for (int offset_y = -HALF_PATCH_WINDOW; offset_y <= HALF_PATCH_WINDOW; offset_y++) { + for (int offset_x = -HALF_PATCH_WINDOW; offset_x <= HALF_PATCH_WINDOW; offset_x++) { + ivec2 offset_input_pos = atlas_pos + ivec2(offset_x, offset_y); + ivec2 offset_search_pos = search_pos + ivec2(offset_x, offset_y); + vec3 offset_input_rgb = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(offset_input_pos, lightmap_slice), 0).rgb; + vec3 offset_search_rgb = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(offset_search_pos, lightmap_slice), 0).rgb; + vec3 offset_delta_rgb = offset_input_rgb - offset_search_rgb; + patch_square_dist += dot(offset_delta_rgb, offset_delta_rgb) - TWO_SIGMA_LIGHT_SQUARE; + } + } + + patch_square_dist = max(0.0f, patch_square_dist / (3.0f * PATCH_WINDOW_DIMENSION_SQUARE)); + + float weight = 1.0f; + + // Ignore weight if search position is out of bounds. + weight *= step(0, search_pos.x) * step(search_pos.x, params.atlas_size.x - 1); + weight *= step(0, search_pos.y) * step(search_pos.y, params.atlas_size.y - 1); + + // Ignore weight if normal is zero length. + weight *= step(EPSILON, length(search_normal)); + + // Weight with pixel distance. + vec2 pixel_delta = vec2(search_x, search_y); + float pixel_square_dist = dot(pixel_delta, pixel_delta); + weight *= exp(-pixel_square_dist / TWO_SIGMA_SPATIAL_SQUARE); + + // Weight with patch. + weight *= exp(-patch_square_dist / FILTER_SQUARE_TWO_SIGMA_LIGHT_SQUARE); + + // Weight with albedo. + vec3 albedo_delta = input_albedo - search_albedo; + float albedo_square_dist = dot(albedo_delta, albedo_delta); + weight *= exp(-albedo_square_dist / TWO_SIGMA_ALBEDO_SQUARE); + + // Weight with normal. + vec3 normal_delta = input_normal - search_normal; + float normal_square_dist = dot(normal_delta, normal_delta); + weight *= exp(-normal_square_dist / TWO_SIGMA_NORMAL_SQUARE); + + denoised_rgb += weight * search_rgb; + sum_weights += weight; + } + } + + denoised_rgb /= sum_weights; + } else { + // Ignore pixels where the normal is empty, just copy the light color. + denoised_rgb = input_light.rgb; + } + + imageStore(dest_light, ivec3(atlas_pos, lightmap_slice), vec4(denoised_rgb, input_light.a)); + } +#endif } diff --git a/modules/lightmapper_rd/register_types.cpp b/modules/lightmapper_rd/register_types.cpp index 7ec4a40766..984ce88316 100644 --- a/modules/lightmapper_rd/register_types.cpp +++ b/modules/lightmapper_rd/register_types.cpp @@ -58,7 +58,6 @@ void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level) { GLOBAL_DEF("rendering/lightmapping/bake_quality/high_quality_probe_ray_count", 512); GLOBAL_DEF("rendering/lightmapping/bake_quality/ultra_quality_probe_ray_count", 2048); GLOBAL_DEF("rendering/lightmapping/bake_performance/max_rays_per_probe_pass", 64); - GLOBAL_DEF("rendering/lightmapping/primitive_meshes/texel_size", 0.2); #ifndef _3D_DISABLED GDREGISTER_CLASS(LightmapperRD); Lightmapper::create_gpu = create_lightmapper_rd; diff --git a/modules/minimp3/SCsub b/modules/minimp3/SCsub index 20e3165f38..09e84f71e9 100644 --- a/modules/minimp3/SCsub +++ b/modules/minimp3/SCsub @@ -13,5 +13,8 @@ if not env.msvc: else: env_minimp3.Prepend(CPPPATH=[thirdparty_dir]) +if not env["minimp3_extra_formats"]: + env_minimp3.Append(CPPDEFINES=["MINIMP3_ONLY_MP3"]) + # Godot source files env_minimp3.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 6af86a96dc..4efa4d329e 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -28,7 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#define MINIMP3_ONLY_MP3 #define MINIMP3_FLOAT_OUTPUT #define MINIMP3_IMPLEMENTATION #define MINIMP3_NO_STDIO diff --git a/modules/minimp3/config.py b/modules/minimp3/config.py index e6bdcb2a83..115b376fc8 100644 --- a/modules/minimp3/config.py +++ b/modules/minimp3/config.py @@ -2,6 +2,14 @@ def can_build(env, platform): return True +def get_opts(platform): + from SCons.Variables import BoolVariable + + return [ + BoolVariable("minimp3_extra_formats", "Build minimp3 with MP1/MP2 decoding support", False), + ] + + def configure(env): pass diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp index 4e56120ec6..d60b979c3f 100644 --- a/modules/minimp3/resource_importer_mp3.cpp +++ b/modules/minimp3/resource_importer_mp3.cpp @@ -47,6 +47,10 @@ String ResourceImporterMP3::get_visible_name() const { } void ResourceImporterMP3::get_recognized_extensions(List<String> *p_extensions) const { +#ifndef MINIMP3_ONLY_MP3 + p_extensions->push_back("mp1"); + p_extensions->push_back("mp2"); +#endif p_extensions->push_back("mp3"); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 81b2ffef34..09269508b7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -134,6 +134,38 @@ namespace Godot } /// <summary> + /// Returns the difference between the two angles, + /// in range of -<see cref="Pi"/>, <see cref="Pi"/>. + /// When <paramref name="from"/> and <paramref name="to"/> are opposite, + /// returns -<see cref="Pi"/> if <paramref name="from"/> is smaller than <paramref name="to"/>, + /// or <see cref="Pi"/> otherwise. + /// </summary> + /// <param name="from">The start angle.</param> + /// <param name="to">The destination angle.</param> + /// <returns>The difference between the two angles.</returns> + public static float AngleDifference(float from, float to) + { + float difference = (to - from) % MathF.Tau; + return ((2.0f * difference) % MathF.Tau) - difference; + } + + /// <summary> + /// Returns the difference between the two angles, + /// in range of -<see cref="Pi"/>, <see cref="Pi"/>. + /// When <paramref name="from"/> and <paramref name="to"/> are opposite, + /// returns -<see cref="Pi"/> if <paramref name="from"/> is smaller than <paramref name="to"/>, + /// or <see cref="Pi"/> otherwise. + /// </summary> + /// <param name="from">The start angle.</param> + /// <param name="to">The destination angle.</param> + /// <returns>The difference between the two angles.</returns> + public static double AngleDifference(double from, double to) + { + double difference = (to - from) % Math.Tau; + return ((2.0 * difference) % Math.Tau) - difference; + } + + /// <summary> /// Returns the arc sine of <paramref name="s"/> in radians. /// Use to get the angle of sine <paramref name="s"/>. /// </summary> @@ -1093,9 +1125,7 @@ namespace Godot /// <returns>The resulting angle of the interpolation.</returns> public static float LerpAngle(float from, float to, float weight) { - float difference = (to - from) % MathF.Tau; - float distance = ((2 * difference) % MathF.Tau) - difference; - return from + (distance * weight); + return from + AngleDifference(from, to) * weight; } /// <summary> @@ -1110,9 +1140,7 @@ namespace Godot /// <returns>The resulting angle of the interpolation.</returns> public static double LerpAngle(double from, double to, double weight) { - double difference = (to - from) % Math.Tau; - double distance = ((2 * difference) % Math.Tau) - difference; - return from + (distance * weight); + return from + AngleDifference(from, to) * weight; } /// <summary> @@ -1429,6 +1457,38 @@ namespace Godot } /// <summary> + /// Rotates <paramref name="from"/> toward <paramref name="to"/> by the <paramref name="delta"/> amount. Will not go past <paramref name="to"/>. + /// Similar to <see cref="MoveToward(float, float, float)"/> but interpolates correctly when the angles wrap around <see cref="Tau"/>. + /// If <paramref name="delta"/> is negative, this function will rotate away from <paramref name="to"/>, toward the opposite angle, and will not go past the opposite angle. + /// </summary> + /// <param name="from">The start angle.</param> + /// <param name="to">The angle to move towards.</param> + /// <param name="delta">The amount to move by.</param> + /// <returns>The angle after moving.</returns> + public static float RotateToward(float from, float to, float delta) + { + float difference = AngleDifference(from, to); + float absDifference = Math.Abs(difference); + return from + Math.Clamp(delta, absDifference - MathF.PI, absDifference) * (difference >= 0.0f ? 1.0f : -1.0f); + } + + /// <summary> + /// Rotates <paramref name="from"/> toward <paramref name="to"/> by the <paramref name="delta"/> amount. Will not go past <paramref name="to"/>. + /// Similar to <see cref="MoveToward(double, double, double)"/> but interpolates correctly when the angles wrap around <see cref="Tau"/>. + /// If <paramref name="delta"/> is negative, this function will rotate away from <paramref name="to"/>, toward the opposite angle, and will not go past the opposite angle. + /// </summary> + /// <param name="from">The start angle.</param> + /// <param name="to">The angle to move towards.</param> + /// <param name="delta">The amount to move by.</param> + /// <returns>The angle after moving.</returns> + public static double RotateToward(double from, double to, double delta) + { + double difference = AngleDifference(from, to); + double absDifference = Math.Abs(difference); + return from + Math.Clamp(delta, absDifference - Math.PI, absDifference) * (difference >= 0.0 ? 1.0 : -1.0); + } + + /// <summary> /// Rounds <paramref name="s"/> to the nearest whole number, /// with halfway cases rounded towards the nearest multiple of two. /// </summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs index a656c5de90..dc53e48bd0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -206,7 +206,7 @@ namespace Godot.NativeInterop } case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS: case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS: - return $"Invalid call to {where}. Expected {error.Argument} arguments."; + return $"Invalid call to {where}. Expected {error.Expected} arguments."; case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD: return $"Invalid call. Nonexistent {where}."; case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL: diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index 0b203c5148..d26d4662a0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -265,7 +265,7 @@ namespace Godot return new Vector3( Mathf.BezierDerivative(X, control1.X, control2.X, end.X, t), Mathf.BezierDerivative(Y, control1.Y, control2.Y, end.Y, t), - Mathf.BezierDerivative(Z, control1.Z, control2.Z, end.Y, t) + Mathf.BezierDerivative(Z, control1.Z, control2.Z, end.Z, t) ); } diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp index 234c2d161f..5005000eae 100644 --- a/modules/raycast/raycast_occlusion_cull.cpp +++ b/modules/raycast/raycast_occlusion_cull.cpp @@ -250,17 +250,15 @@ void RaycastOcclusionCull::free_occluder(RID p_occluder) { //////////////////////////////////////////////////////// void RaycastOcclusionCull::add_scenario(RID p_scenario) { - if (scenarios.has(p_scenario)) { - scenarios[p_scenario].removed = false; - } else { - scenarios[p_scenario] = Scenario(); - } + ERR_FAIL_COND(scenarios.has(p_scenario)); + scenarios[p_scenario] = Scenario(); } void RaycastOcclusionCull::remove_scenario(RID p_scenario) { - ERR_FAIL_COND(!scenarios.has(p_scenario)); - Scenario &scenario = scenarios[p_scenario]; - scenario.removed = true; + Scenario *scenario = scenarios.getptr(p_scenario); + ERR_FAIL_NULL(scenario); + scenario->free(); + scenarios.erase(p_scenario); } void RaycastOcclusionCull::scenario_set_instance(RID p_scenario, RID p_instance, RID p_occluder, const Transform3D &p_xform, bool p_enabled) { @@ -390,6 +388,23 @@ void RaycastOcclusionCull::Scenario::_transform_vertices_range(const Vector3 *p_ } } +void RaycastOcclusionCull::Scenario::free() { + if (commit_thread) { + if (commit_thread->is_started()) { + commit_thread->wait_to_finish(); + } + memdelete(commit_thread); + commit_thread = nullptr; + } + + for (int i = 0; i < 2; i++) { + if (ebr_scene[i]) { + rtcReleaseScene(ebr_scene[i]); + ebr_scene[i] = nullptr; + } + } +} + void RaycastOcclusionCull::Scenario::_commit_scene(void *p_ud) { Scenario *scenario = (Scenario *)p_ud; int commit_idx = 1 - (scenario->current_scene_idx); @@ -397,8 +412,8 @@ void RaycastOcclusionCull::Scenario::_commit_scene(void *p_ud) { scenario->commit_done = true; } -bool RaycastOcclusionCull::Scenario::update() { - ERR_FAIL_NULL_V(singleton, false); +void RaycastOcclusionCull::Scenario::update() { + ERR_FAIL_NULL(singleton); if (commit_thread == nullptr) { commit_thread = memnew(Thread); @@ -409,22 +424,12 @@ bool RaycastOcclusionCull::Scenario::update() { commit_thread->wait_to_finish(); current_scene_idx = 1 - current_scene_idx; } else { - return false; + return; } } - if (removed) { - if (ebr_scene[0]) { - rtcReleaseScene(ebr_scene[0]); - } - if (ebr_scene[1]) { - rtcReleaseScene(ebr_scene[1]); - } - return true; - } - if (!dirty && removed_instances.is_empty() && dirty_instances_array.is_empty()) { - return false; + return; } for (const RID &scenario : removed_instances) { @@ -480,7 +485,6 @@ bool RaycastOcclusionCull::Scenario::update() { dirty = false; commit_done = false; commit_thread->start(&Scenario::_commit_scene, this); - return false; } void RaycastOcclusionCull::Scenario::_raycast(uint32_t p_idx, const RaycastThreadData *p_raycast_data) const { @@ -544,13 +548,7 @@ void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_ } Scenario &scenario = scenarios[buffer.scenario_rid]; - - bool removed = scenario.update(); - - if (removed) { - scenarios.erase(buffer.scenario_rid); - return; - } + scenario.update(); buffer.update_camera_rays(p_cam_transform, p_cam_projection, p_cam_orthogonal); @@ -603,19 +601,7 @@ RaycastOcclusionCull::RaycastOcclusionCull() { RaycastOcclusionCull::~RaycastOcclusionCull() { for (KeyValue<RID, Scenario> &K : scenarios) { - Scenario &scenario = K.value; - if (scenario.commit_thread) { - if (scenario.commit_thread->is_started()) { - scenario.commit_thread->wait_to_finish(); - } - memdelete(scenario.commit_thread); - } - - for (int i = 0; i < 2; i++) { - if (scenario.ebr_scene[i]) { - rtcReleaseScene(scenario.ebr_scene[i]); - } - } + K.value.free(); } if (ebr_device != nullptr) { diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h index c4e733b664..ab5eb4eaf0 100644 --- a/modules/raycast/raycast_occlusion_cull.h +++ b/modules/raycast/raycast_occlusion_cull.h @@ -132,7 +132,6 @@ private: Thread *commit_thread = nullptr; bool commit_done = true; bool dirty = false; - bool removed = false; RTCScene ebr_scene[2] = { nullptr, nullptr }; int current_scene_idx = 0; @@ -147,7 +146,8 @@ private: void _transform_vertices_thread(uint32_t p_thread, TransformThreadData *p_data); void _transform_vertices_range(const Vector3 *p_read, Vector3 *p_write, const Transform3D &p_xform, int p_from, int p_to); static void _commit_scene(void *p_ud); - bool update(); + void free(); + void update(); void _raycast(uint32_t p_thread, const RaycastThreadData *p_raycast_data) const; void raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count) const; diff --git a/modules/svg/SCsub b/modules/svg/SCsub index 55b8c4f4a0..a99bc8df60 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -11,39 +11,45 @@ thirdparty_obj = [] thirdparty_dir = "#thirdparty/thorvg/" thirdparty_sources = [ - "src/lib/sw_engine/tvgSwFill.cpp", - "src/lib/sw_engine/tvgSwImage.cpp", - "src/lib/sw_engine/tvgSwMath.cpp", - "src/lib/sw_engine/tvgSwMemPool.cpp", - "src/lib/sw_engine/tvgSwRaster.cpp", - "src/lib/sw_engine/tvgSwRenderer.cpp", - "src/lib/sw_engine/tvgSwRle.cpp", - "src/lib/sw_engine/tvgSwShape.cpp", - "src/lib/sw_engine/tvgSwStroke.cpp", - "src/lib/tvgAccessor.cpp", - "src/lib/tvgCanvas.cpp", - "src/lib/tvgFill.cpp", - "src/lib/tvgGlCanvas.cpp", - "src/lib/tvgInitializer.cpp", - "src/lib/tvgLoader.cpp", - "src/lib/tvgPaint.cpp", - "src/lib/tvgPicture.cpp", - "src/lib/tvgRender.cpp", - "src/lib/tvgSaver.cpp", - "src/lib/tvgScene.cpp", - "src/lib/tvgShape.cpp", - "src/lib/tvgSwCanvas.cpp", - "src/lib/tvgTaskScheduler.cpp", - "src/utils/tvgBezier.cpp", - "src/utils/tvgCompressor.cpp", - "src/utils/tvgStr.cpp", - "src/loaders/raw/tvgRawLoader.cpp", + # common + "src/common/tvgBezier.cpp", + "src/common/tvgCompressor.cpp", + "src/common/tvgMath.cpp", + "src/common/tvgStr.cpp", + # SVG parser "src/loaders/svg/tvgSvgCssStyle.cpp", "src/loaders/svg/tvgSvgLoader.cpp", "src/loaders/svg/tvgSvgPath.cpp", "src/loaders/svg/tvgSvgSceneBuilder.cpp", "src/loaders/svg/tvgSvgUtil.cpp", "src/loaders/svg/tvgXmlParser.cpp", + "src/loaders/raw/tvgRawLoader.cpp", + # renderer common + "src/renderer/tvgAccessor.cpp", + # "src/renderer/tvgAnimation.cpp", + "src/renderer/tvgCanvas.cpp", + "src/renderer/tvgFill.cpp", + # "src/renderer/tvgGlCanvas.cpp", + "src/renderer/tvgInitializer.cpp", + "src/renderer/tvgLoader.cpp", + "src/renderer/tvgPaint.cpp", + "src/renderer/tvgPicture.cpp", + "src/renderer/tvgRender.cpp", + # "src/renderer/tvgSaver.cpp", + "src/renderer/tvgScene.cpp", + "src/renderer/tvgShape.cpp", + "src/renderer/tvgSwCanvas.cpp", + "src/renderer/tvgTaskScheduler.cpp", + # renderer sw_engine + "src/renderer/sw_engine/tvgSwFill.cpp", + "src/renderer/sw_engine/tvgSwImage.cpp", + "src/renderer/sw_engine/tvgSwMath.cpp", + "src/renderer/sw_engine/tvgSwMemPool.cpp", + "src/renderer/sw_engine/tvgSwRaster.cpp", + "src/renderer/sw_engine/tvgSwRenderer.cpp", + "src/renderer/sw_engine/tvgSwRle.cpp", + "src/renderer/sw_engine/tvgSwShape.cpp", + "src/renderer/sw_engine/tvgSwStroke.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] @@ -57,16 +63,13 @@ env_thirdparty = env_svg.Clone() env_thirdparty.disable_warnings() env_thirdparty.Prepend( CPPPATH=[ - thirdparty_dir + "src/lib", - thirdparty_dir + "src/lib/sw_engine", - thirdparty_dir + "src/loaders/raw", + thirdparty_dir + "src/common", thirdparty_dir + "src/loaders/svg", - thirdparty_dir + "src/utils", + thirdparty_dir + "src/renderer", + thirdparty_dir + "src/renderer/sw_engine", + thirdparty_dir + "src/loaders/raw", ] ) -# Also requires libpng headers -if env["builtin_libpng"]: - env_thirdparty.Prepend(CPPPATH=["#thirdparty/libpng"]) env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) env.modules_sources += thirdparty_obj diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 1acff68135..3c468e61d7 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -40,7 +40,11 @@ msdfgen_enabled = "msdfgen" in env.module_list if "svg" in env.module_list: env_text_server_adv.Prepend( - CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib", "#thirdparty/thorvg/src/utils"] + CPPPATH=[ + "#thirdparty/thorvg/inc", + "#thirdparty/thorvg/src/common", + "#thirdparty/thorvg/src/renderer", + ] ) # Enable ThorVG static object linking. env_text_server_adv.Append(CPPDEFINES=["TVG_STATIC"]) diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index bf29ad3016..b95c35f80d 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -42,51 +42,56 @@ if env["thorvg_enabled"] and env["freetype_enabled"]: thirdparty_tvg_dir = "../../../thirdparty/thorvg/" thirdparty_tvg_sources = [ - "src/lib/sw_engine/tvgSwFill.cpp", - "src/lib/sw_engine/tvgSwImage.cpp", - "src/lib/sw_engine/tvgSwMath.cpp", - "src/lib/sw_engine/tvgSwMemPool.cpp", - "src/lib/sw_engine/tvgSwRaster.cpp", - "src/lib/sw_engine/tvgSwRenderer.cpp", - "src/lib/sw_engine/tvgSwRle.cpp", - "src/lib/sw_engine/tvgSwShape.cpp", - "src/lib/sw_engine/tvgSwStroke.cpp", - "src/lib/tvgAccessor.cpp", - "src/lib/tvgCanvas.cpp", - "src/lib/tvgFill.cpp", - "src/lib/tvgGlCanvas.cpp", - "src/lib/tvgInitializer.cpp", - "src/lib/tvgLoader.cpp", - "src/lib/tvgPaint.cpp", - "src/lib/tvgPicture.cpp", - "src/lib/tvgRender.cpp", - "src/lib/tvgSaver.cpp", - "src/lib/tvgScene.cpp", - "src/lib/tvgShape.cpp", - "src/lib/tvgSwCanvas.cpp", - "src/lib/tvgTaskScheduler.cpp", - "src/utils/tvgBezier.cpp", - "src/utils/tvgCompressor.cpp", - "src/utils/tvgStr.cpp", - "src/loaders/raw/tvgRawLoader.cpp", + # common + "src/common/tvgBezier.cpp", + "src/common/tvgCompressor.cpp", + "src/common/tvgMath.cpp", + "src/common/tvgStr.cpp", + # SVG parser "src/loaders/svg/tvgSvgCssStyle.cpp", "src/loaders/svg/tvgSvgLoader.cpp", "src/loaders/svg/tvgSvgPath.cpp", "src/loaders/svg/tvgSvgSceneBuilder.cpp", "src/loaders/svg/tvgSvgUtil.cpp", "src/loaders/svg/tvgXmlParser.cpp", + "src/loaders/raw/tvgRawLoader.cpp", + # renderer common + "src/renderer/tvgAccessor.cpp", + # "src/renderer/tvgAnimation.cpp", + "src/renderer/tvgCanvas.cpp", + "src/renderer/tvgFill.cpp", + # "src/renderer/tvgGlCanvas.cpp", + "src/renderer/tvgInitializer.cpp", + "src/renderer/tvgLoader.cpp", + "src/renderer/tvgPaint.cpp", + "src/renderer/tvgPicture.cpp", + "src/renderer/tvgRender.cpp", + # "src/renderer/tvgSaver.cpp", + "src/renderer/tvgScene.cpp", + "src/renderer/tvgShape.cpp", + "src/renderer/tvgSwCanvas.cpp", + "src/renderer/tvgTaskScheduler.cpp", + # renderer sw_engine + "src/renderer/sw_engine/tvgSwFill.cpp", + "src/renderer/sw_engine/tvgSwImage.cpp", + "src/renderer/sw_engine/tvgSwMath.cpp", + "src/renderer/sw_engine/tvgSwMemPool.cpp", + "src/renderer/sw_engine/tvgSwRaster.cpp", + "src/renderer/sw_engine/tvgSwRenderer.cpp", + "src/renderer/sw_engine/tvgSwRle.cpp", + "src/renderer/sw_engine/tvgSwShape.cpp", + "src/renderer/sw_engine/tvgSwStroke.cpp", ] thirdparty_tvg_sources = [thirdparty_tvg_dir + file for file in thirdparty_tvg_sources] env_tvg.Append( CPPPATH=[ "../../../thirdparty/thorvg/inc", - "../../../thirdparty/thorvg/src/lib", - "../../../thirdparty/thorvg/src/lib/sw_engine", - "../../../thirdparty/thorvg/src/loaders/raw", + "../../../thirdparty/thorvg/src/common", "../../../thirdparty/thorvg/src/loaders/svg", - "../../../thirdparty/thorvg/src/utils", - "../../../thirdparty/libpng", + "../../../thirdparty/thorvg/src/loaders/raw", + "../../../thirdparty/thorvg/src/renderer", + "../../../thirdparty/thorvg/src/renderer/sw_engine", ] ) @@ -96,8 +101,8 @@ if env["thorvg_enabled"] and env["freetype_enabled"]: env.Append( CPPPATH=[ "../../../thirdparty/thorvg/inc", - "../../../thirdparty/thorvg/src/lib", - "../../../thirdparty/thorvg/src/utils", + "../../../thirdparty/thorvg/src/common", + "../../../thirdparty/thorvg/src/renderer", ] ) env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"]) diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub index 8705bc430d..e808864512 100644 --- a/modules/text_server_fb/SCsub +++ b/modules/text_server_fb/SCsub @@ -10,7 +10,7 @@ env_text_server_fb = env_modules.Clone() if "svg" in env.module_list: env_text_server_fb.Prepend( - CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib", "#thirdparty/thorvg/src/utils"] + CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/common", "#thirdparty/thorvg/src/renderer"] ) # Enable ThorVG static object linking. env_text_server_fb.Append(CPPDEFINES=["TVG_STATIC"]) diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index 40bb2dc1b9..846ac02cf1 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -37,51 +37,56 @@ if env["thorvg_enabled"] and env["freetype_enabled"]: thirdparty_tvg_dir = "../../../thirdparty/thorvg/" thirdparty_tvg_sources = [ - "src/lib/sw_engine/tvgSwFill.cpp", - "src/lib/sw_engine/tvgSwImage.cpp", - "src/lib/sw_engine/tvgSwMath.cpp", - "src/lib/sw_engine/tvgSwMemPool.cpp", - "src/lib/sw_engine/tvgSwRaster.cpp", - "src/lib/sw_engine/tvgSwRenderer.cpp", - "src/lib/sw_engine/tvgSwRle.cpp", - "src/lib/sw_engine/tvgSwShape.cpp", - "src/lib/sw_engine/tvgSwStroke.cpp", - "src/lib/tvgAccessor.cpp", - "src/lib/tvgCanvas.cpp", - "src/lib/tvgFill.cpp", - "src/lib/tvgGlCanvas.cpp", - "src/lib/tvgInitializer.cpp", - "src/lib/tvgLoader.cpp", - "src/lib/tvgPaint.cpp", - "src/lib/tvgPicture.cpp", - "src/lib/tvgRender.cpp", - "src/lib/tvgSaver.cpp", - "src/lib/tvgScene.cpp", - "src/lib/tvgShape.cpp", - "src/lib/tvgSwCanvas.cpp", - "src/lib/tvgTaskScheduler.cpp", - "src/utils/tvgBezier.cpp", - "src/utils/tvgCompressor.cpp", - "src/utils/tvgStr.cpp", - "src/loaders/raw/tvgRawLoader.cpp", + # common + "src/common/tvgBezier.cpp", + "src/common/tvgCompressor.cpp", + "src/common/tvgMath.cpp", + "src/common/tvgStr.cpp", + # SVG parser "src/loaders/svg/tvgSvgCssStyle.cpp", "src/loaders/svg/tvgSvgLoader.cpp", "src/loaders/svg/tvgSvgPath.cpp", "src/loaders/svg/tvgSvgSceneBuilder.cpp", "src/loaders/svg/tvgSvgUtil.cpp", "src/loaders/svg/tvgXmlParser.cpp", + "src/loaders/raw/tvgRawLoader.cpp", + # renderer common + "src/renderer/tvgAccessor.cpp", + # "src/renderer/tvgAnimation.cpp", + "src/renderer/tvgCanvas.cpp", + "src/renderer/tvgFill.cpp", + # "src/renderer/tvgGlCanvas.cpp", + "src/renderer/tvgInitializer.cpp", + "src/renderer/tvgLoader.cpp", + "src/renderer/tvgPaint.cpp", + "src/renderer/tvgPicture.cpp", + "src/renderer/tvgRender.cpp", + # "src/renderer/tvgSaver.cpp", + "src/renderer/tvgScene.cpp", + "src/renderer/tvgShape.cpp", + "src/renderer/tvgSwCanvas.cpp", + "src/renderer/tvgTaskScheduler.cpp", + # renderer sw_engine + "src/renderer/sw_engine/tvgSwFill.cpp", + "src/renderer/sw_engine/tvgSwImage.cpp", + "src/renderer/sw_engine/tvgSwMath.cpp", + "src/renderer/sw_engine/tvgSwMemPool.cpp", + "src/renderer/sw_engine/tvgSwRaster.cpp", + "src/renderer/sw_engine/tvgSwRenderer.cpp", + "src/renderer/sw_engine/tvgSwRle.cpp", + "src/renderer/sw_engine/tvgSwShape.cpp", + "src/renderer/sw_engine/tvgSwStroke.cpp", ] thirdparty_tvg_sources = [thirdparty_tvg_dir + file for file in thirdparty_tvg_sources] env_tvg.Append( CPPPATH=[ "../../../thirdparty/thorvg/inc", - "../../../thirdparty/thorvg/src/lib", - "../../../thirdparty/thorvg/src/lib/sw_engine", - "../../../thirdparty/thorvg/src/loaders/raw", + "../../../thirdparty/thorvg/src/common", "../../../thirdparty/thorvg/src/loaders/svg", - "../../../thirdparty/thorvg/src/utils", - "../../../thirdparty/libpng", + "../../../thirdparty/thorvg/src/loaders/raw", + "../../../thirdparty/thorvg/src/renderer", + "../../../thirdparty/thorvg/src/renderer/sw_engine", ] ) @@ -91,8 +96,8 @@ if env["thorvg_enabled"] and env["freetype_enabled"]: env.Append( CPPPATH=[ "../../../thirdparty/thorvg/inc", - "../../../thirdparty/thorvg/src/lib", - "../../../thirdparty/thorvg/src/utils", + "../../../thirdparty/thorvg/src/common", + "../../../thirdparty/thorvg/src/renderer", ] ) env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"]) |