summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/SCsub1
-rw-r--r--core/config/engine.cpp5
-rw-r--r--core/config/engine.h3
-rw-r--r--core/extension/SCsub7
-rw-r--r--core/extension/extension_api_dump.cpp805
-rw-r--r--core/extension/extension_api_dump.h45
-rw-r--r--core/extension/gdnative_interface.cpp688
-rw-r--r--core/extension/gdnative_interface.h427
-rw-r--r--core/extension/native_extension.cpp411
-rw-r--r--core/extension/native_extension.h94
-rw-r--r--core/extension/native_extension_manager.cpp130
-rw-r--r--core/extension/native_extension_manager.h71
-rw-r--r--core/object/SCsub5
-rw-r--r--core/object/class_db.cpp11
-rw-r--r--core/object/class_db.h4
-rw-r--r--core/object/make_virtuals.py152
-rw-r--r--core/object/method_bind.cpp29
-rw-r--r--core/object/method_bind.h2
-rw-r--r--core/object/object.cpp26
-rw-r--r--core/object/object.h46
-rw-r--r--core/object/ref_counted.h4
-rw-r--r--core/register_core_types.cpp28
-rw-r--r--core/register_core_types.h1
-rw-r--r--core/variant/binder_common.h2
-rw-r--r--core/variant/method_ptrcall.h14
-rw-r--r--core/variant/variant.h4
-rw-r--r--core/variant/variant_call.cpp19
-rw-r--r--core/variant/variant_op.cpp4
-rw-r--r--core/variant/variant_setget.cpp4
-rw-r--r--core/variant/variant_utility.cpp19
-rw-r--r--doc/classes/@GlobalScope.xml2
-rw-r--r--doc/classes/Color.xml40
-rw-r--r--doc/classes/Control.xml16
-rw-r--r--doc/classes/NativeExtension.xml57
-rw-r--r--doc/classes/NativeExtensionManager.xml61
-rw-r--r--doc/classes/Plane.xml14
-rw-r--r--doc/classes/Quaternion.xml32
-rw-r--r--doc/classes/RDFramebufferPass.xml35
-rw-r--r--doc/classes/RenderingDevice.xml50
-rw-r--r--doc/classes/Vector2.xml24
-rw-r--r--doc/classes/Vector2i.xml24
-rw-r--r--doc/classes/Vector3.xml24
-rw-r--r--doc/classes/Vector3i.xml24
-rw-r--r--doc/classes/float.xml33
-rw-r--r--doc/classes/int.xml45
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp781
-rw-r--r--drivers/vulkan/rendering_device_vulkan.h109
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/editor_properties_array_dict.cpp2
-rw-r--r--main/main.cpp24
-rw-r--r--modules/gdnative/include/gdnative/variant.h2
-rw-r--r--platform/javascript/export/export.cpp7
-rw-r--r--scene/gui/control.cpp25
-rw-r--r--scene/gui/control.h3
-rw-r--r--scene/gui/graph_edit.cpp4
-rw-r--r--scene/gui/graph_edit.h1
-rw-r--r--scene/gui/rich_text_label.cpp2
-rw-r--r--scene/gui/scroll_container.cpp4
-rw-r--r--scene/gui/scroll_container.h2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/register_scene_types.cpp5
-rw-r--r--scene/scene_string_names.cpp1
-rw-r--r--scene/scene_string_names.h1
-rw-r--r--servers/register_server_types.cpp26
-rw-r--r--servers/rendering/renderer_rd/pipeline_cache_rd.cpp7
-rw-r--r--servers/rendering/renderer_rd/pipeline_cache_rd.h9
-rw-r--r--servers/rendering/rendering_device.cpp72
-rw-r--r--servers/rendering/rendering_device.h30
-rw-r--r--servers/rendering/rendering_device_binds.h28
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;