diff options
69 files changed, 4187 insertions, 506 deletions
diff --git a/core/SCsub b/core/SCsub index e3ba46be02..d9167b8f83 100644 --- a/core/SCsub +++ b/core/SCsub @@ -186,6 +186,7 @@ SConscript("io/SCsub") SConscript("debugger/SCsub") SConscript("input/SCsub") SConscript("variant/SCsub") +SConscript("extension/SCsub") SConscript("object/SCsub") SConscript("templates/SCsub") SConscript("string/SCsub") diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 13cfd51570..ad31966a65 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -236,9 +236,10 @@ Engine::Engine() { singleton = this; } -Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr) : +Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr, const StringName &p_class_name) : name(p_name), - ptr(p_ptr) { + ptr(p_ptr), + class_name(p_class_name) { #ifdef DEBUG_ENABLED RefCounted *rc = Object::cast_to<RefCounted>(p_ptr); if (rc && !rc->is_referenced()) { diff --git a/core/config/engine.h b/core/config/engine.h index ecf07952ab..970cfb03e8 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -41,7 +41,8 @@ public: struct Singleton { StringName name; Object *ptr; - Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr); + StringName class_name; //used for binding generation hinting + Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr, const StringName &p_class_name = StringName()); }; private: diff --git a/core/extension/SCsub b/core/extension/SCsub new file mode 100644 index 0000000000..a3a54250c1 --- /dev/null +++ b/core/extension/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +env_extension = env.Clone() + +env_extension.add_source_files(env.core_sources, "*.cpp") diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp new file mode 100644 index 0000000000..3c132a619d --- /dev/null +++ b/core/extension/extension_api_dump.cpp @@ -0,0 +1,805 @@ +/*************************************************************************/ +/* extension_api_dump.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "extension_api_dump.h" +#include "core/config/engine.h" +#include "core/core_constants.h" +#include "core/io/file_access.h" +#include "core/io/json.h" +#include "core/templates/pair.h" +#include "core/version.h" + +#ifdef TOOLS_ENABLED + +Dictionary NativeExtensionAPIDump::generate_extension_api() { + Dictionary api_dump; + + { + //header + Dictionary header; + header["version_major"] = VERSION_MAJOR; + header["version_minor"] = VERSION_MINOR; +#if VERSION_PATCH + header["version_patch"] = VERSION_PATCH; +#else + header["version_patch"] = 0; +#endif + header["version_status"] = VERSION_STATUS; + header["version_build"] = VERSION_BUILD; + header["version_full_name"] = VERSION_FULL_NAME; + + api_dump["header"] = header; + } + + const uint32_t vec3_elems = 3; + const uint32_t ptrsize_32 = 4; + const uint32_t ptrsize_64 = 4; + static const char *build_config_name[4] = { "float_32", "float_64", "double_32", "double_64" }; + + { + //type sizes + struct { + Variant::Type type; + uint32_t size_32_bits_real_float; + uint32_t size_64_bits_real_float; + uint32_t size_32_bits_real_double; + uint32_t size_64_bits_real_double; + } type_size_array[Variant::VARIANT_MAX + 1] = { + { Variant::NIL, 0, 0, 0, 0 }, + { Variant::BOOL, sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t) }, + { Variant::INT, sizeof(int64_t), sizeof(int64_t), sizeof(int64_t), sizeof(int64_t) }, + { Variant::FLOAT, sizeof(double), sizeof(double), sizeof(double), sizeof(double) }, + { Variant::STRING, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::VECTOR2, 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::VECTOR2I, 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, + { Variant::RECT2, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, + { Variant::RECT2I, 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t) }, + { Variant::VECTOR3, vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, + { Variant::VECTOR3I, 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t) }, + { Variant::TRANSFORM2D, 6 * sizeof(float), 6 * sizeof(float), 6 * sizeof(double), 6 * sizeof(double) }, + { Variant::PLANE, (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(double), (vec3_elems + 1) * sizeof(double) }, + { Variant::QUATERNION, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, + { Variant::AABB, (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(double), (vec3_elems * 2) * sizeof(double) }, + { Variant::BASIS, (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) }, + { Variant::TRANSFORM3D, (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(double), (vec3_elems * 4) * sizeof(double) }, + { Variant::COLOR, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float) }, + { Variant::STRING_NAME, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::NODE_PATH, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::RID, sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t) }, + { Variant::OBJECT, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::CALLABLE, sizeof(Callable), sizeof(Callable), sizeof(Callable), sizeof(Callable) }, //harcoded align + { Variant::SIGNAL, sizeof(Signal), sizeof(Signal), sizeof(Signal), sizeof(Signal) }, //harcoded align + { Variant::DICTIONARY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_BYTE_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_INT32_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_INT64_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_FLOAT32_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_FLOAT64_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_STRING_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_VECTOR2_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_VECTOR3_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_COLOR_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::VARIANT_MAX, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(double) * 4, sizeof(uint64_t) + sizeof(double) * 4 }, + }; + + Array core_type_sizes; + + for (int i = 0; i < 4; i++) { + Dictionary d; + d["build_configuration"] = build_config_name[i]; + Array sizes; + for (int j = 0; j < Variant::VARIANT_MAX; j++) { + Variant::Type t = type_size_array[j].type; + String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t); + Dictionary d2; + d2["name"] = name; + uint32_t size; + switch (i) { + case 0: + size = type_size_array[j].size_32_bits_real_float; + break; + case 1: + size = type_size_array[j].size_64_bits_real_float; + break; + case 2: + size = type_size_array[j].size_32_bits_real_double; + break; + case 3: + size = type_size_array[j].size_64_bits_real_double; + break; + } + d2["size"] = size; + sizes.push_back(d2); + } + d["sizes"] = sizes; + core_type_sizes.push_back(d); + } + api_dump["builtin_class_sizes"] = core_type_sizes; + } + + { + //member offsets sizes + struct { + Variant::Type type; + const char *member; + uint32_t offset_32_bits_real_float; + uint32_t offset_64_bits_real_float; + uint32_t offset_32_bits_real_double; + uint32_t offset_64_bits_real_double; + } member_offset_array[] = { + { Variant::VECTOR2, "x", 0, 0, 0, 0 }, + { Variant::VECTOR2, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, + { Variant::VECTOR2I, "x", 0, 0, 0, 0 }, + { Variant::VECTOR2I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) }, + { Variant::RECT2, "position", 0, 0, 0, 0 }, + { Variant::RECT2, "size", 2 * sizeof(Vector2), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::RECT2I, "position", 0, 0, 0, 0 }, + { Variant::RECT2I, "size", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, + { Variant::VECTOR3, "x", 0, 0, 0, 0 }, + { Variant::VECTOR3, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, + { Variant::VECTOR3, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::VECTOR3I, "x", 0, 0, 0, 0 }, + { Variant::VECTOR3I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) }, + { Variant::VECTOR3I, "z", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, + { Variant::TRANSFORM2D, "x", 0, 0, 0, 0 }, + { Variant::TRANSFORM2D, "y", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::TRANSFORM2D, "origin", 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, + { Variant::PLANE, "normal", 0, 0, 0, 0 }, + { Variant::PLANE, "d", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, + { Variant::QUATERNION, "x", 0, 0, 0, 0 }, + { Variant::QUATERNION, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, + { Variant::QUATERNION, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::QUATERNION, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(double), 3 * sizeof(double) }, + { Variant::AABB, "position", 0, 0, 0, 0 }, + { Variant::AABB, "size", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, + //rememer that basis vectors are flipped! + { Variant::BASIS, "x", 0, 0, 0, 0 }, + { Variant::BASIS, "y", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, + { Variant::BASIS, "z", vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(double), vec3_elems * 2 * sizeof(double) }, + { Variant::TRANSFORM3D, "basis", 0, 0, 0, 0 }, + { Variant::TRANSFORM3D, "origin", (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) }, + { Variant::COLOR, "x", 0, 0, 0, 0 }, + { Variant::COLOR, "y", sizeof(float), sizeof(float), sizeof(float), sizeof(float) }, + { Variant::COLOR, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(float) }, + { Variant::COLOR, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(float) }, + { Variant::NIL, nullptr, 0, 0, 0, 0 }, + }; + + Array core_type_member_offsets; + + for (int i = 0; i < 4; i++) { + Dictionary d; + d["build_configuration"] = build_config_name[i]; + Array type_offsets; + uint32_t idx = 0; + + Variant::Type last_type = Variant::NIL; + + Dictionary d2; + Array members; + + while (true) { + Variant::Type t = member_offset_array[idx].type; + if (t != last_type) { + if (last_type != Variant::NIL) { + d2["members"] = members; + type_offsets.push_back(d2); + } + if (t == Variant::NIL) { + break; + } + + String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t); + d2 = Dictionary(); + members = Array(); + d2["name"] = name; + last_type = t; + } + Dictionary d3; + uint32_t offset; + switch (i) { + case 0: + offset = member_offset_array[idx].offset_32_bits_real_float; + break; + case 1: + offset = member_offset_array[idx].offset_64_bits_real_float; + break; + case 2: + offset = member_offset_array[idx].offset_32_bits_real_double; + break; + case 3: + offset = member_offset_array[idx].offset_64_bits_real_double; + break; + } + d3["member"] = member_offset_array[idx].member; + d3["offset"] = offset; + members.push_back(d3); + idx++; + } + d["classes"] = type_offsets; + core_type_member_offsets.push_back(d); + } + api_dump["builtin_class_member_offsets"] = core_type_member_offsets; + } + + { + // global enums and constants + Array constants; + Map<String, List<Pair<String, int>>> enum_list; + + for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) { + int value = CoreConstants::get_global_constant_value(i); + String enum_name = CoreConstants::get_global_constant_enum(i); + String name = CoreConstants::get_global_constant_name(i); + if (enum_name != String()) { + enum_list[enum_name].push_back(Pair<String, int>(name, value)); + } else { + Dictionary d; + d["name"] = name; + d["value"] = value; + constants.push_back(d); + } + } + + api_dump["global_constants"] = constants; + + Array enums; + for (Map<String, List<Pair<String, int>>>::Element *E = enum_list.front(); E; E = E->next()) { + Dictionary d1; + d1["name"] = E->key(); + Array values; + for (List<Pair<String, int>>::Element *F = E->get().front(); F; F = F->next()) { + Dictionary d2; + d2["name"] = F->get().first; + d2["value"] = F->get().second; + values.push_back(d2); + } + d1["values"] = values; + enums.push_back(d1); + } + + api_dump["global_enums"] = enums; + } + { + Array utility_funcs; + + List<StringName> utility_func_names; + Variant::get_utility_function_list(&utility_func_names); + + for (List<StringName>::Element *E = utility_func_names.front(); E; E = E->next()) { + StringName name = E->get(); + Dictionary func; + func["name"] = String(name); + if (Variant::has_utility_function_return_value(name)) { + Variant::Type rt = Variant::get_utility_function_return_type(name); + func["return_type"] = rt == Variant::NIL ? String("Variant") : Variant::get_type_name(rt); + } + switch (Variant::get_utility_function_type(name)) { + case Variant::UTILITY_FUNC_TYPE_MATH: + func["category"] = "math"; + break; + case Variant::UTILITY_FUNC_TYPE_RANDOM: + func["category"] = "random"; + break; + case Variant::UTILITY_FUNC_TYPE_GENERAL: + func["category"] = "general"; + break; + } + bool vararg = Variant::is_utility_function_vararg(name); + func["is_vararg"] = Variant::is_utility_function_vararg(name); + func["hash"] = Variant::get_utility_function_hash(name); + Array arguments; + int argcount = Variant::get_utility_function_argument_count(name); + for (int i = 0; i < argcount; i++) { + Dictionary arg; + String argname = vararg ? "arg" + itos(i + 1) : Variant::get_utility_function_argument_name(name, i); + arg["name"] = argname; + Variant::Type argtype = Variant::get_utility_function_argument_type(name, i); + arg["type"] = argtype == Variant::NIL ? String("Variant") : Variant::get_type_name(argtype); + //no default value support in utility functions + arguments.push_back(arg); + } + + if (arguments.size()) { + func["arguments"] = arguments; + } + + utility_funcs.push_back(func); + } + + api_dump["utility_functions"] = utility_funcs; + } + + { + // builtin types + + Array builtins; + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i == Variant::OBJECT) { + continue; + } + + Variant::Type type = Variant::Type(i); + + Dictionary d; + d["name"] = Variant::get_type_name(type); + if (Variant::has_indexing(type)) { + Variant::Type index_type = Variant::get_indexed_element_type(type); + d["indexing_return_type"] = index_type == Variant::NIL ? String("Variant") : Variant::get_type_name(index_type); + } + + { + //members + Array members; + + List<StringName> member_names; + Variant::get_member_list(type, &member_names); + for (List<StringName>::Element *E = member_names.front(); E; E = E->next()) { + StringName member_name = E->get(); + Dictionary d2; + d2["name"] = String(member_name); + d2["type"] = Variant::get_type_name(Variant::get_member_type(type, member_name)); + members.push_back(d2); + } + if (members.size()) { + d["members"] = members; + } + } + { + //constants + Array constants; + + List<StringName> constant_names; + Variant::get_constants_for_type(type, &constant_names); + for (List<StringName>::Element *E = constant_names.front(); E; E = E->next()) { + StringName constant_name = E->get(); + Dictionary d2; + d2["name"] = String(constant_name); + Variant constant = Variant::get_constant_value(type, constant_name); + d2["type"] = Variant::get_type_name(constant.get_type()); + d2["value"] = constant.get_construct_string(); + constants.push_back(d2); + } + if (constants.size()) { + d["constants"] = constants; + } + } + { + //operators + Array operators; + + for (int j = 0; j < Variant::VARIANT_MAX; j++) { + for (int k = 0; k < Variant::OP_MAX; k++) { + Variant::Type rt = Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j)); + if (rt != Variant::NIL) { + Dictionary d2; + d2["name"] = Variant::get_operator_name(Variant::Operator(k)); + if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) { + d2["right_type"] = Variant::get_type_name(Variant::Type(j)); + } + operators.push_back(d2); + } + } + } + if (operators.size()) { + d["operators"] = operators; + } + } + { + //methods + Array methods; + + List<StringName> method_names; + Variant::get_builtin_method_list(type, &method_names); + for (List<StringName>::Element *E = method_names.front(); E; E = E->next()) { + StringName method_name = E->get(); + Dictionary d2; + d2["name"] = String(method_name); + if (Variant::has_builtin_method_return_value(type, method_name)) { + Variant::Type ret_type = Variant::get_builtin_method_return_type(type, method_name); + d2["return_type"] = ret_type == Variant::NIL ? String("Variant") : Variant::get_type_name(ret_type); + } + d2["is_vararg"] = Variant::is_builtin_method_vararg(type, method_name); + d2["is_const"] = Variant::is_builtin_method_const(type, method_name); + d2["is_static"] = Variant::is_builtin_method_static(type, method_name); + d2["hash"] = Variant::get_builtin_method_hash(type, method_name); + + Vector<Variant> default_args = Variant::get_builtin_method_default_arguments(type, method_name); + + Array arguments; + int argcount = Variant::get_builtin_method_argument_count(type, method_name); + for (int j = 0; j < argcount; j++) { + Dictionary d3; + d3["name"] = Variant::get_builtin_method_argument_name(type, method_name, j); + Variant::Type argtype = Variant::get_builtin_method_argument_type(type, method_name, j); + d3["type"] = argtype == Variant::NIL ? String("Variant") : Variant::get_type_name(argtype); + + if (j >= (argcount - default_args.size())) { + int dargidx = j - (argcount - default_args.size()); + d3["default_value"] = default_args[dargidx].get_construct_string(); + } + arguments.push_back(d3); + } + + if (arguments.size()) { + d2["arguments"] = arguments; + } + + methods.push_back(d2); + } + if (methods.size()) { + d["methods"] = methods; + } + } + { + //constructors + Array constructors; + + for (int j = 0; j < Variant::get_constructor_count(type); j++) { + Dictionary d2; + d2["index"] = j; + + Array arguments; + int argcount = Variant::get_constructor_argument_count(type, j); + for (int k = 0; k < argcount; k++) { + Dictionary d3; + d3["name"] = Variant::get_constructor_argument_name(type, j, k); + d3["type"] = Variant::get_type_name(Variant::get_constructor_argument_type(type, j, k)); + arguments.push_back(d3); + } + if (arguments.size()) { + d2["arguments"] = arguments; + } + constructors.push_back(d2); + } + + if (constructors.size()) { + d["constructors"] = constructors; + } + } + + builtins.push_back(d); + } + + api_dump["builtin_classes"] = builtins; + } + + { + // classes + Array classes; + + List<StringName> class_list; + + ClassDB::get_class_list(&class_list); + + class_list.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = class_list.front(); E; E = E->next()) { + Dictionary d; + StringName class_name = E->get(); + d["name"] = String(class_name); + d["is_refcounted"] = ClassDB::is_parent_class(class_name, "RefCounted"); + d["is_instantiable"] = ClassDB::can_instantiate(class_name); + StringName parent_class = ClassDB::get_parent_class(class_name); + if (parent_class != StringName()) { + d["inherits"] = String(parent_class); + } + + { + ClassDB::APIType api = ClassDB::get_api_type(class_name); + static const char *api_type[5] = { "core", "editor", "extension", "editor_extension" }; + d["api_type"] = api_type[api]; + } + + { + //constants + Array constants; + List<String> constant_list; + ClassDB::get_integer_constant_list(class_name, &constant_list, true); + for (List<String>::Element *F = constant_list.front(); F; F = F->next()) { + StringName enum_name = ClassDB::get_integer_constant_enum(class_name, F->get()); + if (enum_name != StringName()) { + continue; //enums will be handled on their own + } + + Dictionary d2; + d2["name"] = String(F->get()); + d2["value"] = ClassDB::get_integer_constant(class_name, F->get()); + + constants.push_back(d2); + } + + if (constants.size()) { + d["constants"] = constants; + } + } + { + //enum + Array enums; + List<StringName> enum_list; + ClassDB::get_enum_list(class_name, &enum_list, true); + for (List<StringName>::Element *F = enum_list.front(); F; F = F->next()) { + Dictionary d2; + d2["name"] = String(F->get()); + + Array values; + List<StringName> enum_constant_list; + ClassDB::get_enum_constants(class_name, F->get(), &enum_constant_list, true); + for (List<StringName>::Element *G = enum_constant_list.front(); G; G = G->next()) { + Dictionary d3; + d3["name"] = String(G->get()); + d3["value"] = ClassDB::get_integer_constant(class_name, G->get()); + values.push_back(d3); + } + + d2["values"] = values; + + enums.push_back(d2); + } + + if (enums.size()) { + d["enums"] = enums; + } + } + { + //methods + Array methods; + List<MethodInfo> method_list; + ClassDB::get_method_list(class_name, &method_list, true); + for (List<MethodInfo>::Element *F = method_list.front(); F; F = F->next()) { + StringName method_name = F->get().name; + if (F->get().flags & METHOD_FLAG_VIRTUAL) { + //virtual method + const MethodInfo &mi = F->get(); + Dictionary d2; + d2["name"] = String(method_name); + d2["is_const"] = (F->get().flags & METHOD_FLAG_CONST) ? true : false; + d2["is_vararg"] = false; + d2["is_virtual"] = true; + // virtual functions have no hash since no MethodBind is involved + bool has_return = mi.return_val.type != Variant::NIL || (mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT); + Array arguments; + for (int i = (has_return ? -1 : 0); i < mi.arguments.size(); i++) { + PropertyInfo pinfo = i == -1 ? mi.return_val : mi.arguments[i]; + Dictionary d3; + + if (i >= 0) { + d3["name"] = pinfo.name; + } + if (pinfo.class_name != StringName()) { + d3["type"] = String(pinfo.class_name); + } else { + Variant::Type type = pinfo.type; + if (type == Variant::NIL) { + d3["type"] = "Variant"; + } else { + d3["type"] = Variant::get_type_name(type); + } + } + + if (i == -1) { + d2["return_value"] = d3; + } else { + arguments.push_back(d3); + } + } + + if (arguments.size()) { + d2["arguments"] = arguments; + } + + methods.push_back(d2); + + } else if (F->get().name.begins_with("_")) { + //hidden method, ignore + + } else { + Dictionary d2; + d2["name"] = String(method_name); + + MethodBind *method = ClassDB::get_method(class_name, method_name); + if (!method) { + continue; + } + + d2["is_const"] = method->is_const(); + d2["is_vararg"] = method->is_vararg(); + d2["is_virtual"] = false; + d2["hash"] = method->get_hash(); + + Vector<Variant> default_args = method->get_default_arguments(); + + Array arguments; + for (int i = (method->has_return() ? -1 : 0); i < method->get_argument_count(); i++) { + PropertyInfo pinfo = i == -1 ? method->get_return_info() : method->get_argument_info(i); + Dictionary d3; + + if (i >= 0) { + d3["name"] = pinfo.name; + } + if (pinfo.class_name != StringName()) { + d3["type"] = String(pinfo.class_name); + } else { + Variant::Type type = pinfo.type; + if (type == Variant::NIL) { + d3["type"] = "Variant"; + } else { + d3["type"] = Variant::get_type_name(type); + } + } + + if (method->get_argument_meta(i) > 0) { + static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" }; + d3["meta"] = argmeta[method->get_argument_meta(i)]; + } + + if (i >= 0 && i >= (method->get_argument_count() - default_args.size())) { + int dargidx = i - (method->get_argument_count() - default_args.size()); + d3["default_value"] = default_args[dargidx].get_construct_string(); + } + + if (i == -1) { + d2["return_value"] = d3; + } else { + arguments.push_back(d3); + } + } + + if (arguments.size()) { + d2["arguments"] = arguments; + } + + methods.push_back(d2); + } + } + + if (methods.size()) { + d["methods"] = methods; + } + } + + { + //signals + Array signals; + List<MethodInfo> signal_list; + ClassDB::get_signal_list(class_name, &signal_list, true); + for (List<MethodInfo>::Element *F = signal_list.front(); F; F = F->next()) { + StringName signal_name = F->get().name; + Dictionary d2; + d2["name"] = String(signal_name); + + Array arguments; + + for (int i = 0; i < F->get().arguments.size(); i++) { + Dictionary d3; + d3["name"] = F->get().arguments[i].name; + Variant::Type type = F->get().arguments[i].type; + if (F->get().arguments[i].class_name != StringName()) { + d3["type"] = String(F->get().arguments[i].class_name); + } else if (type == Variant::NIL) { + d3["type"] = "Variant"; + } else { + d3["type"] = Variant::get_type_name(type); + } + arguments.push_back(d3); + } + if (arguments.size()) { + d2["arguments"] = arguments; + } + + signals.push_back(d2); + } + + if (signals.size()) { + d["signals"] = signals; + } + } + { + //properties + Array properties; + List<PropertyInfo> property_list; + ClassDB::get_property_list(class_name, &property_list, true); + for (List<PropertyInfo>::Element *F = property_list.front(); F; F = F->next()) { + if (F->get().usage & PROPERTY_USAGE_CATEGORY || F->get().usage & PROPERTY_USAGE_GROUP || F->get().usage & PROPERTY_USAGE_SUBGROUP) { + continue; //not real properties + } + if (F->get().name.begins_with("_")) { + continue; //hidden property + } + StringName property_name = F->get().name; + Dictionary d2; + d2["name"] = String(property_name); + + if (F->get().class_name != StringName()) { + d2["type"] = String(F->get().class_name); + } else if (F->get().type == Variant::NIL && F->get().usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + d2["type"] = "Variant"; + } else { + d2["type"] = Variant::get_type_name(F->get().type); + } + + d2["setter"] = ClassDB::get_property_setter(class_name, F->get().name); + d2["getter"] = ClassDB::get_property_getter(class_name, F->get().name); + d2["index"] = ClassDB::get_property_index(class_name, F->get().name); + properties.push_back(d2); + } + + if (properties.size()) { + d["properties"] = properties; + } + } + + classes.push_back(d); + } + + api_dump["classes"] = classes; + } + + { + // singletons + + Array singletons; + List<Engine::Singleton> singleton_list; + Engine::get_singleton()->get_singletons(&singleton_list); + + for (List<Engine::Singleton>::Element *E = singleton_list.front(); E; E = E->next()) { + const Engine::Singleton &s = E->get(); + Dictionary d; + d["name"] = s.name; + if (s.class_name != StringName()) { + d["type"] = String(s.class_name); + } else { + d["type"] = String(s.ptr->get_class()); + } + singletons.push_back(d); + } + + if (singletons.size()) { + api_dump["singletons"] = singletons; + } + } + + return api_dump; +} + +void NativeExtensionAPIDump::generate_extension_json_file(const String &p_path) { + Dictionary api = generate_extension_api(); + Ref<JSON> json; + json.instantiate(); + + String text = json->stringify(api, "\t", false); + FileAccessRef fa = FileAccess::open(p_path, FileAccess::WRITE); + CharString cs = text.ascii(); + fa->store_buffer((const uint8_t *)cs.ptr(), cs.length()); + fa->close(); +} +#endif diff --git a/core/extension/extension_api_dump.h b/core/extension/extension_api_dump.h new file mode 100644 index 0000000000..a7825c10a9 --- /dev/null +++ b/core/extension/extension_api_dump.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* extension_api_dump.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 API_DUMP_H +#define API_DUMP_H + +#include "core/extension/native_extension.h" + +#ifdef TOOLS_ENABLED + +class NativeExtensionAPIDump { +public: + static Dictionary generate_extension_api(); + static void generate_extension_json_file(const String &p_path); +}; +#endif + +#endif // API_DUMP_H diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp new file mode 100644 index 0000000000..324933d7b7 --- /dev/null +++ b/core/extension/gdnative_interface.cpp @@ -0,0 +1,688 @@ +/*************************************************************************/ +/* gdnative_interface.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "gdnative_interface.h" + +#include "core/config/engine.h" +#include "core/object/class_db.h" +#include "core/os/memory.h" +#include "core/variant/variant.h" +#include "core/version.h" + +// Memory Functions +static void *gdnative_alloc(size_t p_size) { + return memalloc(p_size); +} + +static void *gdnative_realloc(void *p_mem, size_t p_size) { + return memrealloc(p_mem, p_size); +} + +static void gdnative_free(void *p_mem) { + memfree(p_mem); +} + +// Helper print functions. +static void gdnative_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { + _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR); +} +static void gdnative_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { + _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING); +} +static void gdnative_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { + _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT); +} + +// Variant functions + +static void gdnative_variant_new_copy(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src) { + memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant(*reinterpret_cast<Variant *>(p_src))); +} +static void gdnative_variant_new_nil(GDNativeVariantPtr r_dest) { + memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant); +} +static void gdnative_variant_destroy(GDNativeVariantPtr p_self) { + reinterpret_cast<Variant *>(p_self)->~Variant(); +} + +// variant type + +#define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr) + +static void gdnative_variant_call(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) { + Variant *self = (Variant *)p_self; + const StringName *method = (const StringName *)p_method; + const Variant **args = (const Variant **)p_args; + Variant ret; + Callable::CallError error; + self->call(*method, args, p_argcount, ret, error); + memnew_placement_custom(r_return, Variant, Variant(ret)); + + if (r_error) { + r_error->error = (GDNativeCallErrorType)(error.error); + r_error->argument = error.argument; + r_error->expected = error.expected; + } +} + +static void gdnative_variant_call_static(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) { + Variant::Type type = (Variant::Type)p_type; + const StringName *method = (const StringName *)p_method; + const Variant **args = (const Variant **)p_args; + Variant ret; + Callable::CallError error; + Variant::call_static(type, *method, args, p_argcount, ret, error); + memnew_placement_custom(r_return, Variant, Variant(ret)); + + if (r_error) { + r_error->error = (GDNativeCallErrorType)error.error; + r_error->argument = error.argument; + r_error->expected = error.expected; + } +} + +static void gdnative_variant_evaluate(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid) { + Variant::Operator op = (Variant::Operator)p_op; + const Variant *a = (const Variant *)p_a; + const Variant *b = (const Variant *)p_b; + Variant *ret = (Variant *)r_return; + bool valid; + Variant::evaluate(op, *a, *b, *ret, valid); + *r_valid = valid; +} + +static void gdnative_variant_set(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) { + Variant *self = (Variant *)p_self; + const Variant *key = (const Variant *)p_key; + const Variant *value = (const Variant *)p_value; + + bool valid; + self->set(*key, *value, &valid); + *r_valid = valid; +} + +static void gdnative_variant_set_named(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) { + Variant *self = (Variant *)p_self; + const StringName *key = (const StringName *)p_key; + const Variant *value = (const Variant *)p_value; + + bool valid; + self->set_named(*key, *value, valid); + *r_valid = valid; +} + +static void gdnative_variant_set_keyed(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) { + Variant *self = (Variant *)p_self; + const Variant *key = (const Variant *)p_key; + const Variant *value = (const Variant *)p_value; + + bool valid; + self->set_keyed(*key, *value, valid); + *r_valid = valid; +} + +static void gdnative_variant_set_indexed(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob) { + Variant *self = (Variant *)p_self; + const Variant *value = (const Variant *)p_value; + + bool valid; + bool oob; + self->set_indexed(p_index, value, valid, oob); + *r_valid = valid; + *r_oob = oob; +} + +static void gdnative_variant_get(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + const Variant *key = (const Variant *)p_key; + + bool valid; + memnew_placement_custom(r_ret, Variant, Variant(self->get(*key, &valid))); + *r_valid = valid; +} + +static void gdnative_variant_get_named(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + const StringName *key = (const StringName *)p_key; + + bool valid; + memnew_placement_custom(r_ret, Variant, Variant(self->get_named(*key, valid))); + *r_valid = valid; +} + +static void gdnative_variant_get_keyed(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + const Variant *key = (const Variant *)p_key; + + bool valid; + memnew_placement_custom(r_ret, Variant, Variant(self->get_keyed(*key, valid))); + *r_valid = valid; +} + +static void gdnative_variant_get_indexed(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob) { + const Variant *self = (const Variant *)p_self; + + bool valid; + bool oob; + memnew_placement_custom(r_ret, Variant, Variant(self->get_indexed(p_index, valid, oob))); + *r_valid = valid; + *r_oob = oob; +} + +/// Iteration. +static GDNativeBool gdnative_variant_iter_init(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + Variant *iter = (Variant *)r_iter; + + bool valid; + bool ret = self->iter_init(*iter, valid); + *r_valid = valid; + return ret; +} + +static GDNativeBool gdnative_variant_iter_next(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + Variant *iter = (Variant *)r_iter; + + bool valid; + bool ret = self->iter_next(*iter, valid); + *r_valid = valid; + return ret; +} + +static void gdnative_variant_iter_get(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + Variant *iter = (Variant *)r_iter; + + bool valid; + memnew_placement_custom(r_ret, Variant, Variant(self->iter_next(*iter, valid))); + *r_valid = valid; +} + +/// Variant functions. +static GDNativeBool gdnative_variant_hash_compare(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other) { + const Variant *self = (const Variant *)p_self; + const Variant *other = (const Variant *)p_other; + return self->hash_compare(*other); +} + +static GDNativeBool gdnative_variant_booleanize(const GDNativeVariantPtr p_self) { + const Variant *self = (const Variant *)p_self; + return self->booleanize(); +} + +static void gdnative_variant_blend(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) { + const Variant *a = (const Variant *)p_a; + const Variant *b = (const Variant *)p_b; + memnew_placement(r_dst, Variant); + Variant::blend(*a, *b, p_c, *(Variant *)r_dst); +} + +static void gdnative_variant_interpolate(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) { + const Variant *a = (const Variant *)p_a; + const Variant *b = (const Variant *)p_b; + memnew_placement(r_dst, Variant); + Variant::interpolate(*a, *b, p_c, *(Variant *)r_dst); +} + +static void gdnative_variant_duplicate(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep) { + const Variant *self = (const Variant *)p_self; + memnew_placement_custom(r_ret, Variant, Variant(self->duplicate(p_deep))); +} + +static void gdnative_variant_stringify(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret) { + const Variant *self = (const Variant *)p_self; + memnew_placement_custom(r_ret, String, String(*self)); +} + +static GDNativeVariantType gdnative_variant_get_type(const GDNativeVariantPtr p_self) { + const Variant *self = (const Variant *)p_self; + return (GDNativeVariantType)self->get_type(); +} + +static GDNativeBool gdnative_variant_has_method(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method) { + const Variant *self = (const Variant *)p_self; + const StringName *method = (const StringName *)p_method; + return self->has_method(*method); +} + +static GDNativeBool gdnative_variant_has_member(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member) { + return Variant::has_member((Variant::Type)p_type, *((const StringName *)p_member)); +} + +static GDNativeBool gdnative_variant_has_key(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + const Variant *key = (const Variant *)p_key; + bool valid; + return self->has_key(*key, valid); + *r_valid = valid; +} + +static void gdnative_variant_get_type_name(GDNativeVariantType p_type, GDNativeStringPtr r_ret) { + String name = Variant::get_type_name((Variant::Type)p_type); + memnew_placement_custom(r_ret, String, String(name)); +} + +static GDNativeBool gdnative_variant_can_convert(GDNativeVariantType p_from, GDNativeVariantType p_to) { + return Variant::can_convert((Variant::Type)p_from, (Variant::Type)p_to); +} + +static GDNativeBool gdnative_variant_can_convert_strict(GDNativeVariantType p_from, GDNativeVariantType p_to) { + return Variant::can_convert_strict((Variant::Type)p_from, (Variant::Type)p_to); +} + +// ptrcalls +static GDNativePtrOperatorEvaluator gdnative_variant_get_ptr_operator_evaluator(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b) { + return (GDNativePtrOperatorEvaluator)Variant::get_ptr_operator_evaluator(Variant::Operator(p_operator), Variant::Type(p_type_a), Variant::Type(p_type_b)); +} +static GDNativePtrBuiltInMethod gdnative_variant_get_ptr_builtin_method(GDNativeVariantType p_type, const char *p_method, GDNativeInt p_hash) { + StringName method = p_method; + uint32_t hash = Variant::get_builtin_method_hash(Variant::Type(p_type), method); + if (hash != p_hash) { + ERR_PRINT_ONCE("Error getting method " + String(method) + ", hash mismatch."); + return nullptr; + } + + return (GDNativePtrBuiltInMethod)Variant::get_ptr_builtin_method(Variant::Type(p_type), method); +} +static GDNativePtrConstructor gdnative_variant_get_ptr_constructor(GDNativeVariantType p_type, int32_t p_constructor) { + return (GDNativePtrConstructor)Variant::get_ptr_constructor(Variant::Type(p_type), p_constructor); +} +static void gdnative_variant_construct(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error) { + memnew_placement(p_base, Variant); + + Callable::CallError error; + Variant::construct(Variant::Type(p_type), *(Variant *)p_base, (const Variant **)p_args, p_argument_count, error); + + if (r_error) { + r_error->error = (GDNativeCallErrorType)(error.error); + r_error->argument = error.argument; + r_error->expected = error.expected; + } +} +static GDNativePtrSetter gdnative_variant_get_ptr_setter(GDNativeVariantType p_type, const char *p_member) { + return (GDNativePtrSetter)Variant::get_member_ptr_setter(Variant::Type(p_type), p_member); +} +static GDNativePtrGetter gdnative_variant_get_ptr_getter(GDNativeVariantType p_type, const char *p_member) { + return (GDNativePtrGetter)Variant::get_member_ptr_getter(Variant::Type(p_type), p_member); +} +static GDNativePtrIndexedSetter gdnative_variant_get_ptr_indexed_setter(GDNativeVariantType p_type) { + return (GDNativePtrIndexedSetter)Variant::get_member_ptr_indexed_setter(Variant::Type(p_type)); +} +static GDNativePtrIndexedGetter gdnative_variant_get_ptr_indexed_getter(GDNativeVariantType p_type) { + return (GDNativePtrIndexedGetter)Variant::get_member_ptr_indexed_getter(Variant::Type(p_type)); +} +static GDNativePtrKeyedSetter gdnative_variant_get_ptr_keyed_setter(GDNativeVariantType p_type) { + return (GDNativePtrKeyedSetter)Variant::get_member_ptr_keyed_setter(Variant::Type(p_type)); +} +static GDNativePtrKeyedGetter gdnative_variant_get_ptr_keyed_getter(GDNativeVariantType p_type) { + return (GDNativePtrKeyedGetter)Variant::get_member_ptr_keyed_getter(Variant::Type(p_type)); +} +static GDNativePtrKeyedChecker gdnative_variant_get_ptr_keyed_checker(GDNativeVariantType p_type) { + return (GDNativePtrKeyedChecker)Variant::get_member_ptr_keyed_checker(Variant::Type(p_type)); +} +static void gdnative_variant_get_constant_value(GDNativeVariantType p_type, const char *p_constant, GDNativeVariantPtr r_ret) { + memnew_placement_custom(r_ret, Variant, Variant(Variant::get_constant_value(Variant::Type(p_type), p_constant))); +} +static GDNativePtrUtilityFunction gdnative_variant_get_ptr_utility_function(const char *p_function, GDNativeInt p_hash) { + StringName function = p_function; + uint32_t hash = Variant::get_utility_function_hash(function); + if (hash != p_hash) { + ERR_PRINT_ONCE("Error getting utility function " + String(function) + ", hash mismatch."); + return nullptr; + } + return (GDNativePtrUtilityFunction)Variant::get_ptr_utility_function(function); +} + +//string helpers + +static void gdnative_string_new_with_latin1_chars(GDNativeStringPtr r_dest, const char *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String(p_contents); +} + +static void gdnative_string_new_with_utf8_chars(GDNativeStringPtr r_dest, const char *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf8(p_contents); +} + +static void gdnative_string_new_with_utf16_chars(GDNativeStringPtr r_dest, const char16_t *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf16(p_contents); +} + +static void gdnative_string_new_with_utf32_chars(GDNativeStringPtr r_dest, const char32_t *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents); +} + +static void gdnative_string_new_with_wide_chars(GDNativeStringPtr r_dest, const wchar_t *p_contents) { + String *dest = (String *)r_dest; + if (sizeof(wchar_t) == 2) { + // wchar_t is 16 bit, parse. + memnew_placement(dest, String); + dest->parse_utf16((const char16_t *)p_contents); + } else { + // wchar_t is 32 bit, copy. + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents); + } +} + +static void gdnative_string_new_with_latin1_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String(p_contents, p_size); +} + +static void gdnative_string_new_with_utf8_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf8(p_contents, p_size); +} + +static void gdnative_string_new_with_utf16_chars_and_len(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf16(p_contents, p_size); +} + +static void gdnative_string_new_with_utf32_chars_and_len(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents, p_size); +} + +static void gdnative_string_new_with_wide_chars_and_len(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + if (sizeof(wchar_t) == 2) { + // wchar_t is 16 bit, parse. + memnew_placement(dest, String); + dest->parse_utf16((const char16_t *)p_contents, p_size); + } else { + // wchar_t is 32 bit, copy. + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents, p_size); + } +} + +static GDNativeInt gdnative_string_to_latin1_chars(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) { + String *self = (String *)p_self; + CharString cs = self->ascii(true); + GDNativeInt len = cs.length(); + if (r_text) { + const char *s_text = cs.ptr(); + for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) { + r_text[i] = s_text[i]; + } + } + return len; +} +static GDNativeInt gdnative_string_to_utf8_chars(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) { + String *self = (String *)p_self; + CharString cs = self->utf8(); + GDNativeInt len = cs.length(); + if (r_text) { + const char *s_text = cs.ptr(); + for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) { + r_text[i] = s_text[i]; + } + } + return len; +} +static GDNativeInt gdnative_string_to_utf16_chars(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length) { + String *self = (String *)p_self; + Char16String cs = self->utf16(); + GDNativeInt len = cs.length(); + if (r_text) { + const char16_t *s_text = cs.ptr(); + for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) { + r_text[i] = s_text[i]; + } + } + return len; +} +static GDNativeInt gdnative_string_to_utf32_chars(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length) { + String *self = (String *)p_self; + GDNativeInt len = self->length(); + if (r_text) { + const char32_t *s_text = self->ptr(); + for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) { + r_text[i] = s_text[i]; + } + } + return len; +} +static GDNativeInt gdnative_string_to_wide_chars(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length) { + if (sizeof(wchar_t) == 4) { + return gdnative_string_to_utf32_chars(p_self, (char32_t *)r_text, p_max_write_length); + } else { + return gdnative_string_to_utf16_chars(p_self, (char16_t *)r_text, p_max_write_length); + } +} + +static char32_t *gdnative_string_operator_index(GDNativeStringPtr p_self, GDNativeInt p_index) { + String *self = (String *)p_self; + ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr); + return &self->ptrw()[p_index]; +} + +static const char32_t *gdnative_string_operator_index_const(const GDNativeStringPtr p_self, GDNativeInt p_index) { + const String *self = (const String *)p_self; + ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr); + return &self->ptr()[p_index]; +} + +/* OBJECT API */ + +static void gdnative_object_method_bind_ptrcall(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr p_ret) { + MethodBind *mb = (MethodBind *)p_method_bind; + Object *o = (Object *)p_instance; + mb->ptrcall(o, (const void **)p_args, p_ret); +} + +static void gdnative_object_destroy(GDNativeObjectPtr p_o) { + memdelete((Object *)p_o); +} + +static GDNativeObjectPtr gdnative_global_get_singleton(const char *p_name) { + return (GDNativeObjectPtr)Engine::get_singleton()->get_singleton_object(String(p_name)); +} + +static GDNativeObjectPtr gdnative_object_get_instance_from_id(GDObjectInstanceID p_instance_id) { + return (GDNativeObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id)); +} + +static GDNativeObjectPtr gdnative_object_cast_to(const GDNativeObjectPtr p_object, void *p_class_tag) { + if (!p_object) { + return nullptr; + } + Object *o = (Object *)p_object; + + return o->is_class_ptr(p_class_tag) ? (GDNativeObjectPtr)o : (GDNativeObjectPtr) nullptr; +} + +static GDObjectInstanceID gdnative_object_get_instance_id(const GDNativeObjectPtr p_object) { + const Object *o = (const Object *)p_object; + return (GDObjectInstanceID)o->get_instance_id(); +} + +static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const char *p_classname, const char *p_methodname, GDNativeInt p_hash) { + MethodBind *mb = ClassDB::get_method(StringName(p_classname), StringName(p_methodname)); + ERR_FAIL_COND_V(!mb, nullptr); + if (mb->get_hash() != p_hash) { + ERR_PRINT_ONCE("Hash mismatch for method '" + String(p_classname) + "." + String(p_methodname) + "'."); + return nullptr; + } + // MethodBind *mb = ClassDB::get_method("Node", "get_name"); + return (GDNativeMethodBindPtr)mb; +} + +static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname) { + ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(StringName(p_classname)); + if (class_info) { + return (GDNativeClassConstructor)class_info->creation_func; + } + return nullptr; +} + +static void *gdnative_classdb_get_class_tag(const char *p_classname) { + ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(p_classname); + return class_info ? class_info->class_ptr : nullptr; +} + +void gdnative_setup_interface(GDNativeInterface *p_interface) { + GDNativeInterface &gdni = *p_interface; + + gdni.version_major = VERSION_MAJOR; + gdni.version_minor = VERSION_MINOR; +#if VERSION_PATCH + gdni.version_patch = VERSION_PATCH; +#else + gdni.version_patch = 0; +#endif + gdni.version_string = VERSION_FULL_NAME; + + /* GODOT CORE */ + + gdni.mem_alloc = gdnative_alloc; + gdni.mem_realloc = gdnative_realloc; + gdni.mem_free = gdnative_free; + + gdni.print_error = gdnative_print_error; + gdni.print_warning = gdnative_print_warning; + gdni.print_script_error = gdnative_print_script_error; + + /* GODOT VARIANT */ + + // variant general + gdni.variant_new_copy = gdnative_variant_new_copy; + gdni.variant_new_nil = gdnative_variant_new_nil; + gdni.variant_destroy = gdnative_variant_destroy; + + gdni.variant_call = gdnative_variant_call; + gdni.variant_call_static = gdnative_variant_call_static; + gdni.variant_evaluate = gdnative_variant_evaluate; + gdni.variant_set = gdnative_variant_set; + gdni.variant_set_named = gdnative_variant_set_named; + gdni.variant_set_keyed = gdnative_variant_set_keyed; + gdni.variant_set_indexed = gdnative_variant_set_indexed; + gdni.variant_get = gdnative_variant_get; + gdni.variant_get_named = gdnative_variant_get_named; + gdni.variant_get_keyed = gdnative_variant_get_keyed; + gdni.variant_get_indexed = gdnative_variant_get_indexed; + gdni.variant_iter_init = gdnative_variant_iter_init; + gdni.variant_iter_next = gdnative_variant_iter_next; + gdni.variant_iter_get = gdnative_variant_iter_get; + gdni.variant_hash_compare = gdnative_variant_hash_compare; + gdni.variant_booleanize = gdnative_variant_booleanize; + gdni.variant_blend = gdnative_variant_blend; + gdni.variant_interpolate = gdnative_variant_interpolate; + gdni.variant_duplicate = gdnative_variant_duplicate; + gdni.variant_stringify = gdnative_variant_stringify; + + gdni.variant_get_type = gdnative_variant_get_type; + gdni.variant_has_method = gdnative_variant_has_method; + gdni.variant_has_member = gdnative_variant_has_member; + gdni.variant_has_key = gdnative_variant_has_key; + gdni.variant_get_type_name = gdnative_variant_get_type_name; + gdni.variant_can_convert = gdnative_variant_can_convert; + gdni.variant_can_convert_strict = gdnative_variant_can_convert_strict; + + //ptrcalls +#if 0 + GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type); + GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type); +#endif + + gdni.variant_get_ptr_operator_evaluator = gdnative_variant_get_ptr_operator_evaluator; + gdni.variant_get_ptr_builtin_method = gdnative_variant_get_ptr_builtin_method; + gdni.variant_get_ptr_constructor = gdnative_variant_get_ptr_constructor; + gdni.variant_construct = gdnative_variant_construct; + gdni.variant_get_ptr_setter = gdnative_variant_get_ptr_setter; + gdni.variant_get_ptr_getter = gdnative_variant_get_ptr_getter; + gdni.variant_get_ptr_indexed_setter = gdnative_variant_get_ptr_indexed_setter; + gdni.variant_get_ptr_indexed_getter = gdnative_variant_get_ptr_indexed_getter; + gdni.variant_get_ptr_keyed_setter = gdnative_variant_get_ptr_keyed_setter; + gdni.variant_get_ptr_keyed_getter = gdnative_variant_get_ptr_keyed_getter; + gdni.variant_get_ptr_keyed_checker = gdnative_variant_get_ptr_keyed_checker; + gdni.variant_get_constant_value = gdnative_variant_get_constant_value; + gdni.variant_get_ptr_utility_function = gdnative_variant_get_ptr_utility_function; + + // extra utilities + + gdni.string_new_with_latin1_chars = gdnative_string_new_with_latin1_chars; + gdni.string_new_with_utf8_chars = gdnative_string_new_with_utf8_chars; + gdni.string_new_with_utf16_chars = gdnative_string_new_with_utf16_chars; + gdni.string_new_with_utf32_chars = gdnative_string_new_with_utf32_chars; + gdni.string_new_with_wide_chars = gdnative_string_new_with_wide_chars; + gdni.string_new_with_latin1_chars_and_len = gdnative_string_new_with_latin1_chars_and_len; + gdni.string_new_with_utf8_chars_and_len = gdnative_string_new_with_utf8_chars_and_len; + gdni.string_new_with_utf16_chars_and_len = gdnative_string_new_with_utf16_chars_and_len; + gdni.string_new_with_utf32_chars_and_len = gdnative_string_new_with_utf32_chars_and_len; + gdni.string_new_with_wide_chars_and_len = gdnative_string_new_with_wide_chars_and_len; + gdni.string_to_latin1_chars = gdnative_string_to_latin1_chars; + gdni.string_to_utf8_chars = gdnative_string_to_utf8_chars; + gdni.string_to_utf16_chars = gdnative_string_to_utf16_chars; + gdni.string_to_utf32_chars = gdnative_string_to_utf32_chars; + gdni.string_to_wide_chars = gdnative_string_to_wide_chars; + gdni.string_operator_index = gdnative_string_operator_index; + gdni.string_operator_index_const = gdnative_string_operator_index_const; + + /* OBJECT */ + + gdni.object_method_bind_ptrcall = gdnative_object_method_bind_ptrcall; + gdni.object_destroy = gdnative_object_destroy; + gdni.global_get_singleton = gdnative_global_get_singleton; + + gdni.object_cast_to = gdnative_object_cast_to; + gdni.object_get_instance_from_id = gdnative_object_get_instance_from_id; + gdni.object_get_instance_id = gdnative_object_get_instance_id; + + /* CLASSDB */ + + gdni.classdb_get_constructor = gdnative_classdb_get_constructor; + gdni.classdb_get_method_bind = gdnative_classdb_get_method_bind; + gdni.classdb_get_class_tag = gdnative_classdb_get_class_tag; + + /* CLASSDB EXTENSION */ + + //these are filled by implementation, since it will want to keep track of registered classes + gdni.classdb_register_extension_class = nullptr; + gdni.classdb_register_extension_class_method = nullptr; + gdni.classdb_register_extension_class_integer_constant = nullptr; + gdni.classdb_register_extension_class_property = nullptr; + gdni.classdb_register_extension_class_signal = nullptr; + gdni.classdb_unregister_extension_class = nullptr; +} diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h new file mode 100644 index 0000000000..318912e889 --- /dev/null +++ b/core/extension/gdnative_interface.h @@ -0,0 +1,427 @@ +/*************************************************************************/ +/* gdnative_interface.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 GDNATIVE_INTERFACE_H +#define GDNATIVE_INTERFACE_H + +/* This is a C class header, you can copy it and use it directly in your own binders. + * Together with the JSON file, you should be able to generate any binder. + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* VARIANT TYPES */ + +typedef enum { + GDNATIVE_VARIANT_TYPE_NIL, + + /* atomic types */ + GDNATIVE_VARIANT_TYPE_BOOL, + GDNATIVE_VARIANT_TYPE_INT, + GDNATIVE_VARIANT_TYPE_FLOAT, + GDNATIVE_VARIANT_TYPE_STRING, + + /* math types */ + GDNATIVE_VARIANT_TYPE_VECTOR2, + GDNATIVE_VARIANT_TYPE_VECTOR2I, + GDNATIVE_VARIANT_TYPE_RECT2, + GDNATIVE_VARIANT_TYPE_RECT2I, + GDNATIVE_VARIANT_TYPE_VECTOR3, + GDNATIVE_VARIANT_TYPE_VECTOR3I, + GDNATIVE_VARIANT_TYPE_TRANSFORM2D, + GDNATIVE_VARIANT_TYPE_PLANE, + GDNATIVE_VARIANT_TYPE_QUATERNION, + GDNATIVE_VARIANT_TYPE_AABB, + GDNATIVE_VARIANT_TYPE_BASIS, + GDNATIVE_VARIANT_TYPE_TRANSFORM3D, + + /* misc types */ + GDNATIVE_VARIANT_TYPE_COLOR, + GDNATIVE_VARIANT_TYPE_STRING_NAME, + GDNATIVE_VARIANT_TYPE_NODE_PATH, + GDNATIVE_VARIANT_TYPE_RID, + GDNATIVE_VARIANT_TYPE_OBJECT, + GDNATIVE_VARIANT_TYPE_CALLABLE, + GDNATIVE_VARIANT_TYPE_SIGNAL, + GDNATIVE_VARIANT_TYPE_DICTIONARY, + GDNATIVE_VARIANT_TYPE_ARRAY, + + /* typed arrays */ + GDNATIVE_VARIANT_TYPE_PACKED_BYTE_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_INT32_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_INT64_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_FLOAT32_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_FLOAT64_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_STRING_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_VECTOR2_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_VECTOR3_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_COLOR_ARRAY, + + GDNATIVE_VARIANT_TYPE_VARIANT_MAX +} GDNativeVariantType; + +typedef enum { + /* comparison */ + GDNATIVE_VARIANT_OP_EQUAL, + GDNATIVE_VARIANT_OP_NOT_EQUAL, + GDNATIVE_VARIANT_OP_LESS, + GDNATIVE_VARIANT_OP_LESS_EQUAL, + GDNATIVE_VARIANT_OP_GREATER, + GDNATIVE_VARIANT_OP_GREATER_EQUAL, + /* mathematic */ + GDNATIVE_VARIANT_OP_ADD, + GDNATIVE_VARIANT_OP_SUBTRACT, + GDNATIVE_VARIANT_OP_MULTIPLY, + GDNATIVE_VARIANT_OP_DIVIDE, + GDNATIVE_VARIANT_OP_NEGATE, + GDNATIVE_VARIANT_OP_POSITIVE, + GDNATIVE_VARIANT_OP_MODULE, + /* bitwise */ + GDNATIVE_VARIANT_OP_SHIFT_LEFT, + GDNATIVE_VARIANT_OP_SHIFT_RIGHT, + GDNATIVE_VARIANT_OP_BIT_AND, + GDNATIVE_VARIANT_OP_BIT_OR, + GDNATIVE_VARIANT_OP_BIT_XOR, + GDNATIVE_VARIANT_OP_BIT_NEGATE, + /* logic */ + GDNATIVE_VARIANT_OP_AND, + GDNATIVE_VARIANT_OP_OR, + GDNATIVE_VARIANT_OP_XOR, + GDNATIVE_VARIANT_OP_NOT, + /* containment */ + GDNATIVE_VARIANT_OP_IN, + GDNATIVE_VARIANT_OP_MAX + +} GDNativeVariantOperator; + +typedef void *GDNativeVariantPtr; +typedef void *GDNativeStringNamePtr; +typedef void *GDNativeStringPtr; +typedef void *GDNativeObjectPtr; +typedef void *GDNativeTypePtr; +typedef void *GDNativeMethodBindPtr; +typedef int64_t GDNativeInt; +typedef uint32_t GDNativeBool; +typedef uint64_t GDObjectInstanceID; + +/* VARIANT DATA I/O */ + +typedef enum { + NATIVE_CALL_OK, + NATIVE_CALL_ERROR_INVALID_METHOD, + NATIVE_CALL_ERROR_INVALID_ARGUMENT, /* expected is variant type */ + NATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */ + NATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */ + NATIVE_CALL_ERROR_INSTANCE_IS_NULL, + +} GDNativeCallErrorType; + +typedef struct { + GDNativeCallErrorType error; + int32_t argument; + int32_t expected; +} GDNativeCallError; + +typedef void (*GDNativeVariantFromTypeConstructorFunc)(GDNativeVariantPtr, GDNativeTypePtr); +typedef void (*GDNativeTypeFromVariantConstructorFunc)(GDNativeTypePtr, GDNativeVariantPtr); +typedef void (*GDNativePtrOperatorEvaluator)(const GDNativeTypePtr p_left, const GDNativeTypePtr p_right, GDNativeTypePtr r_result); +typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count); +typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args); +typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_value); +typedef void (*GDNativePtrGetter)(const GDNativeTypePtr p_base, GDNativeTypePtr r_value); +typedef void (*GDNativePtrIndexedSetter)(GDNativeTypePtr p_base, GDNativeInt p_index, const GDNativeTypePtr p_value); +typedef void (*GDNativePtrIndexedGetter)(const GDNativeTypePtr p_base, GDNativeInt p_index, GDNativeTypePtr r_value); +typedef void (*GDNativePtrKeyedSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_key, const GDNativeTypePtr p_value); +typedef void (*GDNativePtrKeyedGetter)(const GDNativeTypePtr p_base, const GDNativeTypePtr p_key, GDNativeTypePtr r_value); +typedef uint32_t (*GDNativePtrKeyedChecker)(const GDNativeVariantPtr p_base, const GDNativeVariantPtr p_key); +typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, const GDNativeTypePtr *p_arguments, int p_argument_count); + +typedef GDNativeObjectPtr (*GDNativeClassConstructor)(); + +/* EXTENSION CLASSES */ + +typedef void *GDExtensionClassInstancePtr; + +typedef GDNativeBool (*GDNativeExtensionClassSet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value); +typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret); + +typedef struct { + uint32_t type; + const char *name; + const char *class_name; + uint32_t hint; + const char *hint_string; + uint32_t usage; +} GDNativePropertyInfo; + +typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count); +typedef void (*GDNativeExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDNativePropertyInfo *p_list); +typedef void (*GDNativeExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); +typedef const char *(*GDNativeExtensionClassToString)(GDExtensionClassInstancePtr p_instance); +typedef void (*GDNativeExtensionClassReference)(GDExtensionClassInstancePtr p_instance); +typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); +typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); +typedef GDExtensionClassInstancePtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata); +typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); +typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const char *p_name); + +typedef struct { + GDNativeExtensionClassSet set_func; + GDNativeExtensionClassGet get_func; + GDNativeExtensionClassGetPropertyList get_property_list_func; + GDNativeExtensionClassFreePropertyList free_property_list_func; + GDNativeExtensionClassNotification notification_func; + GDNativeExtensionClassToString to_string_func; + GDNativeExtensionClassReference reference_func; + GDNativeExtensionClassUnreference unreference_func; + GDNativeExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ + GDNativeExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ + GDNativeExtensionClassGetVirtual get_firtual_func; + void *class_userdata; +} GDNativeExtensionClassCreationInfo; + +typedef void *GDNativeExtensionClassLibraryPtr; + +typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count); + +/* Method */ + +typedef enum { + GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL = 1, + GDNATIVE_EXTENSION_METHOD_FLAG_EDITOR = 2, + GDNATIVE_EXTENSION_METHOD_FLAG_NOSCRIPT = 4, + GDNATIVE_EXTENSION_METHOD_FLAG_CONST = 8, + GDNATIVE_EXTENSION_METHOD_FLAG_REVERSE = 16, /* used for events */ + GDNATIVE_EXTENSION_METHOD_FLAG_VIRTUAL = 32, + GDNATIVE_EXTENSION_METHOD_FLAG_FROM_SCRIPT = 64, + GDNATIVE_EXTENSION_METHOD_FLAG_VARARG = 128, + GDNATIVE_EXTENSION_METHOD_FLAG_STATIC = 256, + GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT = GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL, +} GDNativeExtensionClassMethodFlags; + +typedef enum { + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_NONE, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE +} GDNativeExtensionClassMethodArgumentMetadata; + +typedef void (*GDNativeExtensionClassMethodCall)(GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); +typedef void (*GDNativeExtensionClassMethodPtrCall)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); + +/* passing -1 as argument in the following functions refers to the return type */ +typedef GDNativeVariantType (*GDNativeExtensionClassMethodGetArgumentType)(void *p_method_userdata, int32_t p_argument); +typedef void (*GDNativeExtensionClassMethodGetArgumentInfo)(void *p_method_userdata, int32_t p_argument, GDNativePropertyInfo *r_info); +typedef GDNativeExtensionClassMethodArgumentMetadata (*GDNativeExtensionClassMethodGetArgumentMetadata)(void *p_method_userdata, int32_t p_argument); + +typedef struct { + const char *name; + void *method_userdata; + GDNativeExtensionClassMethodCall call_func; + GDNativeExtensionClassMethodPtrCall ptrcall_func; + uint32_t method_flags; /* GDNativeExtensionClassMethodFlags */ + uint32_t argument_count; + GDNativeBool has_return_value; + GDNativeExtensionClassMethodGetArgumentType get_argument_type_func; + GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func; /* name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. */ + GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func; + uint32_t default_argument_count; + GDNativeVariantPtr *default_arguments; +} GDNativeExtensionClassMethodInfo; + +/* INTERFACE */ + +typedef struct { + uint32_t version_major; + uint32_t version_minor; + uint32_t version_patch; + const char *version_string; + + /* GODOT CORE */ + void *(*mem_alloc)(size_t p_bytes); + void *(*mem_realloc)(void *p_ptr, size_t p_bytes); + void (*mem_free)(void *p_ptr); + + void (*print_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); + void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); + void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); + + /* GODOT VARIANT */ + + /* variant general */ + void (*variant_new_copy)(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src); + void (*variant_new_nil)(GDNativeVariantPtr r_dest); + void (*variant_destroy)(GDNativeVariantPtr p_self); + + /* variant type */ + void (*variant_call)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); + void (*variant_call_static)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); + void (*variant_evaluate)(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid); + void (*variant_set)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_named)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_keyed)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_indexed)(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob); + void (*variant_get)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_named)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_keyed)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_indexed)(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob); + GDNativeBool (*variant_iter_init)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid); + GDNativeBool (*variant_iter_next)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid); + void (*variant_iter_get)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other); + GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self); + void (*variant_blend)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst); + void (*variant_interpolate)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst); + void (*variant_duplicate)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep); + void (*variant_stringify)(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret); + + GDNativeVariantType (*variant_get_type)(const GDNativeVariantPtr p_self); + GDNativeBool (*variant_has_method)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method); + GDNativeBool (*variant_has_member)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member); + GDNativeBool (*variant_has_key)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid); + void (*variant_get_type_name)(GDNativeVariantType p_type, GDNativeStringPtr r_name); + GDNativeBool (*variant_can_convert)(GDNativeVariantType p_from, GDNativeVariantType p_to); + GDNativeBool (*variant_can_convert_strict)(GDNativeVariantType p_from, GDNativeVariantType p_to); + + /* ptrcalls */ + GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type); + GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type); + GDNativePtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b); + GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, const char *p_method, GDNativeInt p_hash); + GDNativePtrConstructor (*variant_get_ptr_constructor)(GDNativeVariantType p_type, int32_t p_constructor); + void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error); + GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, const char *p_member); + GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, const char *p_member); + GDNativePtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDNativeVariantType p_type); + GDNativePtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDNativeVariantType p_type); + GDNativePtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDNativeVariantType p_type); + GDNativePtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDNativeVariantType p_type); + GDNativePtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDNativeVariantType p_type); + void (*variant_get_constant_value)(GDNativeVariantType p_type, const char *p_constant, GDNativeVariantPtr r_ret); + GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(const char *p_function, GDNativeInt p_hash); + + /* extra utilities */ + + void (*string_new_with_latin1_chars)(GDNativeStringPtr r_dest, const char *p_contents); + void (*string_new_with_utf8_chars)(GDNativeStringPtr r_dest, const char *p_contents); + void (*string_new_with_utf16_chars)(GDNativeStringPtr r_dest, const char16_t *p_contents); + void (*string_new_with_utf32_chars)(GDNativeStringPtr r_dest, const char32_t *p_contents); + void (*string_new_with_wide_chars)(GDNativeStringPtr r_dest, const wchar_t *p_contents); + void (*string_new_with_latin1_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size); + void (*string_new_with_utf8_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size); + void (*string_new_with_utf16_chars_and_len)(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size); + void (*string_new_with_utf32_chars_and_len)(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size); + void (*string_new_with_wide_chars_and_len)(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size); + /* Information about the following functions: + * - The return value is the resulting encoded string length. + * - The length returned is in characters, not in bytes. It also does not include a trailing zero. + * - These functions also do not write trailing zero, If you need it, write it yourself at the position indicated by the length (and make sure to allocate it). + * - Passing NULL in r_text means only the length is computed (again, without including trailing zero). + * - p_max_write_length argument is in characters, not bytes. It will be ignored if r_text is NULL. + * - p_max_write_length argument does not affect the return value, it's only to cap write length. + */ + GDNativeInt (*string_to_latin1_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf8_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf16_chars)(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf32_chars)(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_wide_chars)(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length); + char32_t *(*string_operator_index)(GDNativeStringPtr p_self, GDNativeInt p_index); + const char32_t *(*string_operator_index_const)(const GDNativeStringPtr p_self, GDNativeInt p_index); + + /* OBJECT */ + + void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); + void (*object_destroy)(GDNativeObjectPtr p_o); + GDNativeObjectPtr (*global_get_singleton)(const char *p_name); + + GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag); + GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); + GDObjectInstanceID (*object_get_instance_id)(const GDNativeObjectPtr p_object); + + /* CLASSDB */ + + GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname); + GDNativeMethodBindPtr (*classdb_get_method_bind)(const char *p_classname, const char *p_methodname, GDNativeInt p_hash); + void *(*classdb_get_class_tag)(const char *p_classname); + + /* CLASSDB EXTENSION */ + + void (*classdb_register_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs); + void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); + void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_enum_name, const char *p_class_name, const char *p_constant_name, uint32_t p_constant_value); + void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); + void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); + void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ +} GDNativeInterface; + +/* INITIALIZATION */ + +typedef enum { + GDNATIVE_INITIALIZATION_CORE, + GDNATIVE_INITIALIZATION_SERVERS, + GDNATIVE_INITIALIZATION_SCENE, + GDNATIVE_INITIALIZATION_EDITOR, +} GDNativeInitializationLevel; + +typedef struct { + /* Minimum initialization level required. + * If Core or Servers, the extension needs editor or game restart to take effect */ + GDNativeInitializationLevel minimum_initialization_level; + /* Up to the user to supply when initializing */ + void *userdata; + /* This function will be called multiple times for each initialization level. */ + void (*initialize)(void *userdata, GDNativeInitializationLevel p_level); + void (*deinitialize)(void *userdata, GDNativeInitializationLevel p_level); +} GDNativeInitialization; + +/* Define a C function prototype that implements the function below and expose it to dlopen() (or similar). + * It will be called on initialization. The name must be an unique one specified in the .gdextension config file. + */ + +typedef GDNativeBool (*GDNativeInitializationFunction)(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp new file mode 100644 index 0000000000..65718a7507 --- /dev/null +++ b/core/extension/native_extension.cpp @@ -0,0 +1,411 @@ +/*************************************************************************/ +/* native_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "native_extension.h" +#include "core/io/config_file.h" +#include "core/object/class_db.h" +#include "core/object/method_bind.h" +#include "core/os/os.h" + +class NativeExtensionMethodBind : public MethodBind { + GDNativeExtensionClassMethodCall call_func; + GDNativeExtensionClassMethodPtrCall ptrcall_func; + GDNativeExtensionClassMethodGetArgumentType get_argument_type_func; + GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func; + GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func; + void *method_userdata; + bool vararg; + +protected: + virtual Variant::Type _gen_argument_type(int p_arg) const { + return Variant::Type(get_argument_type_func(method_userdata, p_arg)); + } + virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + GDNativePropertyInfo pinfo; + get_argument_info_func(method_userdata, p_arg, &pinfo); + PropertyInfo ret; + ret.type = Variant::Type(pinfo.type); + ret.name = pinfo.name; + ret.class_name = pinfo.class_name; + ret.hint = PropertyHint(pinfo.hint); + ret.usage = pinfo.usage; + ret.class_name = pinfo.class_name; + return ret; + } + +public: + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + return GodotTypeInfo::Metadata(get_argument_metadata_func(method_userdata, p_arg)); + } + + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + Variant ret; + GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); + GDNativeCallError ce; + call_func(extension_instance, (const GDNativeVariantPtr *)p_args, p_arg_count, (GDNativeVariantPtr)&ret, &ce); + r_error.error = Callable::CallError::Error(ce.error); + r_error.argument = ce.argument; + r_error.expected = ce.expected; + return ret; + } + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { + ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug."); + GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); + ptrcall_func(extension_instance, (const GDNativeTypePtr *)p_args, (GDNativeTypePtr)r_ret); + } + + virtual bool is_vararg() const { + return false; + } + NativeExtensionMethodBind(const GDNativeExtensionClassMethodInfo *p_method_info) { + method_userdata = p_method_info->method_userdata; + call_func = p_method_info->call_func; + ptrcall_func = p_method_info->ptrcall_func; + get_argument_type_func = p_method_info->get_argument_type_func; + get_argument_info_func = p_method_info->get_argument_info_func; + get_argument_metadata_func = p_method_info->get_argument_metadata_func; + + vararg = p_method_info->method_flags & GDNATIVE_EXTENSION_METHOD_FLAG_VARARG; + + _set_returns(p_method_info->has_return_value); + _set_const(p_method_info->method_flags & GDNATIVE_EXTENSION_METHOD_FLAG_CONST); +#ifdef DEBUG_METHODS_ENABLED + _generate_argument_types(p_method_info->argument_count); +#endif + set_argument_count(p_method_info->argument_count); + } +}; + +static GDNativeInterface gdnative_interface; + +void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(String(class_name).is_valid_identifier(), "Attempt to register extension clas '" + class_name + "', which is not a valid class identifier."); + ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered."); + + Extension *parent_extension = nullptr; + StringName parent_class_name = p_parent_class_name; + + if (self->extension_classes.has(parent_class_name)) { + parent_extension = &self->extension_classes[parent_class_name]; + } else if (ClassDB::class_exists(parent_class_name)) { + if (ClassDB::get_api_type(parent_class_name) == ClassDB::API_EXTENSION || ClassDB::get_api_type(parent_class_name) == ClassDB::API_EDITOR_EXTENSION) { + ERR_PRINT("Unimplemented yet"); + //inheriting from another extension + } else { + //inheriting from engine class + } + } else { + ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'"); + } + + self->extension_classes[class_name] = Extension(); + + Extension *extension = &self->extension_classes[class_name]; + + if (parent_extension) { + extension->native_extension.parent = &parent_extension->native_extension; + parent_extension->native_extension.children.push_back(&extension->native_extension); + } + + extension->native_extension.parent_class_name = parent_class_name; + extension->native_extension.class_name = class_name; + extension->native_extension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR; + extension->native_extension.set = p_extension_funcs->set_func; + extension->native_extension.get = p_extension_funcs->get_func; + extension->native_extension.get_property_list = p_extension_funcs->get_property_list_func; + extension->native_extension.free_property_list = p_extension_funcs->free_property_list_func; + extension->native_extension.notification = p_extension_funcs->notification_func; + extension->native_extension.to_string = p_extension_funcs->to_string_func; + extension->native_extension.reference = p_extension_funcs->reference_func; + extension->native_extension.unreference = p_extension_funcs->unreference_func; + extension->native_extension.class_userdata = p_extension_funcs->class_userdata; + extension->native_extension.create_instance = p_extension_funcs->create_instance_func; + extension->native_extension.free_instance = p_extension_funcs->free_instance_func; + + ClassDB::register_extension_class(&extension->native_extension); +} +void NativeExtension::_register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + StringName method_name = p_method_info->name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'."); + + //Extension *extension = &self->extension_classes[class_name]; + + NativeExtensionMethodBind *method = memnew(NativeExtensionMethodBind(p_method_info)); + + ClassDB::bind_method_custom(class_name, method); +} +void NativeExtension::_register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, uint32_t p_constant_value) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension constant '" + String(p_constant_name) + "' for unexisting class '" + class_name + "'."); + + //Extension *extension = &self->extension_classes[class_name]; + + ClassDB::bind_integer_constant(class_name, p_enum_name, p_constant_name, p_constant_value); +} +void NativeExtension::_register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension class property '" + String(p_info->name) + "' for unexisting class '" + class_name + "'."); + + //Extension *extension = &self->extension_classes[class_name]; + PropertyInfo pinfo; + pinfo.type = Variant::Type(p_info->type); + pinfo.name = p_info->name; + pinfo.class_name = p_info->class_name; + pinfo.hint = PropertyHint(p_info->hint); + pinfo.hint_string = p_info->hint_string; + pinfo.usage = p_info->usage; + + ClassDB::add_property(class_name, pinfo, p_setter, p_getter); +} + +void NativeExtension::_register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension class signal '" + String(p_signal_name) + "' for unexisting class '" + class_name + "'."); + + MethodInfo s; + s.name = p_signal_name; + for (int i = 0; i < p_argument_count; i++) { + PropertyInfo arg; + arg.type = Variant::Type(p_argument_info[i].type); + arg.name = p_argument_info[i].name; + arg.class_name = p_argument_info[i].class_name; + arg.hint = PropertyHint(p_argument_info[i].hint); + arg.hint_string = p_argument_info[i].hint_string; + arg.usage = p_argument_info[i].usage; + s.arguments.push_back(arg); + } + ClassDB::add_signal(class_name, s); +} + +void NativeExtension::_unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'."); + Extension *ext = &self->extension_classes[class_name]; + ERR_FAIL_COND_MSG(ext->native_extension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it."); + + ClassDB::unregister_extension_class(class_name); + if (ext->native_extension.parent != nullptr) { + ext->native_extension.parent->children.erase(&ext->native_extension); + } + self->extension_classes.erase(class_name); +} + +Error NativeExtension::open_library(const String &p_path, const String &p_entry_symbol) { + Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true); + if (err != OK) { + return err; + } + + void *entry_funcptr = nullptr; + + err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false); + + if (err != OK) { + OS::get_singleton()->close_dynamic_library(library); + return err; + } + + GDNativeInitializationFunction initialization_function = (GDNativeInitializationFunction)entry_funcptr; + + initialization_function(&gdnative_interface, this, &initialization); + level_initialized = -1; + return OK; +} + +void NativeExtension::close_library() { + ERR_FAIL_COND(library == nullptr); + OS::get_singleton()->close_dynamic_library(library); + + library = nullptr; +} + +bool NativeExtension::is_library_open() const { + return library != nullptr; +} + +NativeExtension::InitializationLevel NativeExtension::get_minimum_library_initialization_level() const { + ERR_FAIL_COND_V(library == nullptr, INITIALIZATION_LEVEL_CORE); + return InitializationLevel(initialization.minimum_initialization_level); +} +void NativeExtension::initialize_library(InitializationLevel p_level) { + ERR_FAIL_COND(library == nullptr); + ERR_FAIL_COND(p_level <= int32_t(level_initialized)); + + level_initialized = int32_t(p_level); + + ERR_FAIL_COND(initialization.initialize == nullptr); + + initialization.initialize(initialization.userdata, GDNativeInitializationLevel(p_level)); +} +void NativeExtension::deinitialize_library(InitializationLevel p_level) { + ERR_FAIL_COND(library == nullptr); + ERR_FAIL_COND(p_level > int32_t(level_initialized)); + + level_initialized = int32_t(p_level) - 1; + initialization.deinitialize(initialization.userdata, GDNativeInitializationLevel(p_level)); +} + +void NativeExtension::_bind_methods() { + ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol"), &NativeExtension::open_library); + ClassDB::bind_method(D_METHOD("close_library"), &NativeExtension::close_library); + ClassDB::bind_method(D_METHOD("is_library_open"), &NativeExtension::is_library_open); + + ClassDB::bind_method(D_METHOD("get_minimum_library_initialization_level"), &NativeExtension::get_minimum_library_initialization_level); + ClassDB::bind_method(D_METHOD("initialize_library", "level"), &NativeExtension::initialize_library); + + BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_CORE); + BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SERVERS); + BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SCENE); + BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_EDITOR); +} + +NativeExtension::NativeExtension() { +} + +NativeExtension::~NativeExtension() { + if (library != nullptr) { + close_library(); + } +} + +extern void gdnative_setup_interface(GDNativeInterface *p_interface); + +void NativeExtension::initialize_native_extensions() { + gdnative_setup_interface(&gdnative_interface); + + gdnative_interface.classdb_register_extension_class = _register_extension_class; + gdnative_interface.classdb_register_extension_class_method = _register_extension_class_method; + gdnative_interface.classdb_register_extension_class_integer_constant = _register_extension_class_integer_constant; + gdnative_interface.classdb_register_extension_class_property = _register_extension_class_property; + gdnative_interface.classdb_register_extension_class_signal = _register_extension_class_signal; + gdnative_interface.classdb_unregister_extension_class = _unregister_extension_class; +} + +RES NativeExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { + Ref<ConfigFile> config; + config.instantiate(); + + Error err = config->load(p_path); + + if (r_error) { + *r_error = err; + } + + if (err != OK) { + return RES(); + } + + if (!config->has_section_key("configuration", "entry_symbol")) { + if (r_error) { + *r_error = ERR_INVALID_DATA; + } + return RES(); + } + + String entry_symbol = config->get_value("configuration", "entry_symbol"); + + List<String> libraries; + + config->get_section_keys("libraries", &libraries); + + String library_path; + + for (List<String>::Element *E = libraries.front(); E; E = E->next()) { + Vector<String> tags = E->get().split("."); + bool all_tags_met = true; + for (int i = 0; i < tags.size(); i++) { + String tag = tags[i].strip_edges(); + if (!OS::get_singleton()->has_feature(tag)) { + all_tags_met = false; + break; + } + } + + if (all_tags_met) { + library_path = config->get_value("libraries", E->get()); + break; + } + } + + if (library_path != String()) { + if (r_error) { + *r_error = ERR_FILE_NOT_FOUND; + } + return RES(); + } + + if (!library_path.is_resource_file()) { + library_path = p_path.get_base_dir().plus_file(library_path); + } + + Ref<NativeExtension> lib; + lib.instantiate(); + err = lib->open_library(library_path, entry_symbol); + + if (r_error) { + *r_error = err; + } + + if (err != OK) { + return RES(); + } + + return lib; +} + +void NativeExtensionResourceLoader::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("gdextension"); +} + +bool NativeExtensionResourceLoader::handles_type(const String &p_type) const { + return p_type == "NativeExtension"; +} + +String NativeExtensionResourceLoader::get_resource_type(const String &p_path) const { + String el = p_path.get_extension().to_lower(); + if (el == "gdextension") { + return "NativeExtension"; + } + return ""; +} diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h new file mode 100644 index 0000000000..0a23848eb2 --- /dev/null +++ b/core/extension/native_extension.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* native_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 NATIVE_EXTENSION_H +#define NATIVE_EXTENSION_H + +#include "core/extension/gdnative_interface.h" +#include "core/io/resource_loader.h" +#include "core/object/ref_counted.h" + +class NativeExtension : public RefCounted { + GDCLASS(NativeExtension, RefCounted) + + void *library = nullptr; // pointer if valid, + + struct Extension { + ObjectNativeExtension native_extension; + }; + + Map<StringName, Extension> extension_classes; + + static void _register_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs); + static void _register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); + static void _register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, uint32_t p_constant_value); + static void _register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); + static void _register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); + static void _unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); + + GDNativeInitialization initialization; + int32_t level_initialized = -1; + +protected: + static void _bind_methods(); + +public: + Error open_library(const String &p_path, const String &p_entry_symbol); + void close_library(); + + enum InitializationLevel { + INITIALIZATION_LEVEL_CORE, + INITIALIZATION_LEVEL_SERVERS, + INITIALIZATION_LEVEL_SCENE, + INITIALIZATION_LEVEL_EDITOR, + }; + + bool is_library_open() const; + + InitializationLevel get_minimum_library_initialization_level() const; + void initialize_library(InitializationLevel p_level); + void deinitialize_library(InitializationLevel p_level); + + static void initialize_native_extensions(); + NativeExtension(); + ~NativeExtension(); +}; + +VARIANT_ENUM_CAST(NativeExtension::InitializationLevel) + +class NativeExtensionResourceLoader : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + +#endif // NATIVEEXTENSION_H diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp new file mode 100644 index 0000000000..7be2593845 --- /dev/null +++ b/core/extension/native_extension_manager.cpp @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* native_extension_manager.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "native_extension_manager.h" + +NativeExtensionManager::LoadStatus NativeExtensionManager::load_extension(const String &p_path) { + if (native_extension_map.has(p_path)) { + return LOAD_STATUS_ALREADY_LOADED; + } + Ref<NativeExtension> extension = ResourceLoader::load(p_path); + if (extension.is_null()) { + return LOAD_STATUS_FAILED; + } + + if (level >= 0) { //already initialized up to some level + int32_t minimum_level = extension->get_minimum_library_initialization_level(); + if (minimum_level < MIN(level, NativeExtension::INITIALIZATION_LEVEL_SCENE)) { + return LOAD_STATUS_NEEDS_RESTART; + } + //initialize up to current level + for (int32_t i = minimum_level; i < level; i++) { + extension->initialize_library(NativeExtension::InitializationLevel(level)); + } + } + native_extension_map[p_path] = extension; + return LOAD_STATUS_OK; +} + +NativeExtensionManager::LoadStatus NativeExtensionManager::reload_extension(const String &p_path) { + return LOAD_STATUS_OK; //TODO +} +NativeExtensionManager::LoadStatus NativeExtensionManager::unload_extension(const String &p_path) { + if (!native_extension_map.has(p_path)) { + return LOAD_STATUS_NOT_LOADED; + } + + Ref<NativeExtension> extension = native_extension_map[p_path]; + + if (level >= 0) { //already initialized up to some level + int32_t minimum_level = extension->get_minimum_library_initialization_level(); + if (minimum_level < MIN(level, NativeExtension::INITIALIZATION_LEVEL_SCENE)) { + return LOAD_STATUS_NEEDS_RESTART; + } + //initialize up to current level + for (int32_t i = level; i >= minimum_level; i--) { + extension->deinitialize_library(NativeExtension::InitializationLevel(level)); + } + } + native_extension_map.erase(p_path); + return LOAD_STATUS_OK; +} +Vector<String> NativeExtensionManager::get_loaded_extensions() const { + Vector<String> ret; + for (const Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} +Ref<NativeExtension> NativeExtensionManager::get_extension(const String &p_path) { + Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.find(p_path); + ERR_FAIL_COND_V(!E, Ref<NativeExtension>()); + return E->get(); +} + +void NativeExtensionManager::initialize_extensions(NativeExtension::InitializationLevel p_level) { + ERR_FAIL_COND(int32_t(p_level) - 1 != level); + for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { + E->get()->initialize_library(p_level); + } + level = p_level; +} + +void NativeExtensionManager::deinitialize_extensions(NativeExtension::InitializationLevel p_level) { + ERR_FAIL_COND(int32_t(p_level) != level); + for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { + E->get()->deinitialize_library(p_level); + } + level = int32_t(p_level) - 1; +} + +NativeExtensionManager *NativeExtensionManager::get_singleton() { + return singleton; +} +void NativeExtensionManager::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_extension", "path"), &NativeExtensionManager::load_extension); + ClassDB::bind_method(D_METHOD("reload_extension", "path"), &NativeExtensionManager::reload_extension); + ClassDB::bind_method(D_METHOD("unload_extension", "path"), &NativeExtensionManager::unload_extension); + ClassDB::bind_method(D_METHOD("get_loaded_extensions"), &NativeExtensionManager::get_loaded_extensions); + ClassDB::bind_method(D_METHOD("get_extension", "path"), &NativeExtensionManager::get_extension); + + BIND_ENUM_CONSTANT(LOAD_STATUS_OK); + BIND_ENUM_CONSTANT(LOAD_STATUS_FAILED); + BIND_ENUM_CONSTANT(LOAD_STATUS_ALREADY_LOADED); + BIND_ENUM_CONSTANT(LOAD_STATUS_NOT_LOADED); + BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART); +} + +NativeExtensionManager *NativeExtensionManager::singleton = nullptr; + +NativeExtensionManager::NativeExtensionManager() { + ERR_FAIL_COND(singleton != nullptr); + singleton = this; +} diff --git a/core/extension/native_extension_manager.h b/core/extension/native_extension_manager.h new file mode 100644 index 0000000000..78465bd5cf --- /dev/null +++ b/core/extension/native_extension_manager.h @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* native_extension_manager.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 NATIVE_EXTENSION_MANAGER_H +#define NATIVE_EXTENSION_MANAGER_H + +#include "core/extension/native_extension.h" + +class NativeExtensionManager : public Object { + GDCLASS(NativeExtensionManager, Object); + + int32_t level = -1; + Map<String, Ref<NativeExtension>> native_extension_map; + + static void _bind_methods(); + + static NativeExtensionManager *singleton; + +public: + enum LoadStatus { + LOAD_STATUS_OK, + LOAD_STATUS_FAILED, + LOAD_STATUS_ALREADY_LOADED, + LOAD_STATUS_NOT_LOADED, + LOAD_STATUS_NEEDS_RESTART, + }; + + LoadStatus load_extension(const String &p_path); + LoadStatus reload_extension(const String &p_path); + LoadStatus unload_extension(const String &p_path); + Vector<String> get_loaded_extensions() const; + Ref<NativeExtension> get_extension(const String &p_path); + + void initialize_extensions(NativeExtension::InitializationLevel p_level); + void deinitialize_extensions(NativeExtension::InitializationLevel p_level); + + static NativeExtensionManager *get_singleton(); + + NativeExtensionManager(); +}; + +VARIANT_ENUM_CAST(NativeExtensionManager::LoadStatus) + +#endif // NATIVEEXTENSIONMANAGER_H diff --git a/core/object/SCsub b/core/object/SCsub index 5d429960e5..dc116aeb19 100644 --- a/core/object/SCsub +++ b/core/object/SCsub @@ -2,6 +2,11 @@ Import("env") +import make_virtuals +from platform_methods import run_in_subprocess + +env.CommandNoCache(["gdvirtual.gen.inc"], "make_virtuals.py", run_in_subprocess(make_virtuals.run)) + env_object = env.Clone() env_object.add_source_files(env.core_sources, "*.cpp") diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index df36587662..a10405dfae 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -503,9 +503,9 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam thread_local bool initializing_with_extension = false; thread_local ObjectNativeExtension *initializing_extension = nullptr; -thread_local void *initializing_extension_instance = nullptr; +thread_local GDExtensionClassInstancePtr initializing_extension_instance = nullptr; -void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, void **r_extension_instance) { +void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance) { if (initializing_with_extension) { *r_extension = initializing_extension; *r_extension_instance = initializing_extension_instance; @@ -539,7 +539,7 @@ Object *ClassDB::instantiate(const StringName &p_class) { if (ti->native_extension) { initializing_with_extension = true; initializing_extension = ti->native_extension; - initializing_extension_instance = ti->native_extension->create_instance(ti->native_extension->create_instance_userdata); + initializing_extension_instance = ti->native_extension->create_instance(ti->native_extension->class_userdata); } return ti->creation_func(); } @@ -1603,6 +1603,11 @@ void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { classes[p_extension->class_name] = c; } +void ClassDB::unregister_extension_class(const StringName &p_class) { + ERR_FAIL_COND(!classes.has(p_class)); + classes.erase(p_class); +} + RWLock ClassDB::lock; void ClassDB::cleanup_defaults() { diff --git a/core/object/class_db.h b/core/object/class_db.h index a4af535149..af528bfde7 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -204,6 +204,7 @@ public: } static void register_extension_class(ObjectNativeExtension *p_extension); + static void unregister_extension_class(const StringName &p_class); template <class T> static Object *_create_ptr_func() { @@ -232,7 +233,8 @@ public: static bool is_parent_class(const StringName &p_class, const StringName &p_inherits); static bool can_instantiate(const StringName &p_class); static Object *instantiate(const StringName &p_class); - static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, void **r_extension_instance); + static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance); + static APIType get_api_type(const StringName &p_class); static uint64_t get_api_hash(APIType p_api); diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py new file mode 100644 index 0000000000..2c6b8cddc9 --- /dev/null +++ b/core/object/make_virtuals.py @@ -0,0 +1,152 @@ +proto = """ +#define GDVIRTUAL$VER($RET m_name $ARG) \\ +GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ +StringName _gdvirtual_##m_name##_sn = #m_name;\\ +bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\ + ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\ + if (script_instance) {\\ + Callable::CallError ce; \\ + $CALLSIARGS\\ + $CALLSIBEGINscript_instance->call(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ + if (ce.error == Callable::CallError::CALL_OK) {\\ + $CALLSIRET\\ + return true;\\ + } \\ + }\\ + if (_gdvirtual_##m_name) {\\ + $CALLPTRARGS\\ + $CALLPTRRETDEF\\ + _gdvirtual_##m_name(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\ + $CALLPTRRET\\ + return true;\\ + }\\ +\\ + return false;\\ +}\\ +\\ +_FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\ + MethodInfo method_info;\\ + method_info.name = #m_name;\\ + method_info.flags = METHOD_FLAG_VIRTUAL;\\ + $FILL_METHOD_INFO\\ + return method_info;\\ +} + + +""" + + +def generate_version(argcount, const=False, returns=False): + s = proto + sproto = str(argcount) + method_info = "" + if returns: + sproto += "R" + s = s.replace("$RET", "m_ret, ") + s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;") + method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n" + else: + s = s.replace("$RET", "") + s = s.replace("$CALLPTRRETDEF", "") + + if const: + sproto += "C" + s = s.replace("$CONST", "const") + method_info += "\tmethod_info.flags|=METHOD_FLAG_CONST;\\\n" + else: + s = s.replace("$CONST", "") + + s = s.replace("$VER", sproto) + argtext = "" + callargtext = "" + callsiargs = "" + callsiargptrs = "" + callptrargsptr = "" + if argcount > 0: + argtext += ", " + callsiargs = "Variant vargs[" + str(argcount) + "]={" + callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={" + callptrargsptr = "\t\tconst GDNativeTypePtr argptrs[" + str(argcount) + "]={" + callptrargs = "" + for i in range(argcount): + if i > 0: + argtext += ", " + callargtext += ", " + callsiargs += ", " + callsiargptrs += ", " + callptrargs += "\t\t" + callptrargsptr += ", " + argtext += "m_type" + str(i + 1) + callargtext += "const m_type" + str(i + 1) + "& arg" + str(i + 1) + callsiargs += "Variant(arg" + str(i + 1) + ")" + callsiargptrs += "&vargs[" + str(i) + "]" + callptrargs += ( + "PtrToArg<m_type" + str(i + 1) + ">::EncodeT argval" + str(i + 1) + " = arg" + str(i + 1) + ";\\\n" + ) + callptrargsptr += "&argval" + str(i + 1) + method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n" + + if argcount: + callsiargs += "};\\\n" + callsiargptrs += "};\\\n" + s = s.replace("$CALLSIARGS", callsiargs + callsiargptrs) + s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount)) + callptrargsptr += "};\\\n" + s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr) + s = s.replace("$CALLPTRARGPASS", "(const GDNativeTypePtr*)argptrs") + else: + s = s.replace("$CALLSIARGS", "") + s = s.replace("$CALLSIARGPASS", "nullptr, 0") + s = s.replace("$CALLPTRARGS", "") + s = s.replace("$CALLPTRARGPASS", "nullptr") + + if returns: + if argcount > 0: + callargtext += "," + callargtext += " m_ret& r_ret" + s = s.replace("$CALLSIBEGIN", "Variant ret = ") + s = s.replace("$CALLSIRET", "r_ret = ret;") + s = s.replace("$CALLPTRRETPASS", "&ret") + s = s.replace("$CALLPTRRET", "r_ret = ret;") + else: + s = s.replace("$CALLSIBEGIN", "") + s = s.replace("$CALLSIRET", "") + s = s.replace("$CALLPTRRETPASS", "nullptr") + s = s.replace("$CALLPTRRET", "") + + s = s.replace("$ARG", argtext) + s = s.replace("$CALLARGS", callargtext) + s = s.replace("$FILL_METHOD_INFO", method_info) + + return s + + +def run(target, source, env): + + max_versions = 12 + + txt = """ +#ifndef GDVIRTUAL_GEN_H +#define GDVIRTUAL_GEN_H + + +""" + + for i in range(max_versions + 1): + + txt += "/* " + str(i) + " Arguments */\n\n" + txt += generate_version(i, False, False) + txt += generate_version(i, False, True) + txt += generate_version(i, True, False) + txt += generate_version(i, True, True) + + txt += "#endif" + + with open(target[0], "w") as f: + f.write(txt) + + +if __name__ == "__main__": + from platform_methods import subprocess_main + + subprocess_main(globals()) diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp index 9c5ed60708..c53104fe3f 100644 --- a/core/object/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -34,6 +34,35 @@ #include "method_bind.h" +uint32_t MethodBind::get_hash() const { + uint32_t hash = hash_djb2_one_32(has_return() ? 1 : 0); + hash = hash_djb2_one_32(get_argument_count(), hash); + +#ifndef _MSC_VER +#warning This needs proper class name and argument type for hashing +#endif +#if 0 + + for (int i = (has_return() ? -1 : 0); i < get_argument_count(); i++) { + PropertyInfo pi = i == -1 ? get_return_info() : get_argument_info(i); + hash = hash_djb2_one_32(get_argument_type(i), hash); + if (pi.class_name != StringName()) { + hash = hash_djb2_one_32(pi.class_name.operator String().hash(), hash); + } + } +#endif + hash = hash_djb2_one_32(get_default_argument_count(), hash); + for (int i = 0; i < get_default_argument_count(); i++) { + Variant v = get_default_argument(i); + hash = hash_djb2_one_32(v.hash(), hash); + } + + hash = hash_djb2_one_32(is_const(), hash); + hash = hash_djb2_one_32(is_vararg(), hash); + + return hash; +} + #ifdef DEBUG_METHODS_ENABLED PropertyInfo MethodBind::get_argument_info(int p_argument) const { ERR_FAIL_INDEX_V(p_argument, get_argument_count(), PropertyInfo()); diff --git a/core/object/method_bind.h b/core/object/method_bind.h index 7030ae201b..92b964772a 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -135,6 +135,8 @@ public: void set_default_arguments(const Vector<Variant> &p_defargs); + uint32_t get_hash() const; + MethodBind(); virtual ~MethodBind(); }; diff --git a/core/object/object.cpp b/core/object/object.cpp index 00b89ab398..1c8db89e5e 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -386,12 +386,20 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid } if (_extension && _extension->set) { - if (_extension->set(_extension_instance, &p_name, &p_value)) { +// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + if (_extension->set(_extension_instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value)) { if (r_valid) { *r_valid = true; } return; } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif } //try built-in setgetter @@ -459,14 +467,22 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { return ret; } } - if (_extension && _extension->get) { - if (_extension->get(_extension_instance, &p_name, &ret)) { +// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + + if (_extension->get(_extension_instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&ret)) { if (r_valid) { *r_valid = true; } return ret; } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif } //try built-in setgetter @@ -616,7 +632,7 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons if (_extension && _extension->get_property_list) { uint32_t pcount; - const ObjectNativeExtension::PInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount); + const GDNativePropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount); for (uint32_t i = 0; i < pcount; i++) { p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name)); } @@ -1812,7 +1828,7 @@ Object::~Object() { script_instance = nullptr; if (_extension && _extension->free_instance) { - _extension->free_instance(_extension->create_instance_userdata, _extension_instance); + _extension->free_instance(_extension->class_userdata, _extension_instance); _extension = nullptr; _extension_instance = nullptr; } diff --git a/core/object/object.h b/core/object/object.h index 65621a47ca..461ed482fe 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -31,6 +31,7 @@ #ifndef OBJECT_H #define OBJECT_H +#include "core/extension/gdnative_interface.h" #include "core/object/object_id.h" #include "core/os/rw_lock.h" #include "core/os/spin_lock.h" @@ -244,29 +245,18 @@ class MethodBind; struct ObjectNativeExtension { ObjectNativeExtension *parent = nullptr; + List<ObjectNativeExtension *> children; StringName parent_class_name; StringName class_name; bool editor_class = false; - bool (*set)(void *instance, const void *name, const void *value) = nullptr; - bool (*get)(void *instance, const void *name, void *ret_variant) = nullptr; - struct PInfo { - uint32_t type; - const char *name; - const char *class_name; - uint32_t hint; - const char *hint_string; - uint32_t usage; - }; - const PInfo *(*get_property_list)(void *instance, uint32_t *count) = nullptr; - void (*free_property_list)(void *instance, const PInfo *) = nullptr; - - //call is not used, as all methods registered in ClassDB - - void (*notification)(void *instance, int32_t what) = nullptr; - const char *(*to_string)(void *instance) = nullptr; - - void (*reference)(void *instance) = nullptr; - void (*unreference)(void *instance) = nullptr; + GDNativeExtensionClassSet set; + GDNativeExtensionClassGet get; + GDNativeExtensionClassGetPropertyList get_property_list; + GDNativeExtensionClassFreePropertyList free_property_list; + GDNativeExtensionClassNotification notification; + GDNativeExtensionClassToString to_string; + GDNativeExtensionClassReference reference; + GDNativeExtensionClassReference unreference; _FORCE_INLINE_ bool is_class(const String &p_class) const { const ObjectNativeExtension *e = this; @@ -278,11 +268,16 @@ struct ObjectNativeExtension { } return false; } - void *create_instance_userdata = nullptr; - void *(*create_instance)(void *create_instance_userdata) = nullptr; - void (*free_instance)(void *create_instance_userdata, void *instance) = nullptr; + void *class_userdata = nullptr; + + GDNativeExtensionClassCreateInstance create_instance; + GDNativeExtensionClassFreeInstance free_instance; + GDNativeExtensionClassGetVirtual get_virtual; }; +#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call(__VA_ARGS__) +#define GDVIRTUAL_BIND(m_name) ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info()); + /* the following is an incomprehensible blob of hacks and workarounds to compensate for many of the fallencies in C++. As a plus, this macro pretty much alone defines the object model. */ @@ -497,7 +492,7 @@ private: friend void postinitialize_handler(Object *); ObjectNativeExtension *_extension = nullptr; - void *_extension_instance = nullptr; + GDExtensionClassInstancePtr _extension_instance = nullptr; struct SignalData { struct Slot { @@ -554,8 +549,9 @@ private: Object(bool p_reference); protected: + friend class NativeExtensionMethodBind; _ALWAYS_INLINE_ const ObjectNativeExtension *_get_extension() const { return _extension; } - _ALWAYS_INLINE_ void *_get_extension_instance() const { return _extension_instance; } + _ALWAYS_INLINE_ GDExtensionClassInstancePtr _get_extension_instance() const { return _extension_instance; } virtual void _initialize_classv() { initialize_class(); } virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; }; virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; }; diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index 61780eb061..e0af2c18bb 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -258,6 +258,8 @@ struct PtrToArg<Ref<T>> { return Ref<T>(const_cast<T *>(reinterpret_cast<const T *>(p_ptr))); } + typedef Ref<T> EncodeT; + _FORCE_INLINE_ static void encode(Ref<T> p_val, const void *p_ptr) { *(Ref<RefCounted> *)p_ptr = p_val; } @@ -265,6 +267,8 @@ struct PtrToArg<Ref<T>> { template <class T> struct PtrToArg<const Ref<T> &> { + typedef Ref<T> EncodeT; + _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { return Ref<T>((T *)p_ptr); } diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 6ce230b77b..1cbb0bb597 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -37,6 +37,8 @@ #include "core/crypto/aes_context.h" #include "core/crypto/crypto.h" #include "core/crypto/hashing_context.h" +#include "core/extension/native_extension.h" +#include "core/extension/native_extension_manager.h" #include "core/input/input.h" #include "core/input/input_map.h" #include "core/io/config_file.h" @@ -95,6 +97,8 @@ static _Geometry3D *_geometry_3d = nullptr; extern Mutex _global_mutex; +static NativeExtensionManager *native_extension_manager = nullptr; + extern void register_global_constants(); extern void unregister_global_constants(); @@ -217,6 +221,12 @@ void register_core_types() { ClassDB::register_virtual_class<ResourceImporter>(); + ClassDB::register_class<NativeExtension>(); + + ClassDB::register_virtual_class<NativeExtensionManager>(); + + native_extension_manager = memnew(NativeExtensionManager); + ip = IP::create(); _geometry_2d = memnew(_Geometry2D); @@ -261,7 +271,7 @@ void register_core_singletons() { ClassDB::register_class<Time>(); Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton(), "IP")); Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry2D", _Geometry2D::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry3D", _Geometry3D::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceLoader", _ResourceLoader::get_singleton())); @@ -275,9 +285,25 @@ void register_core_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", _EngineDebugger::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("NativeExtensionManager", NativeExtensionManager::get_singleton())); +} + +void register_core_extensions() { + //harcoded for now + if (ProjectSettings::get_singleton()->has_setting("native_extensions/paths")) { + Vector<String> paths = ProjectSettings::get_singleton()->get("native_extensions/paths"); + for (int i = 0; i < paths.size(); i++) { + NativeExtensionManager::LoadStatus status = native_extension_manager->load_extension(paths[i]); + ERR_CONTINUE_MSG(status != NativeExtensionManager::LOAD_STATUS_OK, "Error loading extension: " + paths[i]); + } + } + native_extension_manager->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE); } void unregister_core_types() { + native_extension_manager->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE); + + memdelete(native_extension_manager); memdelete(_resource_loader); memdelete(_resource_saver); memdelete(_os); diff --git a/core/register_core_types.h b/core/register_core_types.h index baf7ddbe65..830f05607d 100644 --- a/core/register_core_types.h +++ b/core/register_core_types.h @@ -33,6 +33,7 @@ void register_core_types(); void register_core_settings(); +void register_core_extensions(); void register_core_singletons(); void unregister_core_types(); diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 0885777429..ef5867c685 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -77,6 +77,7 @@ struct VariantCaster<const T &> { _FORCE_INLINE_ static m_enum convert(const void *p_ptr) { \ return m_enum(*reinterpret_cast<const int *>(p_ptr)); \ } \ + typedef int64_t EncodeT; \ _FORCE_INLINE_ static void encode(m_enum p_val, const void *p_ptr) { \ *(int *)p_ptr = p_val; \ } \ @@ -117,6 +118,7 @@ struct PtrToArg<char32_t> { _FORCE_INLINE_ static char32_t convert(const void *p_ptr) { return char32_t(*reinterpret_cast<const int *>(p_ptr)); } + typedef int64_t EncodeT; _FORCE_INLINE_ static void encode(char32_t p_val, const void *p_ptr) { *(int *)p_ptr = p_val; } diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h index d4ec5e570c..7852187b77 100644 --- a/core/variant/method_ptrcall.h +++ b/core/variant/method_ptrcall.h @@ -45,6 +45,7 @@ struct PtrToArg {}; _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \ return *reinterpret_cast<const m_type *>(p_ptr); \ } \ + typedef m_type EncodeT; \ _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \ *((m_type *)p_ptr) = p_val; \ } \ @@ -54,6 +55,7 @@ struct PtrToArg {}; _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \ return *reinterpret_cast<const m_type *>(p_ptr); \ } \ + typedef m_type EncodeT; \ _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \ *((m_type *)p_ptr) = p_val; \ } \ @@ -65,6 +67,7 @@ struct PtrToArg {}; _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \ return static_cast<m_type>(*reinterpret_cast<const m_conv *>(p_ptr)); \ } \ + typedef m_conv EncodeT; \ _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \ *((m_conv *)p_ptr) = static_cast<m_conv>(p_val); \ } \ @@ -74,6 +77,7 @@ struct PtrToArg {}; _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \ return static_cast<m_type>(*reinterpret_cast<const m_conv *>(p_ptr)); \ } \ + typedef m_conv EncodeT; \ _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \ *((m_conv *)p_ptr) = static_cast<m_conv>(p_val); \ } \ @@ -85,6 +89,7 @@ struct PtrToArg {}; _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \ return *reinterpret_cast<const m_type *>(p_ptr); \ } \ + typedef m_type EncodeT; \ _FORCE_INLINE_ static void encode(const m_type &p_val, void *p_ptr) { \ *((m_type *)p_ptr) = p_val; \ } \ @@ -94,12 +99,13 @@ struct PtrToArg {}; _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \ return *reinterpret_cast<const m_type *>(p_ptr); \ } \ + typedef m_type EncodeT; \ _FORCE_INLINE_ static void encode(const m_type &p_val, void *p_ptr) { \ *((m_type *)p_ptr) = p_val; \ } \ } -MAKE_PTRARG(bool); +MAKE_PTRARGCONV(bool, uint32_t); // Integer types. MAKE_PTRARGCONV(uint8_t, int64_t); MAKE_PTRARGCONV(int8_t, int64_t); @@ -153,7 +159,7 @@ struct PtrToArg<T *> { _FORCE_INLINE_ static T *convert(const void *p_ptr) { return const_cast<T *>(reinterpret_cast<const T *>(p_ptr)); } - + typedef Object *EncodeT; _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { *((T **)p_ptr) = p_var; } @@ -164,7 +170,7 @@ struct PtrToArg<const T *> { _FORCE_INLINE_ static const T *convert(const void *p_ptr) { return reinterpret_cast<const T *>(p_ptr); } - + typedef const Object *EncodeT; _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { *((T **)p_ptr) = p_var; } @@ -177,7 +183,7 @@ struct PtrToArg<ObjectID> { _FORCE_INLINE_ static const ObjectID convert(const void *p_ptr) { return ObjectID(*reinterpret_cast<const uint64_t *>(p_ptr)); } - + typedef uint64_t EncodeT; _FORCE_INLINE_ static void encode(const ObjectID &p_val, void *p_ptr) { *((uint64_t *)p_ptr) = p_val; } diff --git a/core/variant/variant.h b/core/variant/variant.h index 125173ea5c..373fe32921 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -499,6 +499,7 @@ public: static bool is_builtin_method_vararg(Variant::Type p_type, const StringName &p_method); static void get_builtin_method_list(Variant::Type p_type, List<StringName> *p_list); static int get_builtin_method_count(Variant::Type p_type); + static uint32_t get_builtin_method_hash(Variant::Type p_type, const StringName &p_method); void call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error); Variant call(const StringName &p_method, const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant()); @@ -586,7 +587,7 @@ public: typedef void (*PTRKeyedSetter)(void *base, const void *key, const void *value); typedef void (*PTRKeyedGetter)(const void *base, const void *key, void *value); - typedef bool (*PTRKeyedChecker)(const void *base, const void *key); + typedef uint32_t (*PTRKeyedChecker)(const void *base, const void *key); static PTRKeyedSetter get_member_ptr_keyed_setter(Variant::Type p_type); static PTRKeyedGetter get_member_ptr_keyed_getter(Variant::Type p_type); @@ -631,6 +632,7 @@ public: static bool has_utility_function_return_value(const StringName &p_name); static Variant::Type get_utility_function_return_type(const StringName &p_name); static bool is_utility_function_vararg(const StringName &p_name); + static uint32_t get_utility_function_hash(const StringName &p_name); static void get_utility_function_list(List<StringName> *r_functions); static int get_utility_function_count(); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 9c7cd23d72..95c4e7121b 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1126,6 +1126,25 @@ bool Variant::is_builtin_method_vararg(Variant::Type p_type, const StringName &p return method->is_vararg; } +uint32_t Variant::get_builtin_method_hash(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_COND_V(!method, 0); + uint32_t hash = hash_djb2_one_32(method->is_const); + hash = hash_djb2_one_32(method->is_static, hash); + hash = hash_djb2_one_32(method->is_vararg, hash); + hash = hash_djb2_one_32(method->has_return_type, hash); + if (method->has_return_type) { + hash = hash_djb2_one_32(method->return_type, hash); + } + hash = hash_djb2_one_32(method->argument_count, hash); + for (int i = 0; i < method->argument_count; i++) { + hash = method->get_argument_type(i); + } + + return hash; +} + void Variant::get_method_list(List<MethodInfo> *p_list) const { if (type == OBJECT) { Object *obj = get_validated_object(); diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index 4a1950c9bf..16c7428781 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -661,8 +661,8 @@ static const char *_op_names[Variant::OP_MAX] = { "-", "*", "/", - "-", - "+", + "unary-", + "unary+", "%", "<<", ">>", diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index 5cb329e69a..de1deace63 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -871,7 +871,7 @@ struct VariantKeyedSetGetDictionary { *r_valid = true; return VariantGetInternalPtr<Dictionary>::get_ptr(base)->has(*key); } - static bool ptr_has(const void *base, const void *key) { + static uint32_t ptr_has(const void *base, const void *key) { /* avoid ptrconvert for performance*/ const Dictionary &v = *reinterpret_cast<const Dictionary *>(base); return v.has(PtrToArg<Variant>::convert(key)); @@ -921,7 +921,7 @@ struct VariantKeyedSetGetObject { obj->getvar(*key, &exists); return exists; } - static bool ptr_has(const void *base, const void *key) { + static uint32_t ptr_has(const void *base, const void *key) { const Object *obj = PtrToArg<Object *>::convert(base); ERR_FAIL_COND_V(!obj, false); bool valid; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 5d1efb4166..1f69e81d99 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -1348,8 +1348,8 @@ String Variant::get_utility_function_argument_name(const StringName &p_name, int if (!bfi) { return String(); } - ERR_FAIL_COND_V(bfi->is_vararg, String()); ERR_FAIL_INDEX_V(p_arg, bfi->argnames.size(), String()); + ERR_FAIL_COND_V(bfi->is_vararg, String()); return bfi->argnames[p_arg]; } @@ -1379,6 +1379,23 @@ bool Variant::is_utility_function_vararg(const StringName &p_name) { return bfi->is_vararg; } +uint32_t Variant::get_utility_function_hash(const StringName &p_name) { + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + ERR_FAIL_COND_V(!bfi, 0); + + uint32_t hash = hash_djb2_one_32(bfi->is_vararg); + hash = hash_djb2_one_32(bfi->returns_value, hash); + if (bfi->returns_value) { + hash = hash_djb2_one_32(bfi->return_type, hash); + } + hash = hash_djb2_one_32(bfi->argcount, hash); + for (int i = 0; i < bfi->argcount; i++) { + hash = hash_djb2_one_32(bfi->get_arg_type(i), hash); + } + + return hash; +} + void Variant::get_utility_function_list(List<StringName> *r_functions) { for (List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) { r_functions->push_back(E->get()); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index fa118bec54..d0e594a78b 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1224,6 +1224,8 @@ <member name="Marshalls" type="Marshalls" setter="" getter=""> The [Marshalls] singleton. </member> + <member name="NativeExtensionManager" type="NativeExtensionManager" setter="" getter=""> + </member> <member name="NavigationMeshGenerator" type="NavigationMeshGenerator" setter="" getter=""> The [NavigationMeshGenerator] singleton. </member> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 29c21b3213..7b1415e40d 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -80,14 +80,16 @@ </argument> <argument index="2" name="b" type="float"> </argument> + <argument index="3" name="a" type="float"> + </argument> <description> - Constructs a [Color] from RGB values, typically between 0 and 1. Alpha will be 1. + Constructs a [Color] from RGBA values, typically between 0 and 1. [codeblocks] [gdscript] - var color = Color(0.2, 1.0, 0.7) # Similar to `Color8(51, 255, 178, 255)` + var color = Color(0.2, 1.0, 0.7, 0.8) # Similar to `Color8(51, 255, 178, 204)` [/gdscript] [csharp] - var color = new Color(0.2f, 1.0f, 0.7f); // Similar to `Color.Color8(51, 255, 178, 255)` + var color = new Color(0.2f, 1.0f, 0.7f, 0.8f); // Similar to `Color.Color8(51, 255, 178, 255, 204)` [/csharp] [/codeblocks] </description> @@ -101,16 +103,14 @@ </argument> <argument index="2" name="b" type="float"> </argument> - <argument index="3" name="a" type="float"> - </argument> <description> - Constructs a [Color] from RGBA values, typically between 0 and 1. + Constructs a [Color] from RGB values, typically between 0 and 1. Alpha will be 1. [codeblocks] [gdscript] - var color = Color(0.2, 1.0, 0.7, 0.8) # Similar to `Color8(51, 255, 178, 204)` + var color = Color(0.2, 1.0, 0.7) # Similar to `Color8(51, 255, 178, 255)` [/gdscript] [csharp] - var color = new Color(0.2f, 1.0f, 0.7f, 0.8f); // Similar to `Color.Color8(51, 255, 178, 255, 204)` + var color = new Color(0.2f, 1.0f, 0.7f); // Similar to `Color.Color8(51, 255, 178, 255)` [/csharp] [/codeblocks] </description> @@ -349,12 +349,6 @@ <method name="operator +" qualifiers="operator"> <return type="Color"> </return> - <description> - </description> - </method> - <method name="operator +" qualifiers="operator"> - <return type="Color"> - </return> <argument index="0" name="right" type="Color"> </argument> <description> @@ -363,12 +357,6 @@ <method name="operator -" qualifiers="operator"> <return type="Color"> </return> - <description> - </description> - </method> - <method name="operator -" qualifiers="operator"> - <return type="Color"> - </return> <argument index="0" name="right" type="Color"> </argument> <description> @@ -414,6 +402,18 @@ <description> </description> </method> + <method name="operator unary+" qualifiers="operator"> + <return type="Color"> + </return> + <description> + </description> + </method> + <method name="operator unary-" qualifiers="operator"> + <return type="Color"> + </return> + <description> + </description> + </method> <method name="to_abgr32" qualifiers="const"> <return type="int"> </return> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 9addd5965a..2692108d94 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -50,14 +50,6 @@ [/codeblocks] </description> </method> - <method name="_clips_input" qualifiers="virtual"> - <return type="bool"> - </return> - <description> - Virtual method to be implemented by the user. Returns whether [method _gui_input] should not be called for children controls outside this control's rectangle. Input will be clipped to the Rect of this [Control]. Similar to [member rect_clip_content], but doesn't affect visibility. - If not overridden, defaults to [code]false[/code]. - </description> - </method> <method name="_drop_data" qualifiers="virtual"> <return type="void"> </return> @@ -155,13 +147,13 @@ * control has [member mouse_filter] set to [constant MOUSE_FILTER_IGNORE]; * control is obstructed by another [Control] on top of it, which doesn't have [member mouse_filter] set to [constant MOUSE_FILTER_IGNORE]; * control's parent has [member mouse_filter] set to [constant MOUSE_FILTER_STOP] or has accepted the event; - * it happens outside parent's rectangle and the parent has either [member rect_clip_content] or [method _clips_input] enabled. + * it happens outside parent's rectangle and the parent has either [member rect_clip_content] enabled. </description> </method> - <method name="_has_point" qualifiers="virtual"> + <method name="_has_point" qualifiers="virtual const"> <return type="bool"> </return> - <argument index="0" name="point" type="Vector2"> + <argument index="0" name="" type="Vector2"> </argument> <description> Virtual method to be implemented by the user. Returns whether the given [code]point[/code] is inside this control. @@ -1135,7 +1127,7 @@ Offsets are often controlled by one or multiple parent [Container] nodes, so you should not modify them manually if your node is a direct child of a [Container]. Offsets update automatically when you move or resize the node. </member> <member name="rect_clip_content" type="bool" setter="set_clip_contents" getter="is_clipping_contents" default="false"> - Enables whether rendering of [CanvasItem] based children should be clipped to this control's rectangle. If [code]true[/code], parts of a child which would be visibly outside of this control's rectangle will not be rendered. + Enables whether rendering of [CanvasItem] based children should be clipped to this control's rectangle. If [code]true[/code], parts of a child which would be visibly outside of this control's rectangle will not be rendered and won't receive input. </member> <member name="rect_global_position" type="Vector2" setter="_set_global_position" getter="get_global_position"> The node's global position, relative to the world (usually to the top-left corner of the window). diff --git a/doc/classes/NativeExtension.xml b/doc/classes/NativeExtension.xml new file mode 100644 index 0000000000..c48af7df7b --- /dev/null +++ b/doc/classes/NativeExtension.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="NativeExtension" inherits="RefCounted" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="close_library"> + <return type="void"> + </return> + <description> + </description> + </method> + <method name="get_minimum_library_initialization_level" qualifiers="const"> + <return type="int" enum="NativeExtension.InitializationLevel"> + </return> + <description> + </description> + </method> + <method name="initialize_library"> + <return type="void"> + </return> + <argument index="0" name="level" type="int" enum="NativeExtension.InitializationLevel"> + </argument> + <description> + </description> + </method> + <method name="is_library_open" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> + <method name="open_library"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <argument index="1" name="entry_symbol" type="String"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + <constant name="INITIALIZATION_LEVEL_CORE" value="0" enum="InitializationLevel"> + </constant> + <constant name="INITIALIZATION_LEVEL_SERVERS" value="1" enum="InitializationLevel"> + </constant> + <constant name="INITIALIZATION_LEVEL_SCENE" value="2" enum="InitializationLevel"> + </constant> + <constant name="INITIALIZATION_LEVEL_EDITOR" value="3" enum="InitializationLevel"> + </constant> + </constants> +</class> diff --git a/doc/classes/NativeExtensionManager.xml b/doc/classes/NativeExtensionManager.xml new file mode 100644 index 0000000000..ba9018ff4c --- /dev/null +++ b/doc/classes/NativeExtensionManager.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="NativeExtensionManager" inherits="Object" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_extension"> + <return type="NativeExtension"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + </description> + </method> + <method name="get_loaded_extensions" qualifiers="const"> + <return type="PackedStringArray"> + </return> + <description> + </description> + </method> + <method name="load_extension"> + <return type="int" enum="NativeExtensionManager.LoadStatus"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + </description> + </method> + <method name="reload_extension"> + <return type="int" enum="NativeExtensionManager.LoadStatus"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + </description> + </method> + <method name="unload_extension"> + <return type="int" enum="NativeExtensionManager.LoadStatus"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + <constant name="LOAD_STATUS_OK" value="0" enum="LoadStatus"> + </constant> + <constant name="LOAD_STATUS_FAILED" value="1" enum="LoadStatus"> + </constant> + <constant name="LOAD_STATUS_ALREADY_LOADED" value="2" enum="LoadStatus"> + </constant> + <constant name="LOAD_STATUS_NOT_LOADED" value="3" enum="LoadStatus"> + </constant> + <constant name="LOAD_STATUS_NEEDS_RESTART" value="4" enum="LoadStatus"> + </constant> + </constants> +</class> diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml index cca5793fc7..12869061c4 100644 --- a/doc/classes/Plane.xml +++ b/doc/classes/Plane.xml @@ -169,23 +169,23 @@ <description> </description> </method> - <method name="operator +" qualifiers="operator"> - <return type="Plane"> + <method name="operator ==" qualifiers="operator"> + <return type="bool"> </return> + <argument index="0" name="right" type="Plane"> + </argument> <description> </description> </method> - <method name="operator -" qualifiers="operator"> + <method name="operator unary+" qualifiers="operator"> <return type="Plane"> </return> <description> </description> </method> - <method name="operator ==" qualifiers="operator"> - <return type="bool"> + <method name="operator unary-" qualifiers="operator"> + <return type="Plane"> </return> - <argument index="0" name="right" type="Plane"> - </argument> <description> </description> </method> diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml index 660204ee7d..06434ab268 100644 --- a/doc/classes/Quaternion.xml +++ b/doc/classes/Quaternion.xml @@ -177,17 +177,17 @@ </description> </method> <method name="operator *" qualifiers="operator"> - <return type="Quaternion"> + <return type="Vector3"> </return> - <argument index="0" name="right" type="Quaternion"> + <argument index="0" name="right" type="Vector3"> </argument> <description> </description> </method> <method name="operator *" qualifiers="operator"> - <return type="Vector3"> + <return type="Quaternion"> </return> - <argument index="0" name="right" type="Vector3"> + <argument index="0" name="right" type="Quaternion"> </argument> <description> </description> @@ -211,12 +211,6 @@ <method name="operator +" qualifiers="operator"> <return type="Quaternion"> </return> - <description> - </description> - </method> - <method name="operator +" qualifiers="operator"> - <return type="Quaternion"> - </return> <argument index="0" name="right" type="Quaternion"> </argument> <description> @@ -225,12 +219,6 @@ <method name="operator -" qualifiers="operator"> <return type="Quaternion"> </return> - <description> - </description> - </method> - <method name="operator -" qualifiers="operator"> - <return type="Quaternion"> - </return> <argument index="0" name="right" type="Quaternion"> </argument> <description> @@ -268,6 +256,18 @@ <description> </description> </method> + <method name="operator unary+" qualifiers="operator"> + <return type="Quaternion"> + </return> + <description> + </description> + </method> + <method name="operator unary-" qualifiers="operator"> + <return type="Quaternion"> + </return> + <description> + </description> + </method> <method name="slerp" qualifiers="const"> <return type="Quaternion"> </return> diff --git a/doc/classes/RDFramebufferPass.xml b/doc/classes/RDFramebufferPass.xml new file mode 100644 index 0000000000..c26c41f93f --- /dev/null +++ b/doc/classes/RDFramebufferPass.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="RDFramebufferPass" inherits="RefCounted" version="4.0"> + <brief_description> + Framebuffer pass attachment description. + </brief_description> + <description> + This class contains the list of attachment descriptions for a framebuffer pass. Each points with an index to a previously supplied list of texture attachments. + Multipass framebuffers can optimize some configurations in mobile, on desktop they provide little to no advantage. + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="color_attachments" type="PackedInt32Array" setter="set_color_attachments" getter="get_color_attachments" default="PackedInt32Array()"> + Color attachments in order starting from 0. If this attachment is not used by the shader, pass ATTACHMENT_UNUSED to skip. + </member> + <member name="depth_attachment" type="int" setter="set_depth_attachment" getter="get_depth_attachment" default="-1"> + Depth attachment. ATTACHMENT_UNUSED should be used if no depth buffer is required for this pass. + </member> + <member name="input_attachments" type="PackedInt32Array" setter="set_input_attachments" getter="get_input_attachments" default="PackedInt32Array()"> + Used for multipass framebuffers (more than one render pass). Converts an attachment to an input. Make sure to also supply it properly in the [RDUniform] for the uniform set. + </member> + <member name="preserve_attachments" type="PackedInt32Array" setter="set_preserve_attachments" getter="get_preserve_attachments" default="PackedInt32Array()"> + Attachments to preserve in this pass (otherwise they are erased). + </member> + <member name="resolve_attachments" type="PackedInt32Array" setter="set_resolve_attachments" getter="get_resolve_attachments" default="PackedInt32Array()"> + If the color attachments are multisampled, non-multisampled resolve attachments can be provided. + </member> + </members> + <constants> + <constant name="ATTACHMENT_UNUSED" value="-1"> + </constant> + </constants> +</class> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index dc56e6fd5d..b66b945025 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -341,13 +341,29 @@ <description> </description> </method> + <method name="draw_list_switch_to_next_pass"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="draw_list_switch_to_next_pass_split"> + <return type="PackedInt64Array"> + </return> + <argument index="0" name="splits" type="int"> + </argument> + <description> + </description> + </method> <method name="framebuffer_create"> <return type="RID"> </return> - <argument index="0" name="textures" type="Array"> + <argument index="0" name="textures" type="RID[]"> </argument> <argument index="1" name="validate_with_format" type="int" default="-1"> </argument> + <argument index="2" name="view_count" type="int" default="1"> + </argument> <description> </description> </method> @@ -363,11 +379,27 @@ <description> </description> </method> + <method name="framebuffer_create_multipass"> + <return type="RID"> + </return> + <argument index="0" name="textures" type="RID[]"> + </argument> + <argument index="1" name="passes" type="RDFramebufferPass[]"> + </argument> + <argument index="2" name="validate_with_format" type="int" default="-1"> + </argument> + <argument index="3" name="view_count" type="int" default="1"> + </argument> + <description> + </description> + </method> <method name="framebuffer_format_create"> <return type="int"> </return> <argument index="0" name="attachments" type="RDAttachmentFormat[]"> </argument> + <argument index="1" name="view_count" type="int" default="1"> + </argument> <description> </description> </method> @@ -379,11 +411,25 @@ <description> </description> </method> + <method name="framebuffer_format_create_multipass"> + <return type="int"> + </return> + <argument index="0" name="attachments" type="RDAttachmentFormat[]"> + </argument> + <argument index="1" name="passes" type="RDFramebufferPass[]"> + </argument> + <argument index="2" name="view_count" type="int" default="1"> + </argument> + <description> + </description> + </method> <method name="framebuffer_format_get_texture_samples"> <return type="int" enum="RenderingDevice.TextureSamples"> </return> <argument index="0" name="format" type="int"> </argument> + <argument index="1" name="render_pass" type="int" default="0"> + </argument> <description> </description> </method> @@ -524,6 +570,8 @@ </argument> <argument index="8" name="dynamic_state_flags" type="int" default="0"> </argument> + <argument index="9" name="for_render_pass" type="int" default="0"> + </argument> <description> </description> </method> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index 07d09c31dc..498aefbef0 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -304,12 +304,6 @@ <method name="operator +" qualifiers="operator"> <return type="Vector2"> </return> - <description> - </description> - </method> - <method name="operator +" qualifiers="operator"> - <return type="Vector2"> - </return> <argument index="0" name="right" type="Vector2"> </argument> <description> @@ -318,12 +312,6 @@ <method name="operator -" qualifiers="operator"> <return type="Vector2"> </return> - <description> - </description> - </method> - <method name="operator -" qualifiers="operator"> - <return type="Vector2"> - </return> <argument index="0" name="right" type="Vector2"> </argument> <description> @@ -401,6 +389,18 @@ <description> </description> </method> + <method name="operator unary+" qualifiers="operator"> + <return type="Vector2"> + </return> + <description> + </description> + </method> + <method name="operator unary-" qualifiers="operator"> + <return type="Vector2"> + </return> + <description> + </description> + </method> <method name="orthogonal" qualifiers="const"> <return type="Vector2"> </return> diff --git a/doc/classes/Vector2i.xml b/doc/classes/Vector2i.xml index 930ec944ba..5f190de8ca 100644 --- a/doc/classes/Vector2i.xml +++ b/doc/classes/Vector2i.xml @@ -126,12 +126,6 @@ <method name="operator +" qualifiers="operator"> <return type="Vector2i"> </return> - <description> - </description> - </method> - <method name="operator +" qualifiers="operator"> - <return type="Vector2i"> - </return> <argument index="0" name="right" type="Vector2i"> </argument> <description> @@ -140,12 +134,6 @@ <method name="operator -" qualifiers="operator"> <return type="Vector2i"> </return> - <description> - </description> - </method> - <method name="operator -" qualifiers="operator"> - <return type="Vector2i"> - </return> <argument index="0" name="right" type="Vector2i"> </argument> <description> @@ -223,6 +211,18 @@ <description> </description> </method> + <method name="operator unary+" qualifiers="operator"> + <return type="Vector2i"> + </return> + <description> + </description> + </method> + <method name="operator unary-" qualifiers="operator"> + <return type="Vector2i"> + </return> + <description> + </description> + </method> <method name="sign" qualifiers="const"> <return type="Vector2i"> </return> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index eb1fd5f098..1361666c18 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -318,12 +318,6 @@ <method name="operator +" qualifiers="operator"> <return type="Vector3"> </return> - <description> - </description> - </method> - <method name="operator +" qualifiers="operator"> - <return type="Vector3"> - </return> <argument index="0" name="right" type="Vector3"> </argument> <description> @@ -332,12 +326,6 @@ <method name="operator -" qualifiers="operator"> <return type="Vector3"> </return> - <description> - </description> - </method> - <method name="operator -" qualifiers="operator"> - <return type="Vector3"> - </return> <argument index="0" name="right" type="Vector3"> </argument> <description> @@ -415,6 +403,18 @@ <description> </description> </method> + <method name="operator unary+" qualifiers="operator"> + <return type="Vector3"> + </return> + <description> + </description> + </method> + <method name="operator unary-" qualifiers="operator"> + <return type="Vector3"> + </return> + <description> + </description> + </method> <method name="outer" qualifiers="const"> <return type="Basis"> </return> diff --git a/doc/classes/Vector3i.xml b/doc/classes/Vector3i.xml index 8b45a62afa..e08bafa665 100644 --- a/doc/classes/Vector3i.xml +++ b/doc/classes/Vector3i.xml @@ -134,12 +134,6 @@ <method name="operator +" qualifiers="operator"> <return type="Vector3i"> </return> - <description> - </description> - </method> - <method name="operator +" qualifiers="operator"> - <return type="Vector3i"> - </return> <argument index="0" name="right" type="Vector3i"> </argument> <description> @@ -148,12 +142,6 @@ <method name="operator -" qualifiers="operator"> <return type="Vector3i"> </return> - <description> - </description> - </method> - <method name="operator -" qualifiers="operator"> - <return type="Vector3i"> - </return> <argument index="0" name="right" type="Vector3i"> </argument> <description> @@ -231,6 +219,18 @@ <description> </description> </method> + <method name="operator unary+" qualifiers="operator"> + <return type="Vector3i"> + </return> + <description> + </description> + </method> + <method name="operator unary-" qualifiers="operator"> + <return type="Vector3i"> + </return> + <description> + </description> + </method> <method name="sign" qualifiers="const"> <return type="Vector3i"> </return> diff --git a/doc/classes/float.xml b/doc/classes/float.xml index f75c130039..585c847d22 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -145,16 +145,6 @@ <method name="operator +" qualifiers="operator"> <return type="float"> </return> - <description> - Unary plus operator. Doesn't have any effect. - [codeblock] - var a = +2.5 # a is 2.5. - [/codeblock] - </description> - </method> - <method name="operator +" qualifiers="operator"> - <return type="float"> - </return> <argument index="0" name="right" type="float"> </argument> <description> @@ -173,17 +163,6 @@ <method name="operator -" qualifiers="operator"> <return type="float"> </return> - <description> - Unary minus operator. Negates the number. - [codeblock] - var a = -2.5 # a is -2.5. - print(-a) # 2.5 - [/codeblock] - </description> - </method> - <method name="operator -" qualifiers="operator"> - <return type="float"> - </return> <argument index="0" name="right" type="float"> </argument> <description> @@ -308,6 +287,18 @@ Returns [code]true[/code] if this [float] is greater than or equal to the given [int]. </description> </method> + <method name="operator unary+" qualifiers="operator"> + <return type="float"> + </return> + <description> + </description> + </method> + <method name="operator unary-" qualifiers="operator"> + <return type="float"> + </return> + <description> + </description> + </method> </methods> <constants> </constants> diff --git a/doc/classes/int.xml b/doc/classes/int.xml index b0ad963998..95918c9007 100644 --- a/doc/classes/int.xml +++ b/doc/classes/int.xml @@ -126,21 +126,21 @@ </description> </method> <method name="operator *" qualifiers="operator"> - <return type="float"> + <return type="int"> </return> - <argument index="0" name="right" type="float"> + <argument index="0" name="right" type="int"> </argument> <description> - Multiplies an [int] and a [float]. The result is a [float]. + Multiplies two [int]s. </description> </method> <method name="operator *" qualifiers="operator"> - <return type="int"> + <return type="float"> </return> - <argument index="0" name="right" type="int"> + <argument index="0" name="right" type="float"> </argument> <description> - Multiplies two [int]s. + Multiplies an [int] and a [float]. The result is a [float]. </description> </method> <method name="operator *" qualifiers="operator"> @@ -204,16 +204,6 @@ </description> </method> <method name="operator +" qualifiers="operator"> - <return type="int"> - </return> - <description> - Unary plus operator. Doesn't have any effect. - [codeblock] - var a = +1 # a is 1. - [/codeblock] - </description> - </method> - <method name="operator +" qualifiers="operator"> <return type="float"> </return> <argument index="0" name="right" type="float"> @@ -232,17 +222,6 @@ </description> </method> <method name="operator -" qualifiers="operator"> - <return type="int"> - </return> - <description> - Unary minus operator. Negates the number. - [codeblock] - var a = -1 # a is -1. - print(-a) # 1 - [/codeblock] - </description> - </method> - <method name="operator -" qualifiers="operator"> <return type="float"> </return> <argument index="0" name="right" type="float"> @@ -414,6 +393,18 @@ [/codeblock] </description> </method> + <method name="operator unary+" qualifiers="operator"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="operator unary-" qualifiers="operator"> + <return type="int"> + </return> + <description> + </description> + </method> <method name="operator |" qualifiers="operator"> <return type="int"> </return> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index c69b516f37..1539962803 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -3244,12 +3244,7 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f /**** ATTACHMENT ****/ /********************/ -VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count, uint32_t p_view_count) { - Vector<VkAttachmentDescription> attachments; - Vector<VkAttachmentReference> color_references; - Vector<VkAttachmentReference> depth_stencil_references; - Vector<VkAttachmentReference> resolve_references; - +VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count, Vector<TextureSamples> *r_samples) { // Set up dependencies from/to external equivalent to the default (implicit) one, and then amend them const VkPipelineStageFlags default_access_mask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | @@ -3262,27 +3257,31 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF { 0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, default_access_mask, 0, 0 } }; VkSubpassDependency &dependency_from_external = dependencies[0]; VkSubpassDependency &dependency_to_external = dependencies[1]; + LocalVector<int32_t> attachment_last_pass; + attachment_last_pass.resize(p_attachments.size()); - for (int i = 0; i < p_format.size(); i++) { - ERR_FAIL_INDEX_V(p_format[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); - ERR_FAIL_INDEX_V(p_format[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); - ERR_FAIL_COND_V_MSG(!(p_format[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)), + Vector<VkAttachmentDescription> attachments; + + for (int i = 0; i < p_attachments.size(); i++) { + ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); + ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); + ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)), VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (depth, stencil or resolve) bit set."); VkAttachmentDescription description = {}; description.flags = 0; - description.format = vulkan_formats[p_format[i].format]; - description.samples = rasterization_sample_count[p_format[i].samples]; + description.format = vulkan_formats[p_attachments[i].format]; + description.samples = rasterization_sample_count[p_attachments[i].samples]; - bool is_depth_stencil = p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - bool is_sampled = p_format[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; - bool is_storage = p_format[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; + bool is_sampled = p_attachments[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; + bool is_storage = p_attachments[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; + bool is_depth = p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution synchronization vs. // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that // stage - switch (is_depth_stencil ? p_initial_depth_action : p_initial_color_action) { + switch (is_depth ? p_initial_depth_action : p_initial_action) { case INITIAL_ACTION_CLEAR_REGION: case INITIAL_ACTION_CLEAR: { description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -3291,11 +3290,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF dependency_from_external.srcStageMask |= reading_stages; } break; case INITIAL_ACTION_KEEP: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; @@ -3308,11 +3307,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case INITIAL_ACTION_DROP: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; @@ -3326,11 +3325,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } break; case INITIAL_ACTION_CLEAR_REGION_CONTINUE: case INITIAL_ACTION_CONTINUE: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; @@ -3346,14 +3345,14 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } - switch (is_depth_stencil ? p_final_depth_action : p_final_color_action) { + switch (is_depth ? p_final_depth_action : p_final_action) { case FINAL_ACTION_READ: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -3366,11 +3365,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case FINAL_ACTION_DISCARD: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -3381,11 +3380,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case FINAL_ACTION_CONTINUE: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; description.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; @@ -3401,27 +3400,191 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } + attachment_last_pass[i] = -1; + attachments.push_back(description); + } + + LocalVector<VkSubpassDescription> subpasses; + LocalVector<LocalVector<VkAttachmentReference>> color_reference_array; + LocalVector<LocalVector<VkAttachmentReference>> input_reference_array; + LocalVector<LocalVector<VkAttachmentReference>> resolve_reference_array; + LocalVector<LocalVector<uint32_t>> preserve_reference_array; + LocalVector<VkAttachmentReference> depth_reference_array; - VkAttachmentReference reference; - reference.attachment = i; + subpasses.resize(p_passes.size()); + color_reference_array.resize(p_passes.size()); + input_reference_array.resize(p_passes.size()); + resolve_reference_array.resize(p_passes.size()); + preserve_reference_array.resize(p_passes.size()); + depth_reference_array.resize(p_passes.size()); - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + LocalVector<VkSubpassDependency> subpass_dependencies; + + for (int i = 0; i < p_passes.size(); i++) { + const FramebufferPass *pass = &p_passes[i]; + + LocalVector<VkAttachmentReference> &color_references = color_reference_array[i]; + + TextureSamples texture_samples = TEXTURE_SAMPLES_1; + bool is_multisample_first = true; + + for (int j = 0; j < pass->color_attachments.size(); j++) { + int32_t attachment = pass->color_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), color attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as color attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + + if (is_multisample_first) { + texture_samples = p_attachments[attachment].samples; + is_multisample_first = false; + } else { + ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples."); + } + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachment_last_pass[attachment] = i; + } color_references.push_back(reference); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depth_stencil_references.push_back(reference); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + LocalVector<VkAttachmentReference> &input_references = input_reference_array[i]; + + for (int j = 0; j < pass->input_attachments.size(); j++) { + int32_t attachment = pass->input_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), input attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as input attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment_last_pass[attachment] = i; + } + input_references.push_back(reference); + } + + LocalVector<VkAttachmentReference> &resolve_references = resolve_reference_array[i]; + + if (pass->resolve_attachments.size() > 0) { + ERR_FAIL_COND_V_MSG(pass->resolve_attachments.size() != pass->color_attachments.size(), VK_NULL_HANDLE, "The amount of resolve attachments (" + itos(pass->resolve_attachments.size()) + ") must match the number of color attachments (" + itos(pass->color_attachments.size()) + ")."); + ERR_FAIL_COND_V_MSG(texture_samples == TEXTURE_SAMPLES_1, VK_NULL_HANDLE, "Resolve attachments specified, but color attachments are not multisample."); + } + for (int j = 0; j < pass->resolve_attachments.size(); j++) { + int32_t attachment = pass->resolve_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(pass->color_attachments[j] == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + "), the respective color attachment is marked as unused."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as resolve attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + bool multisample = p_attachments[attachment].samples > TEXTURE_SAMPLES_1; + ERR_FAIL_COND_V_MSG(multisample, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample."); + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment_last_pass[attachment] = i; + } resolve_references.push_back(reference); - // if resolves are done, we need to ensure the copy is safe - dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; - dependency_to_external.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; + } + + LocalVector<uint32_t> &preserve_references = preserve_reference_array[i]; + + for (int j = 0; j < pass->preserve_attachments.size(); j++) { + int32_t attachment = pass->preserve_attachments[j]; + + ERR_FAIL_COND_V_MSG(attachment == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + "). Preserve attachments can't be unused."); + + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + + attachment_last_pass[attachment] = i; + + preserve_references.push_back(attachment); + } + + VkAttachmentReference &depth_stencil_reference = depth_reference_array[i]; + + if (pass->depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { + int32_t attachment = pass->depth_attachment; + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), depth attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not a depth attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + depth_stencil_reference.attachment = attachment; + depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachment_last_pass[attachment] = i; + + if (!is_multisample_first) { + ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples including the depth."); + } + + } else { + depth_stencil_reference.attachment = VK_ATTACHMENT_UNUSED; + depth_stencil_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + VkSubpassDescription &subpass = subpasses[i]; + subpass.flags = 0; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.inputAttachmentCount = input_references.size(); + if (input_references.size()) { + subpass.pInputAttachments = input_references.ptr(); + } else { + subpass.pInputAttachments = nullptr; + } + subpass.colorAttachmentCount = color_references.size(); + if (color_references.size()) { + subpass.pColorAttachments = color_references.ptr(); + } else { + subpass.pColorAttachments = nullptr; + } + if (depth_stencil_reference.attachment != VK_ATTACHMENT_UNUSED) { + subpass.pDepthStencilAttachment = &depth_stencil_reference; } else { - ERR_FAIL_V_MSG(VK_NULL_HANDLE, "Texture index " + itos(i) + " is neither color, depth stencil or resolve so it can't be used as attachment."); + subpass.pDepthStencilAttachment = nullptr; } + if (resolve_references.size()) { + subpass.pResolveAttachments = resolve_references.ptr(); + } else { + subpass.pResolveAttachments = nullptr; + } + + subpass.preserveAttachmentCount = preserve_references.size(); + if (preserve_references.size()) { + subpass.pPreserveAttachments = preserve_references.ptr(); + } else { + subpass.pPreserveAttachments = nullptr; + } + + if (r_samples) { + r_samples->push_back(texture_samples); + } + + if (i > 0) { + VkSubpassDependency dependency; + dependency.srcSubpass = i - 1; + dependency.dstSubpass = i; + dependency.srcStageMask = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + + dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + subpass_dependencies.push_back(dependency); + } + /* // NOTE: Big Mallet Approach -- any layout transition causes a full barrier if (reference.layout != description.initialLayout) { // NOTE: this should be smarter based on the texture's knowledge of its previous role @@ -3433,41 +3596,28 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; dependency_to_external.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; } + */ } - ERR_FAIL_COND_V_MSG(depth_stencil_references.size() > 1, VK_NULL_HANDLE, - "Formats can only have one depth/stencil attachment, supplied (" + itos(depth_stencil_references.size()) + ")."); - - ERR_FAIL_COND_V_MSG(resolve_references.size() > 1, VK_NULL_HANDLE, - "Formats can only have one resolve attachment, supplied (" + itos(resolve_references.size()) + ")."); - - VkSubpassDescription subpass; - subpass.flags = 0; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.inputAttachmentCount = 0; //unsupported for now - subpass.pInputAttachments = nullptr; - subpass.colorAttachmentCount = color_references.size(); - subpass.pColorAttachments = color_references.ptr(); - subpass.pDepthStencilAttachment = depth_stencil_references.ptr(); - subpass.pResolveAttachments = resolve_references.ptr(); - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - VkRenderPassCreateInfo render_pass_create_info; render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_create_info.pNext = nullptr; render_pass_create_info.flags = 0; render_pass_create_info.attachmentCount = attachments.size(); render_pass_create_info.pAttachments = attachments.ptr(); - render_pass_create_info.subpassCount = 1; - render_pass_create_info.pSubpasses = &subpass; + render_pass_create_info.subpassCount = subpasses.size(); + render_pass_create_info.pSubpasses = subpasses.ptr(); // Commenting this because it seems it just avoids raster and compute to work at the same time. // Other barriers seem to be protecting the render pass fine. // render_pass_create_info.dependencyCount = 2; // render_pass_create_info.pDependencies = dependencies; - render_pass_create_info.dependencyCount = 0; - render_pass_create_info.pDependencies = nullptr; + render_pass_create_info.dependencyCount = subpass_dependencies.size(); + if (subpass_dependencies.size()) { + render_pass_create_info.pDependencies = subpass_dependencies.ptr(); + } else { + render_pass_create_info.pDependencies = nullptr; + } const uint32_t view_mask = (1 << p_view_count) - 1; const uint32_t correlation_mask = (1 << p_view_count) - 1; @@ -3498,17 +3648,29 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass failed with error " + itos(res) + "."); - if (r_color_attachment_count) { - *r_color_attachment_count = color_references.size(); - } return render_pass; } RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) { + FramebufferPass pass; + for (int i = 0; i < p_format.size(); i++) { + if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + pass.depth_attachment = i; + } else { + pass.color_attachments.push_back(i); + } + } + + Vector<FramebufferPass> passes; + passes.push_back(pass); + return framebuffer_format_create_multipass(p_format, passes, p_view_count); +} +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ FramebufferFormatKey key; - key.attachments = p_format; + key.attachments = p_attachments; + key.passes = p_passes; key.view_count = p_view_count; const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); @@ -3517,8 +3679,8 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c return E->get(); } - int color_references; - VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references, p_view_count); //actions don't matter for this use case + Vector<TextureSamples> samples; + VkRenderPass render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, p_view_count, &samples); //actions don't matter for this use case if (render_pass == VK_NULL_HANDLE) { //was likely invalid return INVALID_ID; @@ -3528,9 +3690,8 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c E = framebuffer_format_cache.insert(key, id); FramebufferFormat fb_format; fb_format.E = E; - fb_format.color_attachments = color_references; fb_format.render_pass = render_pass; - fb_format.samples = p_format[0].samples; + fb_format.pass_samples = samples; fb_format.view_count = p_view_count; framebuffer_formats[id] = fb_format; return id; @@ -3538,6 +3699,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_empty(TextureSamples p_samples) { FramebufferFormatKey key; + key.passes.push_back(FramebufferPass()); const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { @@ -3583,18 +3745,18 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c FramebufferFormat fb_format; fb_format.E = E; - fb_format.color_attachments = 0; fb_format.render_pass = render_pass; - fb_format.samples = p_samples; + fb_format.pass_samples.push_back(p_samples); framebuffer_formats[id] = fb_format; return id; } -RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format) { +RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass) { Map<FramebufferFormatID, FramebufferFormat>::Element *E = framebuffer_formats.find(p_format); ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1); + ERR_FAIL_COND_V(p_pass >= uint32_t(E->get().pass_samples.size()), TEXTURE_SAMPLES_1); - return E->get().samples; + return E->get().pass_samples[p_pass]; } /***********************/ @@ -3615,6 +3777,30 @@ RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, Textur RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ + FramebufferPass pass; + + for (int i = 0; i < p_texture_attachments.size(); i++) { + Texture *texture = texture_owner.getornull(p_texture_attachments[i]); + ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + + if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + pass.depth_attachment = i; + } else { + pass.color_attachments.push_back(i); + } + } + + Vector<FramebufferPass> passes; + passes.push_back(pass); + + return framebuffer_create_multipass(p_texture_attachments, passes, p_format_check, p_view_count); +} + +RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { + _THREAD_SAFE_METHOD_ + Vector<AttachmentFormat> attachments; Size2i size; @@ -3639,7 +3825,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac attachments.push_back(af); } - FramebufferFormatID format_id = framebuffer_format_create(attachments, p_view_count); + FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count); if (format_id == INVALID_ID) { return RID(); } @@ -4401,8 +4587,9 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining output variables."); for (uint32_t j = 0; j < ov_count; j++) { - if (output_vars[j]) { - fragment_outputs = MAX(fragment_outputs, output_vars[j]->location + 1); + const SpvReflectInterfaceVariable *refvar = output_vars[j]; + if (refvar != nullptr && refvar->built_in != SpvBuiltInFragDepth) { + fragment_outputs |= 1 << refvar->location; } } } @@ -4453,7 +4640,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages Shader shader; shader.vertex_input_mask = vertex_input_mask; - shader.fragment_outputs = fragment_outputs; + shader.fragment_output_mask = fragment_outputs; shader.push_constant = push_constant; shader.is_compute = is_compute; shader.compute_local_size[0] = compute_local_size[0]; @@ -5194,6 +5381,49 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, write.pTexelBufferView = nullptr; } break; case UNIFORM_TYPE_INPUT_ATTACHMENT: { + ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") supplied for compute shader (this is not allowed)."); + + if (uniform.ids.size() != set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector<VkDescriptorImageInfo> image_info; + + for (int j = 0; j < uniform.ids.size(); j++) { + Texture *texture = texture_owner.getornull(uniform.ids[j]); + + ERR_FAIL_COND_V_MSG(!texture, RID(), + "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), + "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); + + VkDescriptorImageInfo img_info; + img_info.sampler = VK_NULL_HANDLE; + img_info.imageView = texture->view; + + if (texture->owner.is_valid()) { + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + } + + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + image_info.push_back(img_info); + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size(); + write.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = nullptr; + write.pTexelBufferView = nullptr; + + type_size = uniform.ids.size(); } break; default: { } @@ -5404,7 +5634,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { /**** RENDER PIPELINE ****/ /*************************/ -RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags) { +RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass) { _THREAD_SAFE_METHOD_ //needs a shader @@ -5423,8 +5653,16 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma { //validate shader vs framebuffer - ERR_FAIL_COND_V_MSG(shader->fragment_outputs != fb_format.color_attachments, RID(), - "Mismatch fragment output bindings (" + itos(shader->fragment_outputs) + ") and framebuffer color buffers (" + itos(fb_format.color_attachments) + ") when binding both in render pipeline."); + ERR_FAIL_COND_V_MSG(p_for_render_pass >= uint32_t(fb_format.E->key().passes.size()), RID(), "Render pass requested for pipeline creation (" + itos(p_for_render_pass) + ") is out of bounds"); + const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; + uint32_t output_mask = 0; + for (int i = 0; i < pass.color_attachments.size(); i++) { + if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) { + output_mask |= 1 << i; + } + } + ERR_FAIL_COND_V_MSG(shader->fragment_output_mask != output_mask, RID(), + "Mismatch fragment shader output mask (" + itos(shader->fragment_output_mask) + ") and framebuffer color output mask (" + itos(output_mask) + ") when binding both in render pipeline."); } //vertex VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info; @@ -5609,44 +5847,53 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_INDEX_V(p_blend_state.logic_op, LOGIC_OP_MAX, RID()); color_blend_state_create_info.logicOp = logic_operations[p_blend_state.logic_op]; - ERR_FAIL_COND_V(fb_format.color_attachments != p_blend_state.attachments.size(), RID()); - Vector<VkPipelineColorBlendAttachmentState> attachment_states; + { + const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; + + for (int i = 0; i < pass.color_attachments.size(); i++) { + if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) { + int idx = attachment_states.size(); + + ERR_FAIL_INDEX_V(idx, p_blend_state.attachments.size(), RID()); + VkPipelineColorBlendAttachmentState state; + state.blendEnable = p_blend_state.attachments[idx].enable_blend; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[idx].src_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[idx].dst_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].color_blend_op, BLEND_OP_MAX, RID()); + state.colorBlendOp = blend_operations[p_blend_state.attachments[idx].color_blend_op]; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[idx].src_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[idx].dst_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].alpha_blend_op, BLEND_OP_MAX, RID()); + state.alphaBlendOp = blend_operations[p_blend_state.attachments[idx].alpha_blend_op]; + + state.colorWriteMask = 0; + if (p_blend_state.attachments[idx].write_r) { + state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; + } + if (p_blend_state.attachments[idx].write_g) { + state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; + } + if (p_blend_state.attachments[idx].write_b) { + state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; + } + if (p_blend_state.attachments[idx].write_a) { + state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + } - for (int i = 0; i < p_blend_state.attachments.size(); i++) { - VkPipelineColorBlendAttachmentState state; - state.blendEnable = p_blend_state.attachments[i].enable_blend; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[i].src_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[i].dst_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].color_blend_op, BLEND_OP_MAX, RID()); - state.colorBlendOp = blend_operations[p_blend_state.attachments[i].color_blend_op]; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].src_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].dst_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].alpha_blend_op, BLEND_OP_MAX, RID()); - state.alphaBlendOp = blend_operations[p_blend_state.attachments[i].alpha_blend_op]; - - state.colorWriteMask = 0; - if (p_blend_state.attachments[i].write_r) { - state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; - } - if (p_blend_state.attachments[i].write_g) { - state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; - } - if (p_blend_state.attachments[i].write_b) { - state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; - } - if (p_blend_state.attachments[i].write_a) { - state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + attachment_states.push_back(state); + idx++; + }; } - attachment_states.push_back(state); - }; + ERR_FAIL_COND_V(attachment_states.size() != p_blend_state.attachments.size(), RID()); + } color_blend_state_create_info.attachmentCount = attachment_states.size(); color_blend_state_create_info.pAttachments = attachment_states.ptr(); @@ -5719,7 +5966,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma graphics_pipeline_create_info.layout = shader->pipeline_layout; graphics_pipeline_create_info.renderPass = fb_format.render_pass; - graphics_pipeline_create_info.subpass = 0; + graphics_pipeline_create_info.subpass = p_for_render_pass; graphics_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; graphics_pipeline_create_info.basePipelineIndex = 0; @@ -5736,6 +5983,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma #ifdef DEBUG_ENABLED pipeline.validation.dynamic_state = p_dynamic_state_flags; pipeline.validation.framebuffer_format = p_framebuffer_format; + pipeline.validation.render_pass = p_for_render_pass; pipeline.validation.vertex_format = p_vertex_format; pipeline.validation.uses_restart_indices = input_assembly_create_info.primitiveRestartEnable; @@ -5874,13 +6122,14 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - draw_list = memnew(DrawList); - draw_list->command_buffer = command_buffer; + + Size2i size = Size2i(context->window_get_width(p_screen), context->window_get_height(p_screen)); + + _draw_list_allocate(Rect2i(Vector2i(), size), 0, 0); #ifdef DEBUG_ENABLED - draw_list->validation.framebuffer_format = screen_get_framebuffer_format(); + draw_list_framebuffer_format = screen_get_framebuffer_format(); #endif - draw_list_count = 0; - draw_list_split = false; + draw_list_subpass_count = 1; VkRenderPassBeginInfo render_pass_begin; render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; @@ -5888,8 +6137,8 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di render_pass_begin.renderPass = context->window_get_render_pass(p_screen); render_pass_begin.framebuffer = context->window_get_framebuffer(p_screen); - render_pass_begin.renderArea.extent.width = context->window_get_width(p_screen); - render_pass_begin.renderArea.extent.height = context->window_get_height(p_screen); + render_pass_begin.renderArea.extent.width = size.width; + render_pass_begin.renderArea.extent.height = size.height; render_pass_begin.renderArea.offset.x = 0; render_pass_begin.renderArea.offset.y = 0; @@ -5929,7 +6178,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } -Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass) { +Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count) { Framebuffer::VersionKey vk; vk.initial_color_action = p_initial_color_action; vk.final_color_action = p_final_color_action; @@ -5941,7 +6190,7 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu //need to create this version Framebuffer::Version version; - version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, nullptr, p_framebuffer->view_count); + version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, framebuffer_formats[p_framebuffer->format_id].E->key().passes, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_framebuffer->view_count); VkFramebufferCreateInfo framebuffer_create_info; framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; @@ -5965,11 +6214,14 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu VkResult err = vkCreateFramebuffer(device, &framebuffer_create_info, nullptr, &version.framebuffer); ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkCreateFramebuffer failed with error " + itos(err) + "."); + version.subpass_count = framebuffer_formats[p_framebuffer->format_id].E->key().passes.size(); + p_framebuffer->framebuffers.insert(vk, version); } const Framebuffer::Version &version = p_framebuffer->framebuffers[vk]; *r_framebuffer = version.framebuffer; *r_render_pass = version.render_pass; + *r_subpass_count = version.subpass_count; return OK; } @@ -6159,15 +6411,23 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values - int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, INVALID_ID, - "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ")."); + int color_count = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); + + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + color_count++; + } + } + + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, INVALID_ID, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(color_count) + ")."); } VkFramebuffer vkframebuffer; VkRenderPass render_pass; - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass); + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count); ERR_FAIL_COND_V(err != OK, INVALID_ID); VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; @@ -6177,13 +6437,14 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu return INVALID_ID; } - draw_list = memnew(DrawList); - draw_list->command_buffer = command_buffer; + draw_list_render_pass = render_pass; + draw_list_vkframebuffer = vkframebuffer; + + _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0, 0); #ifdef DEBUG_ENABLED - draw_list->validation.framebuffer_format = framebuffer->format_id; + draw_list_framebuffer_format = framebuffer->format_id; #endif - draw_list_count = 0; - draw_list_split = false; + draw_list_current_subpass = 0; if (needs_clear_color || needs_clear_depth) { _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); @@ -6207,7 +6468,6 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu vkCmdSetScissor(command_buffer, 0, 1, &scissor); - draw_list->viewport = Rect2i(viewport_offset, viewport_size); return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } @@ -6249,47 +6509,23 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values - int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, ERR_INVALID_PARAMETER, - "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ")."); - } - - if (p_splits > (uint32_t)split_draw_list_allocators.size()) { - uint32_t from = split_draw_list_allocators.size(); - split_draw_list_allocators.resize(p_splits); - for (uint32_t i = from; i < p_splits; i++) { - VkCommandPoolCreateInfo cmd_pool_info; - cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmd_pool_info.pNext = nullptr; - cmd_pool_info.queueFamilyIndex = context->get_graphics_queue(); - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - - VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + "."); - - for (int j = 0; j < frame_count; j++) { - VkCommandBuffer command_buffer; - - VkCommandBufferAllocateInfo cmdbuf; - //no command buffer exists, create it. - cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmdbuf.pNext = nullptr; - cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; - cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - cmdbuf.commandBufferCount = 1; - - VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); + int color_count = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); - split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer); + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + color_count++; } } + + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, ERR_INVALID_PARAMETER, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_count) + ")."); } VkFramebuffer vkframebuffer; VkRenderPass render_pass; - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass); + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer; @@ -6299,53 +6535,24 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p return ERR_CANT_CREATE; } - draw_list = memnew_arr(DrawList, p_splits); - draw_list_count = p_splits; - draw_list_split = true; - - for (uint32_t i = 0; i < p_splits; i++) { - //take a command buffer and initialize it - VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; - - VkCommandBufferInheritanceInfo inheritance_info; - inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; - inheritance_info.pNext = nullptr; - inheritance_info.renderPass = render_pass; - inheritance_info.subpass = 0; - inheritance_info.framebuffer = vkframebuffer; - inheritance_info.occlusionQueryEnable = false; - inheritance_info.queryFlags = 0; //? - inheritance_info.pipelineStatistics = 0; - - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; - cmdbuf_begin.pInheritanceInfo = &inheritance_info; - - VkResult res = vkResetCommandBuffer(command_buffer, 0); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + "."); - } - - res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + "."); - } + draw_list_current_subpass = 0; - draw_list[i].command_buffer = command_buffer; #ifdef DEBUG_ENABLED - draw_list[i].validation.framebuffer_format = framebuffer->format_id; + draw_list_framebuffer_format = framebuffer->format_id; #endif + draw_list_render_pass = render_pass; + draw_list_vkframebuffer = vkframebuffer; - if (i == 0 && (needs_clear_color || needs_clear_depth)) { - _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); - } + err = _draw_list_allocate(Rect2i(viewport_offset, viewport_size), p_splits, 0); + if (err != OK) { + return err; + } + if (needs_clear_color || needs_clear_depth) { + _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); + } + + for (uint32_t i = 0; i < p_splits; i++) { VkViewport viewport; viewport.x = viewport_offset.x; viewport.y = viewport_offset.y; @@ -6354,7 +6561,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p viewport.minDepth = 0; viewport.maxDepth = 1.0; - vkCmdSetViewport(command_buffer, 0, 1, &viewport); + vkCmdSetViewport(draw_list[i].command_buffer, 0, 1, &viewport); VkRect2D scissor; scissor.offset.x = viewport_offset.x; @@ -6362,10 +6569,8 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p scissor.extent.width = viewport_size.width; scissor.extent.height = viewport_size.height; - vkCmdSetScissor(command_buffer, 0, 1, &scissor); + vkCmdSetScissor(draw_list[i].command_buffer, 0, 1, &scissor); r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; - - draw_list[i].viewport = Rect2i(viewport_offset, viewport_size); } return OK; @@ -6410,7 +6615,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI const RenderPipeline *pipeline = render_pipeline_owner.getornull(p_render_pipeline); ERR_FAIL_COND(!pipeline); #ifdef DEBUG_ENABLED - ERR_FAIL_COND(pipeline->validation.framebuffer_format != dl->validation.framebuffer_format); + ERR_FAIL_COND(pipeline->validation.framebuffer_format != draw_list_framebuffer_format && pipeline->validation.render_pass != draw_list_current_subpass); #endif if (p_render_pipeline == dl->state.pipeline) { @@ -6749,30 +6954,162 @@ void RenderingDeviceVulkan::draw_list_disable_scissor(DrawListID p_list) { vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor); } -void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { - _THREAD_SAFE_METHOD_ +RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass() { + ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID); + ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID); - ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); + draw_list_current_subpass++; + + Rect2i viewport; + _draw_list_free(&viewport); + + vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE); + + _draw_list_allocate(viewport, 0, draw_list_current_subpass); + + return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; +} +Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) { + ERR_FAIL_COND_V(draw_list == nullptr, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER); + + draw_list_current_subpass++; + + Rect2i viewport; + _draw_list_free(&viewport); + + vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE); + + _draw_list_allocate(viewport, p_splits, draw_list_current_subpass); + + for (uint32_t i = 0; i < p_splits; i++) { + r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; + } + + return OK; +} +Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { + if (p_splits == 0) { + draw_list = memnew(DrawList); + draw_list->command_buffer = frames[frame].draw_command_buffer; + draw_list->viewport = p_viewport; + draw_list_count = 0; + draw_list_split = false; + } else { + if (p_splits > (uint32_t)split_draw_list_allocators.size()) { + uint32_t from = split_draw_list_allocators.size(); + split_draw_list_allocators.resize(p_splits); + for (uint32_t i = from; i < p_splits; i++) { + VkCommandPoolCreateInfo cmd_pool_info; + cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmd_pool_info.pNext = nullptr; + cmd_pool_info.queueFamilyIndex = context->get_graphics_queue(); + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool); + ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + "."); + + for (int j = 0; j < frame_count; j++) { + VkCommandBuffer command_buffer; + + VkCommandBufferAllocateInfo cmdbuf; + //no command buffer exists, create it. + cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdbuf.pNext = nullptr; + cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; + cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + cmdbuf.commandBufferCount = 1; + + VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); + + split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer); + } + } + } + draw_list = memnew_arr(DrawList, p_splits); + draw_list_count = p_splits; + draw_list_split = true; + + for (uint32_t i = 0; i < p_splits; i++) { + //take a command buffer and initialize it + VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; + + VkCommandBufferInheritanceInfo inheritance_info; + inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + inheritance_info.pNext = nullptr; + inheritance_info.renderPass = draw_list_render_pass; + inheritance_info.subpass = p_subpass; + inheritance_info.framebuffer = draw_list_vkframebuffer; + inheritance_info.occlusionQueryEnable = false; + inheritance_info.queryFlags = 0; //? + inheritance_info.pipelineStatistics = 0; + + VkCommandBufferBeginInfo cmdbuf_begin; + cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin.pNext = nullptr; + cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + cmdbuf_begin.pInheritanceInfo = &inheritance_info; + + VkResult res = vkResetCommandBuffer(command_buffer, 0); + if (res) { + memdelete_arr(draw_list); + draw_list = nullptr; + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + "."); + } + + res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin); + if (res) { + memdelete_arr(draw_list); + draw_list = nullptr; + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + "."); + } + + draw_list[i].command_buffer = command_buffer; + draw_list[i].viewport = p_viewport; + } + } + + return OK; +} + +void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { if (draw_list_split) { //send all command buffers VkCommandBuffer *command_buffers = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer) * draw_list_count); for (uint32_t i = 0; i < draw_list_count; i++) { vkEndCommandBuffer(draw_list[i].command_buffer); command_buffers[i] = draw_list[i].command_buffer; + if (r_last_viewport) { + if (i == 0 || draw_list[i].viewport_set) { + *r_last_viewport = draw_list[i].viewport; + } + } } vkCmdExecuteCommands(frames[frame].draw_command_buffer, draw_list_count, command_buffers); - vkCmdEndRenderPass(frames[frame].draw_command_buffer); memdelete_arr(draw_list); draw_list = nullptr; } else { + if (r_last_viewport) { + *r_last_viewport = draw_list->viewport; + } //just end the list - vkCmdEndRenderPass(draw_list->command_buffer); memdelete(draw_list); draw_list = nullptr; } +} + +void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); + + _draw_list_free(); + + vkCmdEndRenderPass(frames[frame].draw_command_buffer); for (int i = 0; i < draw_list_bound_textures.size(); i++) { Texture *texture = texture_owner.getornull(draw_list_bound_textures[i]); diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index f4fe9cf956..9f5830103d 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -234,12 +234,87 @@ class RenderingDeviceVulkan : public RenderingDevice { struct FramebufferFormatKey { Vector<AttachmentFormat> attachments; + Vector<FramebufferPass> passes; uint32_t view_count = 1; bool operator<(const FramebufferFormatKey &p_key) const { if (view_count != p_key.view_count) { return view_count < p_key.view_count; } + uint32_t pass_size = passes.size(); + uint32_t key_pass_size = p_key.passes.size(); + if (pass_size != key_pass_size) { + return pass_size < key_pass_size; + } + const FramebufferPass *pass_ptr = passes.ptr(); + const FramebufferPass *key_pass_ptr = p_key.passes.ptr(); + + for (uint32_t i = 0; i < pass_size; i++) { + { //compare color attachments + uint32_t attachment_size = pass_ptr[i].color_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].color_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].color_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].color_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare input attachments + uint32_t attachment_size = pass_ptr[i].input_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].input_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].input_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].input_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare resolve attachments + uint32_t attachment_size = pass_ptr[i].resolve_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].resolve_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].resolve_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].resolve_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare preserve attachments + uint32_t attachment_size = pass_ptr[i].preserve_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].preserve_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].preserve_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].preserve_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + if (pass_ptr[i].depth_attachment != key_pass_ptr[i].depth_attachment) { + return pass_ptr[i].depth_attachment < key_pass_ptr[i].depth_attachment; + } + } + int as = attachments.size(); int bs = p_key.attachments.size(); if (as != bs) { @@ -266,16 +341,14 @@ class RenderingDeviceVulkan : public RenderingDevice { } }; - VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr, uint32_t p_view_count = 1); - + VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count = 1, Vector<TextureSamples> *r_samples = nullptr); // This is a cache and it's never freed, it ensures // IDs for a given format are always unique. Map<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache; struct FramebufferFormat { const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E; VkRenderPass render_pass = VK_NULL_HANDLE; //here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec) - int color_attachments = 0; //used for pipeline validation - TextureSamples samples; + Vector<TextureSamples> pass_samples; uint32_t view_count = 1; // number of views }; @@ -289,6 +362,7 @@ class RenderingDeviceVulkan : public RenderingDevice { InitialAction initial_depth_action; FinalAction final_depth_action; uint32_t view_count; + bool operator<(const VersionKey &p_key) const { if (initial_color_action == p_key.initial_color_action) { if (final_color_action == p_key.final_color_action) { @@ -316,6 +390,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct Version { VkFramebuffer framebuffer = VK_NULL_HANDLE; VkRenderPass render_pass = VK_NULL_HANDLE; //this one is owned + uint32_t subpass_count = 1; }; Map<VersionKey, Version> framebuffers; @@ -536,7 +611,7 @@ class RenderingDeviceVulkan : public RenderingDevice { }; uint32_t vertex_input_mask = 0; //inputs used, this is mostly for validation - int fragment_outputs = 0; + uint32_t fragment_output_mask = 0; struct PushConstant { uint32_t push_constant_size = 0; @@ -680,6 +755,7 @@ class RenderingDeviceVulkan : public RenderingDevice { #ifdef DEBUG_ENABLED struct Validation { FramebufferFormatID framebuffer_format = 0; + uint32_t render_pass = 0; uint32_t dynamic_state = 0; VertexFormatID vertex_format = 0; bool uses_restart_indices = false; @@ -735,6 +811,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct DrawList { VkCommandBuffer command_buffer = VK_NULL_HANDLE; // If persistent, this is owned, otherwise it's shared with the ringbuffer. Rect2i viewport; + bool viewport_set = false; struct SetState { uint32_t pipeline_expected_format = 0; @@ -758,7 +835,6 @@ class RenderingDeviceVulkan : public RenderingDevice { #ifdef DEBUG_ENABLED struct Validation { bool active = true; // Means command buffer was not closed, so you can keep adding things. - FramebufferFormatID framebuffer_format = INVALID_ID; // Actual render pass values. uint32_t dynamic_state = 0; VertexFormatID vertex_format = INVALID_ID; @@ -794,7 +870,15 @@ class RenderingDeviceVulkan : public RenderingDevice { }; DrawList *draw_list = nullptr; // One for regular draw lists, multiple for split. + uint32_t draw_list_subpass_count = 0; uint32_t draw_list_count = 0; + VkRenderPass draw_list_render_pass; + VkFramebuffer draw_list_vkframebuffer; +#ifdef DEBUG_ENABLED + FramebufferFormatID draw_list_framebuffer_format = INVALID_ID; +#endif + uint32_t draw_list_current_subpass = 0; + bool draw_list_split = false; Vector<RID> draw_list_bound_textures; Vector<RID> draw_list_storage_textures; @@ -802,10 +886,12 @@ class RenderingDeviceVulkan : public RenderingDevice { bool draw_list_unbind_depth_textures = false; void _draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); - Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass); + Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count); Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access, uint32_t p_post_barrier); + Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass); + void _draw_list_free(Rect2i *r_last_viewport = nullptr); /**********************/ /**** COMPUTE LIST ****/ @@ -951,10 +1037,12 @@ public: /*********************/ virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1); + virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); - virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format); + virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0); virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); @@ -1005,7 +1093,7 @@ public: /**** RENDER PIPELINE ****/ /*************************/ - virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0); + virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0); virtual bool render_pipeline_is_valid(RID p_pipeline); /**************************/ @@ -1044,6 +1132,9 @@ public: virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect); virtual void draw_list_disable_scissor(DrawListID p_list); + virtual DrawListID draw_list_switch_to_next_pass(); + virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids); + virtual void draw_list_end(uint32_t p_post_barrier = BARRIER_MASK_ALL); /***********************/ diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f8c2f2910b..b962ea6e3e 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/core_bind.h" +#include "core/extension/native_extension_manager.h" #include "core/input/input.h" #include "core/io/config_file.h" #include "core/io/file_access.h" @@ -3771,9 +3772,12 @@ void EditorNode::register_editor_types() { ClassDB::register_class<EditorScenePostImport>(); //ClassDB::register_type<EditorImportExport>(); ClassDB::register_class<EditorDebuggerPlugin>(); + + NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_EDITOR); } void EditorNode::unregister_editor_types() { + NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_EDITOR); _init_callbacks.clear(); if (EditorPaths::get_singleton()) { EditorPaths::free(); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 5c5b5fe0ab..9987aaf3fe 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -550,6 +550,8 @@ void EditorPropertyArray::_length_changed(double p_page) { void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint_string) { array_type = p_array_type; + // The format of p_hint_string is: + // subType/subTypeHint:nextSubtype ... etc if (array_type == Variant::ARRAY && !p_hint_string.is_empty()) { int hint_subtype_separator = p_hint_string.find(":"); if (hint_subtype_separator >= 0) { diff --git a/main/main.cpp b/main/main.cpp index f2820fee07..9e3b5c9ba2 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -34,6 +34,7 @@ #include "core/core_string_names.h" #include "core/crypto/crypto.h" #include "core/debugger/engine_debugger.h" +#include "core/extension/extension_api_dump.h" #include "core/input/input.h" #include "core/input/input_map.h" #include "core/io/dir_access.h" @@ -174,7 +175,9 @@ static int frame_delay = 0; static bool disable_render_loop = false; static int fixed_fps = -1; static bool print_fps = false; - +#ifdef TOOLS_ENABLED +static bool dump_extension_api = false; +#endif bool profile_gpu = false; /* Helper methods */ @@ -406,6 +409,8 @@ Error Main::test_setup() { translation_server = memnew(TranslationServer); + register_core_extensions(); + // From `Main::setup2()`. preregister_module_types(); preregister_server_types(); @@ -887,7 +892,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph auto_build_solutions = true; editor = true; cmdline_tool = true; -#ifdef DEBUG_METHODS_ENABLED + } else if (I->get() == "--gdnative-generate-json-api" || I->get() == "--gdnative-generate-json-builtin-api") { // Register as an editor instance to use low-end fallback if relevant. editor = true; @@ -895,7 +900,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // We still pass it to the main arguments since the argument handling itself is not done in this function main_args.push_back(I->get()); -#endif + } else if (I->get() == "--dump-extension-api") { + // Register as an editor instance to use low-end fallback if relevant. + editor = true; + cmdline_tool = true; + dump_extension_api = true; + print_line("dump extension?"); + main_args.push_back(I->get()); } else if (I->get() == "--export" || I->get() == "--export-debug" || I->get() == "--export-pack") { // Export project // Actually handling is done in start(). @@ -1198,6 +1209,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->set_cmdline(execpath, main_args); + register_core_extensions(); //before display + GLOBAL_DEF("rendering/driver/driver_name", "Vulkan"); ProjectSettings::get_singleton()->set_custom_property_info("rendering/driver/driver_name", PropertyInfo(Variant::STRING, @@ -2004,6 +2017,11 @@ bool Main::start() { return false; } + + if (dump_extension_api) { + NativeExtensionAPIDump::generate_extension_json_file("extension_api.json"); + return false; + } #endif if (script == "" && game_path == "" && String(GLOBAL_GET("application/run/main_scene")) != "") { diff --git a/modules/gdnative/include/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h index dd4f76cf57..a88bd2878a 100644 --- a/modules/gdnative/include/gdnative/variant.h +++ b/modules/gdnative/include/gdnative/variant.h @@ -164,7 +164,7 @@ typedef void (*godot_validated_keyed_getter)(const godot_variant *p_base, const typedef bool (*godot_validated_keyed_checker)(const godot_variant *p_base, const godot_variant *p_key, bool *r_valid); typedef void (*godot_ptr_keyed_setter)(void *p_base, const void *p_key, const void *p_value); typedef void (*godot_ptr_keyed_getter)(const void *p_base, const void *p_key, void *r_value); -typedef bool (*godot_ptr_keyed_checker)(const godot_variant *p_base, const godot_variant *p_key); +typedef uint32_t (*godot_ptr_keyed_checker)(const godot_variant *p_base, const godot_variant *p_key); typedef void (*godot_validated_utility_function)(godot_variant *r_return, const godot_variant **p_arguments, int p_argument_count); typedef void (*godot_ptr_utility_function)(void *r_return, const void **p_arguments, int p_argument_count); diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 58cecfef63..51c72e10eb 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -135,8 +135,11 @@ public: // Wrong protocol ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version."); - const String req_file = req[1].get_file(); - const String req_ext = req[1].get_extension(); + const int query_index = req[1].find_char('?'); + const String path = (query_index == -1) ? req[1] : req[1].substr(0, query_index); + + const String req_file = path.get_file(); + const String req_ext = path.get_extension(); const String cache_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); const String filepath = cache_path.plus_file(req_file); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 64a3f1ca77..0ae1bb04b7 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -650,27 +650,11 @@ void Control::_notification(int p_notification) { } } -bool Control::clips_input() const { - if (get_script_instance()) { - return get_script_instance()->call(SceneStringNames::get_singleton()->_clips_input); - } - return false; -} - bool Control::has_point(const Point2 &p_point) const { - if (get_script_instance()) { - Variant v = p_point; - const Variant *p = &v; - Callable::CallError ce; - Variant ret = get_script_instance()->call(SceneStringNames::get_singleton()->_has_point, &p, 1, ce); - if (ce.error == Callable::CallError::CALL_OK) { - return ret; - } + bool ret; + if (GDVIRTUAL_CALL(_has_point, p_point, ret)) { + return ret; } - /*if (has_stylebox("mask")) { - Ref<StyleBox> mask = get_stylebox("mask"); - return mask->test_mask(p_point,Rect2(Point2(),get_size())); - }*/ return Rect2(Point2(), get_size()).has_point(p_point); } @@ -2784,7 +2768,6 @@ void Control::_bind_methods() { BIND_VMETHOD(MethodInfo( PropertyInfo(Variant::OBJECT, "control", PROPERTY_HINT_RESOURCE_TYPE, "Control"), "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text"))); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input")); ADD_GROUP("Anchor", "anchor_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT); @@ -2940,5 +2923,5 @@ void Control::_bind_methods() { ADD_SIGNAL(MethodInfo("minimum_size_changed")); ADD_SIGNAL(MethodInfo("theme_changed")); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_has_point", PropertyInfo(Variant::VECTOR2, "point"))); + GDVIRTUAL_BIND(_has_point); } diff --git a/scene/gui/control.h b/scene/gui/control.h index a05025c32d..87fad96571 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -32,6 +32,7 @@ #define CONTROL_H #include "core/math/transform_2d.h" +#include "core/object/gdvirtual.gen.inc" #include "core/templates/rid.h" #include "scene/gui/shortcut.h" #include "scene/main/canvas_item.h" @@ -264,6 +265,7 @@ private: static bool has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; + GDVIRTUAL1RC(bool, _has_point, Vector2) protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; @@ -329,7 +331,6 @@ public: virtual Size2 get_minimum_size() const; virtual Size2 get_combined_minimum_size() const; virtual bool has_point(const Point2 &p_point) const; - virtual bool clips_input() const; virtual void set_drag_forwarding(Control *p_target); virtual Variant get_drag_data(const Point2 &p_point); virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 5ef89e38f0..39aa6749e7 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -239,10 +239,6 @@ void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const } } -bool GraphEdit::clips_input() const { - return true; -} - void GraphEdit::get_connection_list(List<Connection> *r_connections) const { *r_connections = connections; } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index e8300f901c..5251de1722 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -235,7 +235,6 @@ protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; void _notification(int p_what); - virtual bool clips_input() const override; public: Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 7db6665e82..f32ad2144a 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -4085,7 +4085,7 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, "RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 5f872644ab..95445bdb7e 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -32,10 +32,6 @@ #include "core/os/os.h" #include "scene/main/window.h" -bool ScrollContainer::clips_input() const { - return true; -} - Size2 ScrollContainer::get_minimum_size() const { Ref<StyleBox> sb = get_theme_stylebox("bg"); Size2 min_size; diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index c77a0d62f5..4733fdabca 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -108,8 +108,6 @@ public: VScrollBar *get_v_scrollbar(); void ensure_control_visible(Control *p_control); - virtual bool clips_input() const override; - TypedArray<String> get_configuration_warnings() const override; ScrollContainer(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index e31135b46a..4c9c1c0055 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1746,7 +1746,7 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ Control *c = Object::cast_to<Control>(p_node); - if (!c || !c->clips_input() || c->has_point(matrix.affine_inverse().xform(p_global))) { + if (!c || !c->is_clipping_contents() || c->has_point(matrix.affine_inverse().xform(p_global))) { for (int i = p_node->get_child_count() - 1; i >= 0; i--) { CanvasItem *ci = Object::cast_to<CanvasItem>(p_node->get_child(i)); if (!ci || ci->is_set_as_top_level()) { diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 3a2b34c6c8..8d0283d536 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -31,6 +31,7 @@ #include "register_scene_types.h" #include "core/config/project_settings.h" +#include "core/extension/native_extension_manager.h" #include "core/object/class_db.h" #include "core/os/os.h" #include "scene/2d/animated_sprite_2d.h" @@ -1040,9 +1041,13 @@ void register_scene_types() { } } SceneDebugger::initialize(); + + NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE); } void unregister_scene_types() { + NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE); + SceneDebugger::deinitialize(); clear_default_theme(); diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 8acab79c9e..2e0af89c1a 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -103,7 +103,6 @@ SceneStringNames::SceneStringNames() { _update_scroll = StaticCString::create("_update_scroll"); _update_xform = StaticCString::create("_update_xform"); - _clips_input = StaticCString::create("_clips_input"); _structured_text_parser = StaticCString::create("_structured_text_parser"); _proxgroup_add = StaticCString::create("_proxgroup_add"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 0c528a45f7..01865b0d2f 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -129,7 +129,6 @@ public: StringName _update_scroll; StringName _update_xform; - StringName _clips_input; StringName _structured_text_parser; StringName _proxgroup_add; diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index deb230c4fb..59c7dcc7d2 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -55,6 +55,7 @@ #include "audio_server.h" #include "camera/camera_feed.h" #include "camera_server.h" +#include "core/extension/native_extension_manager.h" #include "display_server.h" #include "navigation_server_2d.h" #include "navigation_server_3d.h" @@ -194,6 +195,7 @@ void register_server_types() { ClassDB::register_class<RDTextureFormat>(); ClassDB::register_class<RDTextureView>(); ClassDB::register_class<RDAttachmentFormat>(); + ClassDB::register_class<RDFramebufferPass>(); ClassDB::register_class<RDSamplerState>(); ClassDB::register_class<RDVertexAttribute>(); ClassDB::register_class<RDUniform>(); @@ -232,22 +234,26 @@ void register_server_types() { PhysicsServer3DManager::register_server("GodotPhysics3D", &_createGodotPhysics3DCallback); PhysicsServer3DManager::set_default_server("GodotPhysics3D"); + + NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SERVERS); } void unregister_server_types() { + NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SERVERS); + memdelete(shader_types); TextServer::finish_hex_code_box_fonts(); } void register_server_singletons() { - Engine::get_singleton()->add_singleton(Engine::Singleton("DisplayServer", DisplayServer::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("RenderingServer", RenderingServer::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer2D", PhysicsServer2D::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton_mut())); - Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton_mut())); - Engine::get_singleton()->add_singleton(Engine::Singleton("TextServerManager", TextServerManager::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("DisplayServer", DisplayServer::get_singleton(), "DisplayServer")); + Engine::get_singleton()->add_singleton(Engine::Singleton("RenderingServer", RenderingServer::get_singleton(), "RenderingServer")); + Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton(), "AudioServer")); + Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer2D", PhysicsServer2D::get_singleton(), "PhysicsServer2D")); + Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton(), "PhysicsServer3D")); + Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton_mut(), "NavigationServer2D")); + Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton_mut(), "NavigationServer3D")); + Engine::get_singleton()->add_singleton(Engine::Singleton("TextServerManager", TextServerManager::get_singleton(), "TextServerManager")); + Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton(), "XRServer")); + Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton(), "CameraServer")); } diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp index b2b919c40e..22888ddbe5 100644 --- a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp +++ b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp @@ -31,20 +31,21 @@ #include "pipeline_cache_rd.h" #include "core/os/memory.h" -RID PipelineCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe) { +RID PipelineCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe, uint32_t p_render_pass) { RD::PipelineMultisampleState multisample_state_version = multisample_state; - multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id); + multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id, p_render_pass); RD::PipelineRasterizationState raster_state_version = rasterization_state; raster_state_version.wireframe = p_wireframe; - RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, raster_state_version, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags); + RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, raster_state_version, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags, p_render_pass); ERR_FAIL_COND_V(pipeline.is_null(), RID()); versions = (Version *)memrealloc(versions, sizeof(Version) * (version_count + 1)); versions[version_count].framebuffer_id = p_framebuffer_format_id; versions[version_count].vertex_id = p_vertex_format_id; versions[version_count].wireframe = p_wireframe; versions[version_count].pipeline = pipeline; + versions[version_count].render_pass = p_render_pass; version_count++; return pipeline; } diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.h b/servers/rendering/renderer_rd/pipeline_cache_rd.h index b1c8f21ecc..387a8a038f 100644 --- a/servers/rendering/renderer_rd/pipeline_cache_rd.h +++ b/servers/rendering/renderer_rd/pipeline_cache_rd.h @@ -50,6 +50,7 @@ class PipelineCacheRD { struct Version { RD::VertexFormatID vertex_id; RD::FramebufferFormatID framebuffer_id; + uint32_t render_pass; bool wireframe; RID pipeline; }; @@ -57,7 +58,7 @@ class PipelineCacheRD { Version *versions; uint32_t version_count; - RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe); + RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe, uint32_t p_render_pass); void _clear(); @@ -65,7 +66,7 @@ public: void setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0); void update_shader(RID p_shader); - _FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe = false) { + _FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe = false, uint32_t p_render_pass = 0) { #ifdef DEBUG_ENABLED ERR_FAIL_COND_V_MSG(shader.is_null(), RID(), "Attempted to use an unused shader variant (shader is null),"); @@ -74,13 +75,13 @@ public: spin_lock.lock(); RID result; for (uint32_t i = 0; i < version_count; i++) { - if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id && versions[i].wireframe == p_wireframe) { + if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id && versions[i].wireframe == p_wireframe && versions[i].render_pass == p_render_pass) { result = versions[i].pipeline; spin_lock.unlock(); return result; } } - result = _generate_version(p_vertex_format_id, p_framebuffer_format_id, p_wireframe); + result = _generate_version(p_vertex_format_id, p_framebuffer_format_id, p_wireframe, p_render_pass); spin_lock.unlock(); return result; } diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index ddf2ee9fe3..c52d97a3de 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -98,7 +98,7 @@ RID RenderingDevice::_texture_create_shared_from_slice(const Ref<RDTextureView> return texture_create_shared_from_slice(p_view->base, p_with_texture, p_layer, p_mipmap, p_slice_type); } -RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments) { +RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments, uint32_t p_view_count) { Vector<AttachmentFormat> attachments; attachments.resize(p_attachments.size()); @@ -107,12 +107,43 @@ RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID); attachments.write[i] = af->base; } - return framebuffer_format_create(attachments); + return framebuffer_format_create(attachments, p_view_count); } -RID RenderingDevice::_framebuffer_create(const Array &p_textures, FramebufferFormatID p_format_check) { +RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create_multipass(const TypedArray<RDAttachmentFormat> &p_attachments, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_view_count) { + Vector<AttachmentFormat> attachments; + attachments.resize(p_attachments.size()); + + for (int i = 0; i < p_attachments.size(); i++) { + Ref<RDAttachmentFormat> af = p_attachments[i]; + ERR_FAIL_COND_V(af.is_null(), INVALID_FORMAT_ID); + attachments.write[i] = af->base; + } + + Vector<FramebufferPass> passes; + for (int i = 0; i < p_passes.size(); i++) { + Ref<RDFramebufferPass> pass = p_passes[i]; + ERR_CONTINUE(pass.is_null()); + passes.push_back(pass->base); + } + + return framebuffer_format_create_multipass(attachments, passes, p_view_count); +} + +RID RenderingDevice::_framebuffer_create(const TypedArray<RID> &p_textures, FramebufferFormatID p_format_check, uint32_t p_view_count) { Vector<RID> textures = Variant(p_textures); - return framebuffer_create(textures, p_format_check); + return framebuffer_create(textures, p_format_check, p_view_count); +} + +RID RenderingDevice::_framebuffer_create_multipass(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { + Vector<RID> textures = Variant(p_textures); + Vector<FramebufferPass> passes; + for (int i = 0; i < p_passes.size(); i++) { + Ref<RDFramebufferPass> pass = p_passes[i]; + ERR_CONTINUE(pass.is_null()); + passes.push_back(pass->base); + } + return framebuffer_create_multipass(textures, passes, p_format_check, p_view_count); } RID RenderingDevice::_sampler_create(const Ref<RDSamplerState> &p_state) { @@ -190,7 +221,7 @@ Error RenderingDevice::_buffer_update(RID p_buffer, uint32_t p_offset, uint32_t return buffer_update(p_buffer, p_offset, p_size, p_data.ptr(), p_post_barrier); } -RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags) { +RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass) { PipelineRasterizationState rasterization_state; if (p_rasterization_state.is_valid()) { rasterization_state = p_rasterization_state->base; @@ -221,7 +252,7 @@ RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p } } - return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags); + return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags, p_for_render_pass); } Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { @@ -242,6 +273,22 @@ Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint3 return split_ids; } +Vector<int64_t> RenderingDevice::_draw_list_switch_to_next_pass_split(uint32_t p_splits) { + Vector<DrawListID> splits; + splits.resize(p_splits); + + Error err = draw_list_switch_to_next_pass_split(p_splits, splits.ptrw()); + ERR_FAIL_COND_V(err != OK, Vector<int64_t>()); + + Vector<int64_t> split_ids; + split_ids.resize(splits.size()); + for (int i = 0; i < splits.size(); i++) { + split_ids.write[i] = splits[i]; + } + + return split_ids; +} + void RenderingDevice::_draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); draw_list_set_push_constant(p_list, p_data.ptr(), p_data_size); @@ -269,10 +316,12 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count", "post_barrier"), &RenderingDevice::texture_clear, DEFVAL(BARRIER_MASK_ALL)); ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "post_barrier"), &RenderingDevice::texture_resolve_multisample, DEFVAL(BARRIER_MASK_ALL)); - ClassDB::bind_method(D_METHOD("framebuffer_format_create", "attachments"), &RenderingDevice::_framebuffer_format_create); + ClassDB::bind_method(D_METHOD("framebuffer_format_create", "attachments", "view_count"), &RenderingDevice::_framebuffer_format_create, DEFVAL(1)); + ClassDB::bind_method(D_METHOD("framebuffer_format_create_multipass", "attachments", "passes", "view_count"), &RenderingDevice::_framebuffer_format_create_multipass, DEFVAL(1)); ClassDB::bind_method(D_METHOD("framebuffer_format_create_empty", "samples"), &RenderingDevice::framebuffer_format_create_empty, DEFVAL(TEXTURE_SAMPLES_1)); - ClassDB::bind_method(D_METHOD("framebuffer_format_get_texture_samples", "format"), &RenderingDevice::framebuffer_format_get_texture_samples); - ClassDB::bind_method(D_METHOD("framebuffer_create", "textures", "validate_with_format"), &RenderingDevice::_framebuffer_create, DEFVAL(INVALID_FORMAT_ID)); + ClassDB::bind_method(D_METHOD("framebuffer_format_get_texture_samples", "format", "render_pass"), &RenderingDevice::framebuffer_format_get_texture_samples, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("framebuffer_create", "textures", "validate_with_format", "view_count"), &RenderingDevice::_framebuffer_create, DEFVAL(INVALID_FORMAT_ID), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("framebuffer_create_multipass", "textures", "passes", "validate_with_format", "view_count"), &RenderingDevice::_framebuffer_create_multipass, DEFVAL(INVALID_FORMAT_ID), DEFVAL(1)); ClassDB::bind_method(D_METHOD("framebuffer_create_empty", "size", "samples", "validate_with_format"), &RenderingDevice::framebuffer_create_empty, DEFVAL(TEXTURE_SAMPLES_1), DEFVAL(INVALID_FORMAT_ID)); ClassDB::bind_method(D_METHOD("framebuffer_get_format", "framebuffer"), &RenderingDevice::framebuffer_get_format); @@ -299,7 +348,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes", "post_barrier"), &RenderingDevice::buffer_clear, DEFVAL(BARRIER_MASK_ALL)); ClassDB::bind_method(D_METHOD("buffer_get_data", "buffer"), &RenderingDevice::buffer_get_data); - ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags"), &RenderingDevice::_render_pipeline_create, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags", "for_render_pass"), &RenderingDevice::_render_pipeline_create, DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("render_pipeline_is_valid", "render_pipeline"), &RenderingDevice::render_pipeline_is_valid); ClassDB::bind_method(D_METHOD("compute_pipeline_create", "shader"), &RenderingDevice::compute_pipeline_create); @@ -325,6 +374,9 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_list_enable_scissor", "draw_list", "rect"), &RenderingDevice::draw_list_enable_scissor, DEFVAL(Rect2i())); ClassDB::bind_method(D_METHOD("draw_list_disable_scissor", "draw_list"), &RenderingDevice::draw_list_disable_scissor); + ClassDB::bind_method(D_METHOD("draw_list_switch_to_next_pass"), &RenderingDevice::draw_list_switch_to_next_pass); + ClassDB::bind_method(D_METHOD("draw_list_switch_to_next_pass_split", "splits"), &RenderingDevice::_draw_list_switch_to_next_pass_split); + ClassDB::bind_method(D_METHOD("draw_list_end", "post_barrier"), &RenderingDevice::draw_list_end, DEFVAL(BARRIER_MASK_ALL)); ClassDB::bind_method(D_METHOD("compute_list_begin", "allow_draw_overlap"), &RenderingDevice::compute_list_begin, DEFVAL(false)); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index c13dc01dd7..be7e127491 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -47,6 +47,7 @@ class RDPipelineRasterizationState; class RDPipelineMultisampleState; class RDPipelineDepthStencilState; class RDPipelineColorBlendState; +class RDFramebufferPass; class RenderingDevice : public Object { GDCLASS(RenderingDevice, Object) @@ -517,10 +518,23 @@ public: // This ID is warranted to be unique for the same formats, does not need to be freed virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1) = 0; + struct FramebufferPass { + enum { + ATTACHMENT_UNUSED = -1 + }; + Vector<int32_t> color_attachments; + Vector<int32_t> input_attachments; + Vector<int32_t> resolve_attachments; + Vector<int32_t> preserve_attachments; + int32_t depth_attachment = ATTACHMENT_UNUSED; + }; + + virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1) = 0; virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1) = 0; - virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format) = 0; + virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0) = 0; virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0; + virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0; virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID) = 0; virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0; @@ -962,7 +976,7 @@ public: }; virtual bool render_pipeline_is_valid(RID p_pipeline) = 0; - virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0) = 0; + virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0) = 0; /**************************/ /**** COMPUTE PIPELINE ****/ @@ -1018,6 +1032,9 @@ public: virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) = 0; virtual void draw_list_disable_scissor(DrawListID p_list) = 0; + virtual DrawListID draw_list_switch_to_next_pass() = 0; + virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) = 0; + virtual void draw_list_end(uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0; /***********************/ @@ -1134,8 +1151,10 @@ protected: RID _texture_create_shared(const Ref<RDTextureView> &p_view, RID p_with_texture); RID _texture_create_shared_from_slice(const Ref<RDTextureView> &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type = TEXTURE_SLICE_2D); - FramebufferFormatID _framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments); - RID _framebuffer_create(const Array &p_textures, FramebufferFormatID p_format_check = INVALID_ID); + FramebufferFormatID _framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments, uint32_t p_view_count); + FramebufferFormatID _framebuffer_format_create_multipass(const TypedArray<RDAttachmentFormat> &p_attachments, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_view_count); + RID _framebuffer_create(const TypedArray<RID> &p_textures, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + RID _framebuffer_create_multipass(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); RID _sampler_create(const Ref<RDSamplerState> &p_state); VertexFormatID _vertex_format_create(const TypedArray<RDVertexAttribute> &p_vertex_formats); RID _vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const TypedArray<RID> &p_src_buffers); @@ -1146,11 +1165,12 @@ protected: Error _buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); - RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags = 0); + RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0); Vector<int64_t> _draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>()); void _draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size); void _compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size); + Vector<int64_t> _draw_list_switch_to_next_pass_split(uint32_t p_splits); }; VARIANT_ENUM_CAST(RenderingDevice::ShaderStage) diff --git a/servers/rendering/rendering_device_binds.h b/servers/rendering/rendering_device_binds.h index 912674e309..dc59568ce9 100644 --- a/servers/rendering/rendering_device_binds.h +++ b/servers/rendering/rendering_device_binds.h @@ -128,6 +128,34 @@ protected: } }; +class RDFramebufferPass : public RefCounted { + GDCLASS(RDFramebufferPass, RefCounted) + friend class RenderingDevice; + + RD::FramebufferPass base; + +public: + RD_SETGET(PackedInt32Array, color_attachments) + RD_SETGET(PackedInt32Array, input_attachments) + RD_SETGET(PackedInt32Array, resolve_attachments) + RD_SETGET(PackedInt32Array, preserve_attachments) + RD_SETGET(int32_t, depth_attachment) +protected: + enum { + ATTACHMENT_UNUSED = -1 + }; + + static void _bind_methods() { + RD_BIND(Variant::PACKED_INT32_ARRAY, RDFramebufferPass, color_attachments); + RD_BIND(Variant::PACKED_INT32_ARRAY, RDFramebufferPass, input_attachments); + RD_BIND(Variant::PACKED_INT32_ARRAY, RDFramebufferPass, resolve_attachments); + RD_BIND(Variant::PACKED_INT32_ARRAY, RDFramebufferPass, preserve_attachments); + RD_BIND(Variant::INT, RDFramebufferPass, depth_attachment); + + BIND_CONSTANT(ATTACHMENT_UNUSED); + } +}; + class RDSamplerState : public RefCounted { GDCLASS(RDSamplerState, RefCounted) friend class RenderingDevice; |