mirror of
https://github.com/Redot-Engine/redot-engine.git
synced 2025-12-06 07:17:42 -05:00
Merge pull request #100673 from RandomShaper/res_duplicate
Overhaul resource duplication
This commit is contained in:
@@ -4268,7 +4268,7 @@ Image::Image(const uint8_t *p_mem_png_jpg, int p_len) {
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Resource> Image::duplicate(bool p_subresources) const {
|
||||
Ref<Resource> Image::_duplicate(const DuplicateParams &p_params) const {
|
||||
Ref<Image> copy;
|
||||
copy.instantiate();
|
||||
copy->_copy_internals_from(*this);
|
||||
|
||||
@@ -244,6 +244,8 @@ public:
|
||||
static Ref<Image> (*basis_universal_unpacker_ptr)(const uint8_t *p_data, int p_size);
|
||||
|
||||
protected:
|
||||
virtual Ref<Resource> _duplicate(const DuplicateParams &p_params) const override;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
@@ -425,8 +427,6 @@ public:
|
||||
void convert_ra_rgba8_to_rg();
|
||||
void convert_rgba8_to_bgra8();
|
||||
|
||||
virtual Ref<Resource> duplicate(bool p_subresources = false) const override;
|
||||
|
||||
UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC) const;
|
||||
void optimize_channels();
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/math/random_pcg.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/variant/container_type_validate.h"
|
||||
#include "scene/main/node.h" //only so casting works
|
||||
|
||||
void Resource::emit_changed() {
|
||||
@@ -265,76 +266,178 @@ void Resource::reload_from_file() {
|
||||
copy_from(s);
|
||||
}
|
||||
|
||||
void Resource::_dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
|
||||
switch (r_variant.get_type()) {
|
||||
case Variant::ARRAY: {
|
||||
Array a = r_variant;
|
||||
for (int i = 0; i < a.size(); i++) {
|
||||
_dupe_sub_resources(a[i], p_for_scene, p_remap_cache);
|
||||
}
|
||||
} break;
|
||||
case Variant::DICTIONARY: {
|
||||
Dictionary d = r_variant;
|
||||
for (Variant &k : d.get_key_list()) {
|
||||
if (k.get_type() == Variant::OBJECT) {
|
||||
// Replace in dictionary key.
|
||||
Ref<Resource> sr = k;
|
||||
if (sr.is_valid() && sr->is_local_to_scene()) {
|
||||
if (p_remap_cache.has(sr)) {
|
||||
d[p_remap_cache[sr]] = d[k];
|
||||
d.erase(k);
|
||||
} else {
|
||||
Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache);
|
||||
d[dupe] = d[k];
|
||||
d.erase(k);
|
||||
p_remap_cache[sr] = dupe;
|
||||
Variant Resource::_duplicate_recursive(const Variant &p_variant, const DuplicateParams &p_params, uint32_t p_usage) const {
|
||||
// Anything other than object can be simply skipped in case of a shallow copy.
|
||||
if (!p_params.deep && p_variant.get_type() != Variant::OBJECT) {
|
||||
return p_variant;
|
||||
}
|
||||
|
||||
switch (p_variant.get_type()) {
|
||||
case Variant::OBJECT: {
|
||||
const Ref<Resource> &sr = p_variant;
|
||||
bool should_duplicate = false;
|
||||
if (sr.is_valid()) {
|
||||
if ((p_usage & PROPERTY_USAGE_ALWAYS_DUPLICATE)) {
|
||||
should_duplicate = true;
|
||||
} else if ((p_usage & PROPERTY_USAGE_NEVER_DUPLICATE)) {
|
||||
should_duplicate = false;
|
||||
} else if (p_params.local_scene) {
|
||||
should_duplicate = sr->is_local_to_scene();
|
||||
} else {
|
||||
switch (p_params.subres_mode) {
|
||||
case RESOURCE_DEEP_DUPLICATE_NONE: {
|
||||
should_duplicate = false;
|
||||
} break;
|
||||
case RESOURCE_DEEP_DUPLICATE_INTERNAL: {
|
||||
should_duplicate = p_params.deep && sr->is_built_in();
|
||||
} break;
|
||||
case RESOURCE_DEEP_DUPLICATE_ALL: {
|
||||
should_duplicate = p_params.deep;
|
||||
} break;
|
||||
default: {
|
||||
DEV_ASSERT(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_dupe_sub_resources(k, p_for_scene, p_remap_cache);
|
||||
}
|
||||
|
||||
_dupe_sub_resources(d[k], p_for_scene, p_remap_cache);
|
||||
}
|
||||
if (should_duplicate) {
|
||||
if (thread_duplicate_remap_cache->has(sr)) {
|
||||
return thread_duplicate_remap_cache->get(sr);
|
||||
} else {
|
||||
const Ref<Resource> &dupe = p_params.local_scene
|
||||
? sr->duplicate_for_local_scene(p_params.local_scene, *thread_duplicate_remap_cache)
|
||||
: sr->_duplicate(p_params);
|
||||
thread_duplicate_remap_cache->insert(sr, dupe);
|
||||
return dupe;
|
||||
}
|
||||
} else {
|
||||
return p_variant;
|
||||
}
|
||||
} break;
|
||||
case Variant::OBJECT: {
|
||||
Ref<Resource> sr = r_variant;
|
||||
if (sr.is_valid() && sr->is_local_to_scene()) {
|
||||
if (p_remap_cache.has(sr)) {
|
||||
r_variant = p_remap_cache[sr];
|
||||
} else {
|
||||
Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache);
|
||||
r_variant = dupe;
|
||||
p_remap_cache[sr] = dupe;
|
||||
}
|
||||
case Variant::ARRAY: {
|
||||
const Array &src = p_variant;
|
||||
Array dst;
|
||||
if (src.is_typed()) {
|
||||
dst.set_typed(src.get_element_type());
|
||||
}
|
||||
dst.resize(src.size());
|
||||
for (int i = 0; i < src.size(); i++) {
|
||||
dst[i] = _duplicate_recursive(src[i], p_params);
|
||||
}
|
||||
return dst;
|
||||
} break;
|
||||
case Variant::DICTIONARY: {
|
||||
const Dictionary &src = p_variant;
|
||||
Dictionary dst;
|
||||
if (src.is_typed()) {
|
||||
dst.set_typed(src.get_key_type(), src.get_value_type());
|
||||
}
|
||||
for (const Variant &k : src.get_key_list()) {
|
||||
const Variant &v = src[k];
|
||||
dst.set(
|
||||
_duplicate_recursive(k, p_params),
|
||||
_duplicate_recursive(v, p_params));
|
||||
}
|
||||
return dst;
|
||||
} break;
|
||||
case Variant::PACKED_BYTE_ARRAY:
|
||||
case Variant::PACKED_INT32_ARRAY:
|
||||
case Variant::PACKED_INT64_ARRAY:
|
||||
case Variant::PACKED_FLOAT32_ARRAY:
|
||||
case Variant::PACKED_FLOAT64_ARRAY:
|
||||
case Variant::PACKED_STRING_ARRAY:
|
||||
case Variant::PACKED_VECTOR2_ARRAY:
|
||||
case Variant::PACKED_VECTOR3_ARRAY:
|
||||
case Variant::PACKED_COLOR_ARRAY:
|
||||
case Variant::PACKED_VECTOR4_ARRAY: {
|
||||
return p_variant.duplicate();
|
||||
} break;
|
||||
default: {
|
||||
return p_variant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
|
||||
Ref<Resource> Resource::_duplicate(const DuplicateParams &p_params) const {
|
||||
ERR_FAIL_COND_V_MSG(p_params.local_scene && p_params.subres_mode != RESOURCE_DEEP_DUPLICATE_MAX, Ref<Resource>(), "Duplication for local-to-scene can't specify a deep duplicate mode.");
|
||||
|
||||
DuplicateRemapCacheT *remap_cache_backup = thread_duplicate_remap_cache;
|
||||
|
||||
// These are for avoiding potential duplicates that can happen in custom code
|
||||
// from participating in the same duplication session (remap cache).
|
||||
#define BEFORE_USER_CODE thread_duplicate_remap_cache = nullptr;
|
||||
#define AFTER_USER_CODE thread_duplicate_remap_cache = remap_cache_backup;
|
||||
|
||||
List<PropertyInfo> plist;
|
||||
get_property_list(&plist);
|
||||
|
||||
BEFORE_USER_CODE
|
||||
Ref<Resource> r = Object::cast_to<Resource>(ClassDB::instantiate(get_class()));
|
||||
AFTER_USER_CODE
|
||||
ERR_FAIL_COND_V(r.is_null(), Ref<Resource>());
|
||||
|
||||
r->local_scene = p_for_scene;
|
||||
thread_duplicate_remap_cache->insert(Ref<Resource>(this), r);
|
||||
|
||||
if (p_params.local_scene) {
|
||||
r->local_scene = p_params.local_scene;
|
||||
}
|
||||
|
||||
// Duplicate script first, so the scripted properties are considered.
|
||||
BEFORE_USER_CODE
|
||||
r->set_script(get_script());
|
||||
AFTER_USER_CODE
|
||||
|
||||
for (const PropertyInfo &E : plist) {
|
||||
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
|
||||
continue;
|
||||
}
|
||||
Variant p = get(E.name).duplicate(true);
|
||||
if (E.name == "script") {
|
||||
continue;
|
||||
}
|
||||
|
||||
_dupe_sub_resources(p, p_for_scene, p_remap_cache);
|
||||
BEFORE_USER_CODE
|
||||
Variant p = get(E.name);
|
||||
AFTER_USER_CODE
|
||||
|
||||
p = _duplicate_recursive(p, p_params, E.usage);
|
||||
|
||||
BEFORE_USER_CODE
|
||||
r->set(E.name, p);
|
||||
AFTER_USER_CODE
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
#undef BEFORE_USER_CODE
|
||||
#undef AFTER_USER_CODE
|
||||
}
|
||||
|
||||
Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, DuplicateRemapCacheT &p_remap_cache) const {
|
||||
#ifdef DEBUG_ENABLED
|
||||
// The only possibilities for the remap cache passed being valid are these:
|
||||
// a) It's the same already used as the one of the thread. That happens when this function
|
||||
// is called within some recursion level within a duplication.
|
||||
// b) There's no current thread remap cache, which means this function is acting as an entry point.
|
||||
// This check failing means that this function is being called as an entry point during an ongoing
|
||||
// duplication, likely due to custom instantiation or setter code. It would be an engine bug because
|
||||
// code starting or joining a duplicate session must ensure to exit it temporarily when making calls
|
||||
// that may in turn invoke such custom code.
|
||||
if (thread_duplicate_remap_cache && &p_remap_cache != thread_duplicate_remap_cache) {
|
||||
ERR_PRINT("Resource::duplicate_for_local_scene() called during an ongoing duplication session. This is an engine bug.");
|
||||
}
|
||||
#endif
|
||||
|
||||
DuplicateRemapCacheT *remap_cache_backup = thread_duplicate_remap_cache;
|
||||
thread_duplicate_remap_cache = &p_remap_cache;
|
||||
|
||||
DuplicateParams params;
|
||||
params.deep = true;
|
||||
params.local_scene = p_for_scene;
|
||||
const Ref<Resource> &dupe = _duplicate(params);
|
||||
|
||||
thread_duplicate_remap_cache = remap_cache_backup;
|
||||
|
||||
return dupe;
|
||||
}
|
||||
|
||||
void Resource::_find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found) {
|
||||
@@ -363,7 +466,7 @@ void Resource::_find_sub_resources(const Variant &p_variant, HashSet<Ref<Resourc
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
|
||||
void Resource::configure_for_local_scene(Node *p_for_scene, DuplicateRemapCacheT &p_remap_cache) {
|
||||
List<PropertyInfo> plist;
|
||||
get_property_list(&plist);
|
||||
|
||||
@@ -390,53 +493,90 @@ void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Resource> Resource::duplicate(bool p_subresources) const {
|
||||
List<PropertyInfo> plist;
|
||||
get_property_list(&plist);
|
||||
Ref<Resource> Resource::duplicate(bool p_deep) const {
|
||||
DuplicateRemapCacheT remap_cache;
|
||||
bool started_session = false;
|
||||
if (!thread_duplicate_remap_cache) {
|
||||
thread_duplicate_remap_cache = &remap_cache;
|
||||
started_session = true;
|
||||
}
|
||||
|
||||
Ref<Resource> r = static_cast<Resource *>(ClassDB::instantiate(get_class()));
|
||||
ERR_FAIL_COND_V(r.is_null(), Ref<Resource>());
|
||||
DuplicateParams params;
|
||||
params.deep = p_deep;
|
||||
params.subres_mode = RESOURCE_DEEP_DUPLICATE_INTERNAL;
|
||||
const Ref<Resource> &dupe = _duplicate(params);
|
||||
|
||||
for (const PropertyInfo &E : plist) {
|
||||
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
|
||||
continue;
|
||||
}
|
||||
Variant p = get(E.name);
|
||||
if (started_session) {
|
||||
thread_duplicate_remap_cache = nullptr;
|
||||
}
|
||||
|
||||
switch (p.get_type()) {
|
||||
case Variant::Type::DICTIONARY:
|
||||
case Variant::Type::ARRAY:
|
||||
case Variant::Type::PACKED_BYTE_ARRAY:
|
||||
case Variant::Type::PACKED_COLOR_ARRAY:
|
||||
case Variant::Type::PACKED_INT32_ARRAY:
|
||||
case Variant::Type::PACKED_INT64_ARRAY:
|
||||
case Variant::Type::PACKED_FLOAT32_ARRAY:
|
||||
case Variant::Type::PACKED_FLOAT64_ARRAY:
|
||||
case Variant::Type::PACKED_STRING_ARRAY:
|
||||
case Variant::Type::PACKED_VECTOR2_ARRAY:
|
||||
case Variant::Type::PACKED_VECTOR3_ARRAY:
|
||||
case Variant::Type::PACKED_VECTOR4_ARRAY: {
|
||||
r->set(E.name, p.duplicate(p_subresources));
|
||||
} break;
|
||||
return dupe;
|
||||
}
|
||||
|
||||
case Variant::Type::OBJECT: {
|
||||
if (!(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) {
|
||||
Ref<Resource> sr = p;
|
||||
if (sr.is_valid()) {
|
||||
r->set(E.name, sr->duplicate(p_subresources));
|
||||
}
|
||||
} else {
|
||||
r->set(E.name, p);
|
||||
}
|
||||
} break;
|
||||
Ref<Resource> Resource::duplicate_deep(ResourceDeepDuplicateMode p_deep_subresources_mode) const {
|
||||
ERR_FAIL_INDEX_V(p_deep_subresources_mode, RESOURCE_DEEP_DUPLICATE_MAX, Ref<Resource>());
|
||||
|
||||
default: {
|
||||
r->set(E.name, p);
|
||||
}
|
||||
DuplicateRemapCacheT remap_cache;
|
||||
bool started_session = false;
|
||||
if (!thread_duplicate_remap_cache) {
|
||||
thread_duplicate_remap_cache = &remap_cache;
|
||||
started_session = true;
|
||||
}
|
||||
|
||||
DuplicateParams params;
|
||||
params.deep = true;
|
||||
params.subres_mode = p_deep_subresources_mode;
|
||||
const Ref<Resource> &dupe = _duplicate(params);
|
||||
|
||||
if (started_session) {
|
||||
thread_duplicate_remap_cache = nullptr;
|
||||
}
|
||||
|
||||
return dupe;
|
||||
}
|
||||
|
||||
Ref<Resource> Resource::_duplicate_from_variant(bool p_deep, ResourceDeepDuplicateMode p_deep_subresources_mode, int p_recursion_count) const {
|
||||
// A call without deep duplication would have been early-rejected at Variant::duplicate() unless it's the root call.
|
||||
DEV_ASSERT(!(p_recursion_count > 0 && p_deep_subresources_mode == RESOURCE_DEEP_DUPLICATE_NONE));
|
||||
|
||||
// When duplicating from Variant, this function may be called multiple times from
|
||||
// different parts of the data structure being copied. Therefore, we need to create
|
||||
// a remap cache instance in a way that can be shared among all of the calls.
|
||||
// Whatever Variant, Array or Dictionary that initiated the call chain will eventually
|
||||
// claim it, when the stack unwinds up to the root call.
|
||||
// One exception is that this is the root call.
|
||||
|
||||
if (p_recursion_count == 0) {
|
||||
if (p_deep) {
|
||||
return duplicate_deep(p_deep_subresources_mode);
|
||||
} else {
|
||||
return duplicate(false);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
if (thread_duplicate_remap_cache) {
|
||||
Resource::DuplicateRemapCacheT::Iterator E = thread_duplicate_remap_cache->find(Ref<Resource>(this));
|
||||
if (E) {
|
||||
return E->value;
|
||||
}
|
||||
} else {
|
||||
thread_duplicate_remap_cache = memnew(DuplicateRemapCacheT);
|
||||
}
|
||||
|
||||
DuplicateParams params;
|
||||
params.deep = p_deep;
|
||||
params.subres_mode = p_deep_subresources_mode;
|
||||
|
||||
const Ref<Resource> dupe = _duplicate(params);
|
||||
|
||||
return dupe;
|
||||
}
|
||||
|
||||
void Resource::_teardown_duplicate_from_variant() {
|
||||
if (thread_duplicate_remap_cache) {
|
||||
memdelete(thread_duplicate_remap_cache);
|
||||
thread_duplicate_remap_cache = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::_set_path(const String &p_path) {
|
||||
@@ -583,7 +723,14 @@ void Resource::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("emit_changed"), &Resource::emit_changed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("duplicate", "deep"), &Resource::duplicate, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("duplicate_deep", "deep_subresources_mode"), &Resource::duplicate_deep, DEFVAL(RESOURCE_DEEP_DUPLICATE_INTERNAL));
|
||||
|
||||
// For the bindings, it's much more natural to expose this enum from the Variant realm via Resource.
|
||||
ClassDB::bind_integer_constant(get_class_static(), StringName("ResourceDeepDuplicateMode"), "RESOURCE_DEEP_DUPLICATE_NONE", RESOURCE_DEEP_DUPLICATE_NONE);
|
||||
ClassDB::bind_integer_constant(get_class_static(), StringName("ResourceDeepDuplicateMode"), "RESOURCE_DEEP_DUPLICATE_INTERNAL", RESOURCE_DEEP_DUPLICATE_INTERNAL);
|
||||
ClassDB::bind_integer_constant(get_class_static(), StringName("ResourceDeepDuplicateMode"), "RESOURCE_DEEP_DUPLICATE_ALL", RESOURCE_DEEP_DUPLICATE_ALL);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("changed"));
|
||||
ADD_SIGNAL(MethodInfo("setup_local_to_scene_requested"));
|
||||
|
||||
|
||||
@@ -57,6 +57,13 @@ public:
|
||||
static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension("res", get_class_static()); }
|
||||
virtual String get_base_extension() const { return "res"; }
|
||||
|
||||
protected:
|
||||
struct DuplicateParams {
|
||||
bool deep = false;
|
||||
ResourceDeepDuplicateMode subres_mode = RESOURCE_DEEP_DUPLICATE_MAX;
|
||||
Node *local_scene = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class ResBase;
|
||||
friend class ResourceCache;
|
||||
@@ -83,7 +90,10 @@ private:
|
||||
|
||||
SelfList<Resource> remapped_list;
|
||||
|
||||
void _dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
|
||||
using DuplicateRemapCacheT = HashMap<Ref<Resource>, Ref<Resource>>;
|
||||
static thread_local inline DuplicateRemapCacheT *thread_duplicate_remap_cache = nullptr;
|
||||
|
||||
Variant _duplicate_recursive(const Variant &p_variant, const DuplicateParams &p_params, uint32_t p_usage = 0) const;
|
||||
void _find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found);
|
||||
|
||||
protected:
|
||||
@@ -104,6 +114,8 @@ protected:
|
||||
GDVIRTUAL1C(_set_path_cache, String);
|
||||
GDVIRTUAL0(_reset_state);
|
||||
|
||||
virtual Ref<Resource> _duplicate(const DuplicateParams &p_params) const;
|
||||
|
||||
public:
|
||||
static Node *(*_get_local_scene_func)(); //used by editor
|
||||
static void (*_update_configuration_warning)(); //used by editor
|
||||
@@ -131,8 +143,11 @@ public:
|
||||
void set_scene_unique_id(const String &p_id);
|
||||
String get_scene_unique_id() const;
|
||||
|
||||
virtual Ref<Resource> duplicate(bool p_subresources = false) const;
|
||||
Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
|
||||
Ref<Resource> duplicate(bool p_deep = false) const;
|
||||
Ref<Resource> duplicate_deep(ResourceDeepDuplicateMode p_deep_subresources_mode = RESOURCE_DEEP_DUPLICATE_INTERNAL) const;
|
||||
Ref<Resource> _duplicate_from_variant(bool p_deep, ResourceDeepDuplicateMode p_deep_subresources_mode, int p_recursion_count) const;
|
||||
static void _teardown_duplicate_from_variant();
|
||||
Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) const;
|
||||
void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
|
||||
|
||||
void set_local_to_scene(bool p_enable);
|
||||
@@ -168,6 +183,8 @@ public:
|
||||
~Resource();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(ResourceDeepDuplicateMode);
|
||||
|
||||
class ResourceCache {
|
||||
friend class Resource;
|
||||
friend class ResourceLoader; //need the lock
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include "core/templates/vector.h"
|
||||
#include "core/variant/callable.h"
|
||||
#include "core/variant/dictionary.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
struct ArrayPrivate {
|
||||
SafeRefCount refcount;
|
||||
@@ -518,10 +517,14 @@ const Variant &Array::get(int p_idx) const {
|
||||
}
|
||||
|
||||
Array Array::duplicate(bool p_deep) const {
|
||||
return recursive_duplicate(p_deep, 0);
|
||||
return recursive_duplicate(p_deep, RESOURCE_DEEP_DUPLICATE_NONE, 0);
|
||||
}
|
||||
|
||||
Array Array::recursive_duplicate(bool p_deep, int recursion_count) const {
|
||||
Array Array::duplicate_deep(ResourceDeepDuplicateMode p_deep_subresources_mode) const {
|
||||
return recursive_duplicate(true, p_deep_subresources_mode, 0);
|
||||
}
|
||||
|
||||
Array Array::recursive_duplicate(bool p_deep, ResourceDeepDuplicateMode p_deep_subresources_mode, int recursion_count) const {
|
||||
Array new_arr;
|
||||
new_arr._p->typed = _p->typed;
|
||||
|
||||
@@ -531,12 +534,19 @@ Array Array::recursive_duplicate(bool p_deep, int recursion_count) const {
|
||||
}
|
||||
|
||||
if (p_deep) {
|
||||
bool is_call_chain_end = recursion_count == 0;
|
||||
|
||||
recursion_count++;
|
||||
int element_count = size();
|
||||
new_arr.resize(element_count);
|
||||
Variant *write = new_arr._p->array.ptrw();
|
||||
for (int i = 0; i < element_count; i++) {
|
||||
write[i] = get(i).recursive_duplicate(true, recursion_count);
|
||||
write[i] = get(i).recursive_duplicate(true, p_deep_subresources_mode, recursion_count);
|
||||
}
|
||||
|
||||
// Variant::recursive_duplicate() may have created a remap cache by now.
|
||||
if (is_call_chain_end) {
|
||||
Resource::_teardown_duplicate_from_variant();
|
||||
}
|
||||
} else {
|
||||
new_arr._p->array = _p->array;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/typedefs.h"
|
||||
#include "core/variant/variant_deep_duplicate.h"
|
||||
|
||||
#include <climits>
|
||||
#include <initializer_list>
|
||||
@@ -164,7 +165,8 @@ public:
|
||||
Variant pop_at(int p_pos);
|
||||
|
||||
Array duplicate(bool p_deep = false) const;
|
||||
Array recursive_duplicate(bool p_deep, int recursion_count) const;
|
||||
Array duplicate_deep(ResourceDeepDuplicateMode p_deep_subresources_mode = RESOURCE_DEEP_DUPLICATE_INTERNAL) const;
|
||||
Array recursive_duplicate(bool p_deep, ResourceDeepDuplicateMode p_deep_subresources_mode, int recursion_count) const;
|
||||
|
||||
Array slice(int p_begin, int p_end = INT_MAX, int p_step = 1, bool p_deep = false) const;
|
||||
Array filter(const Callable &p_callable) const;
|
||||
|
||||
@@ -569,7 +569,11 @@ const Variant *Dictionary::next(const Variant *p_key) const {
|
||||
}
|
||||
|
||||
Dictionary Dictionary::duplicate(bool p_deep) const {
|
||||
return recursive_duplicate(p_deep, 0);
|
||||
return recursive_duplicate(p_deep, RESOURCE_DEEP_DUPLICATE_NONE, 0);
|
||||
}
|
||||
|
||||
Dictionary Dictionary::duplicate_deep(ResourceDeepDuplicateMode p_deep_subresources_mode) const {
|
||||
return recursive_duplicate(true, p_deep_subresources_mode, 0);
|
||||
}
|
||||
|
||||
void Dictionary::make_read_only() {
|
||||
@@ -581,7 +585,7 @@ bool Dictionary::is_read_only() const {
|
||||
return _p->read_only != nullptr;
|
||||
}
|
||||
|
||||
Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) const {
|
||||
Dictionary Dictionary::recursive_duplicate(bool p_deep, ResourceDeepDuplicateMode p_deep_subresources_mode, int recursion_count) const {
|
||||
Dictionary n;
|
||||
n._p->typed_key = _p->typed_key;
|
||||
n._p->typed_value = _p->typed_value;
|
||||
@@ -592,9 +596,16 @@ Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) con
|
||||
}
|
||||
|
||||
if (p_deep) {
|
||||
bool is_call_chain_end = recursion_count == 0;
|
||||
|
||||
recursion_count++;
|
||||
for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
|
||||
n[E.key.recursive_duplicate(true, recursion_count)] = E.value.recursive_duplicate(true, recursion_count);
|
||||
n[E.key.recursive_duplicate(true, p_deep_subresources_mode, recursion_count)] = E.value.recursive_duplicate(true, p_deep_subresources_mode, recursion_count);
|
||||
}
|
||||
|
||||
// Variant::recursive_duplicate() may have created a remap cache by now.
|
||||
if (is_call_chain_end) {
|
||||
Resource::_teardown_duplicate_from_variant();
|
||||
}
|
||||
} else {
|
||||
for (const KeyValue<Variant, Variant> &E : _p->variant_map) {
|
||||
@@ -643,6 +654,10 @@ bool Dictionary::is_typed_value() const {
|
||||
return _p->typed_value.type != Variant::NIL;
|
||||
}
|
||||
|
||||
bool Dictionary::is_same_instance(const Dictionary &p_other) const {
|
||||
return _p == p_other._p;
|
||||
}
|
||||
|
||||
bool Dictionary::is_same_typed(const Dictionary &p_other) const {
|
||||
return is_same_typed_key(p_other) && is_same_typed_value(p_other);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/pair.h"
|
||||
#include "core/variant/array.h"
|
||||
#include "core/variant/variant_deep_duplicate.h"
|
||||
|
||||
class Variant;
|
||||
|
||||
@@ -98,7 +99,8 @@ public:
|
||||
Array values() const;
|
||||
|
||||
Dictionary duplicate(bool p_deep = false) const;
|
||||
Dictionary recursive_duplicate(bool p_deep, int recursion_count) const;
|
||||
Dictionary duplicate_deep(ResourceDeepDuplicateMode p_deep_subresources_mode = RESOURCE_DEEP_DUPLICATE_INTERNAL) const;
|
||||
Dictionary recursive_duplicate(bool p_deep, ResourceDeepDuplicateMode p_deep_subresources_mode, int recursion_count) const;
|
||||
|
||||
void set_typed(const ContainerType &p_key_type, const ContainerType &p_value_type);
|
||||
void set_typed(uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script);
|
||||
@@ -106,6 +108,7 @@ public:
|
||||
bool is_typed() const;
|
||||
bool is_typed_key() const;
|
||||
bool is_typed_value() const;
|
||||
bool is_same_instance(const Dictionary &p_other) const;
|
||||
bool is_same_typed(const Dictionary &p_other) const;
|
||||
bool is_same_typed_key(const Dictionary &p_other) const;
|
||||
bool is_same_typed_value(const Dictionary &p_other) const;
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#include "core/variant/array.h"
|
||||
#include "core/variant/callable.h"
|
||||
#include "core/variant/dictionary.h"
|
||||
#include "core/variant/variant_deep_duplicate.h"
|
||||
|
||||
class Object;
|
||||
class RefCounted;
|
||||
@@ -612,7 +613,8 @@ public:
|
||||
|
||||
void zero();
|
||||
Variant duplicate(bool p_deep = false) const;
|
||||
Variant recursive_duplicate(bool p_deep, int recursion_count) const;
|
||||
Variant duplicate_deep(ResourceDeepDuplicateMode p_deep_subresources_mode = RESOURCE_DEEP_DUPLICATE_INTERNAL) const;
|
||||
Variant recursive_duplicate(bool p_deep, ResourceDeepDuplicateMode p_deep_subresources_mode, int recursion_count) const;
|
||||
|
||||
/* Built-In Methods */
|
||||
|
||||
|
||||
@@ -2408,6 +2408,7 @@ static void _register_variant_builtin_methods_misc() {
|
||||
bind_method(Dictionary, keys, sarray(), varray());
|
||||
bind_method(Dictionary, values, sarray(), varray());
|
||||
bind_method(Dictionary, duplicate, sarray("deep"), varray(false));
|
||||
bind_method(Dictionary, duplicate_deep, sarray("deep_subresources_mode"), varray(RESOURCE_DEEP_DUPLICATE_INTERNAL));
|
||||
bind_method(Dictionary, get, sarray("key", "default"), varray(Variant()));
|
||||
bind_method(Dictionary, get_or_add, sarray("key", "default"), varray(Variant()));
|
||||
bind_method(Dictionary, set, sarray("key", "value"), varray());
|
||||
@@ -2466,6 +2467,7 @@ static void _register_variant_builtin_methods_array() {
|
||||
bind_method(Array, bsearch_custom, sarray("value", "func", "before"), varray(true));
|
||||
bind_method(Array, reverse, sarray(), varray());
|
||||
bind_method(Array, duplicate, sarray("deep"), varray(false));
|
||||
bind_method(Array, duplicate_deep, sarray("deep_subresources_mode"), varray(RESOURCE_DEEP_DUPLICATE_INTERNAL));
|
||||
bind_method(Array, slice, sarray("begin", "end", "step", "deep"), varray(INT_MAX, 1, false));
|
||||
bind_method(Array, filter, sarray("method"), varray());
|
||||
bind_method(Array, map, sarray("method"), varray());
|
||||
|
||||
41
core/variant/variant_deep_duplicate.h
Normal file
41
core/variant/variant_deep_duplicate.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/**************************************************************************/
|
||||
/* variant_deep_duplicate.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// This would be ideally declared nested in Variant, but that would cause circular
|
||||
// includes with Array and Dictionary, for instance.
|
||||
// Also, this enum is be exposed via Resource.
|
||||
enum ResourceDeepDuplicateMode {
|
||||
RESOURCE_DEEP_DUPLICATE_NONE,
|
||||
RESOURCE_DEEP_DUPLICATE_INTERNAL,
|
||||
RESOURCE_DEEP_DUPLICATE_ALL,
|
||||
RESOURCE_DEEP_DUPLICATE_MAX
|
||||
};
|
||||
@@ -29,9 +29,10 @@
|
||||
/**************************************************************************/
|
||||
|
||||
#include "variant_setget.h"
|
||||
|
||||
#include "variant_callable.h"
|
||||
|
||||
#include "core/io/resource.h"
|
||||
|
||||
struct VariantSetterGetterInfo {
|
||||
void (*setter)(Variant *base, const Variant *value, bool &valid);
|
||||
void (*getter)(const Variant *base, Variant *value);
|
||||
@@ -1969,26 +1970,33 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
|
||||
}
|
||||
|
||||
Variant Variant::duplicate(bool p_deep) const {
|
||||
return recursive_duplicate(p_deep, 0);
|
||||
return recursive_duplicate(p_deep, RESOURCE_DEEP_DUPLICATE_NONE, 0);
|
||||
}
|
||||
|
||||
Variant Variant::recursive_duplicate(bool p_deep, int recursion_count) const {
|
||||
Variant Variant::duplicate_deep(ResourceDeepDuplicateMode p_deep_subresources_mode) const {
|
||||
ERR_FAIL_INDEX_V(p_deep_subresources_mode, RESOURCE_DEEP_DUPLICATE_MAX, Variant());
|
||||
return recursive_duplicate(true, p_deep_subresources_mode, 0);
|
||||
}
|
||||
|
||||
Variant Variant::recursive_duplicate(bool p_deep, ResourceDeepDuplicateMode p_deep_subresources_mode, int recursion_count) const {
|
||||
switch (type) {
|
||||
case OBJECT: {
|
||||
/* breaks stuff :(
|
||||
if (p_deep && !_get_obj().ref.is_null()) {
|
||||
Ref<Resource> resource = _get_obj().ref;
|
||||
if (resource.is_valid()) {
|
||||
return resource->duplicate(true);
|
||||
}
|
||||
// If the root target of duplicate() is a Resource, we can't early-reject because that
|
||||
// resource itself must be duplicated, much as if Resource::duplicate() had been called.
|
||||
if (p_deep_subresources_mode == RESOURCE_DEEP_DUPLICATE_NONE && recursion_count > 0) {
|
||||
return *this;
|
||||
}
|
||||
Resource *res = Object::cast_to<Resource>(_get_obj().obj);
|
||||
if (res) {
|
||||
return res->_duplicate_from_variant(p_deep, p_deep_subresources_mode, recursion_count);
|
||||
} else {
|
||||
return *this;
|
||||
}
|
||||
*/
|
||||
return *this;
|
||||
} break;
|
||||
case DICTIONARY:
|
||||
return operator Dictionary().recursive_duplicate(p_deep, recursion_count);
|
||||
return operator Dictionary().recursive_duplicate(p_deep, p_deep_subresources_mode, recursion_count);
|
||||
case ARRAY:
|
||||
return operator Array().recursive_duplicate(p_deep, recursion_count);
|
||||
return operator Array().recursive_duplicate(p_deep, p_deep_subresources_mode, recursion_count);
|
||||
case PACKED_BYTE_ARRAY:
|
||||
return operator Vector<uint8_t>().duplicate();
|
||||
case PACKED_INT32_ARRAY:
|
||||
|
||||
Reference in New Issue
Block a user