Explorar o código

Improve resource load cache

-Added a new method in Resource: reset_state , used for reloading the same resource from disk
-Added a new cache mode "replace" in ResourceLoader, which reuses existing loaded sub-resources but resets their data from disk (or replaces them if they chaged type)
-Because the correct sub-resource paths are always loaded now, this fixes bugs with subresource folding or subresource ordering when saving.
reduz %!s(int64=4) %!d(string=hai) anos
pai
achega
f8d03b98e7
Modificáronse 83 ficheiros con 395 adicións e 183 borrados
  1. 7 3
      core/core_bind.cpp
  2. 8 1
      core/core_bind.h
  3. 1 1
      core/crypto/crypto.cpp
  4. 1 1
      core/crypto/crypto.h
  5. 1 1
      core/io/image_loader.cpp
  6. 1 1
      core/io/image_loader.h
  7. 24 11
      core/io/resource.cpp
  8. 2 0
      core/io/resource.h
  9. 39 30
      core/io/resource_format_binary.cpp
  10. 2 2
      core/io/resource_format_binary.h
  11. 2 2
      core/io/resource_importer.cpp
  12. 1 1
      core/io/resource_importer.h
  13. 12 10
      core/io/resource_loader.cpp
  14. 12 4
      core/io/resource_loader.h
  15. 1 1
      core/io/translation_loader_po.cpp
  16. 1 1
      core/io/translation_loader_po.h
  17. 1 1
      drivers/dummy/texture_loader_dummy.cpp
  18. 1 1
      drivers/dummy/texture_loader_dummy.h
  19. 3 3
      editor/editor_audio_buses.cpp
  20. 10 6
      editor/editor_folding.cpp
  21. 6 2
      editor/editor_node.cpp
  22. 3 3
      editor/import/resource_importer_scene.cpp
  23. 1 1
      editor/plugins/packed_scene_translation_parser_plugin.cpp
  24. 1 1
      editor/plugins/script_editor_plugin.cpp
  25. 1 1
      editor/plugins/script_text_editor.cpp
  26. 1 1
      editor/plugins/shader_editor_plugin.cpp
  27. 1 1
      modules/dds/texture_loader_dds.cpp
  28. 1 1
      modules/dds/texture_loader_dds.h
  29. 1 1
      modules/etc/texture_loader_pkm.cpp
  30. 1 1
      modules/etc/texture_loader_pkm.h
  31. 11 1
      modules/gdnative/gdnative.cpp
  32. 3 1
      modules/gdnative/gdnative.h
  33. 1 1
      modules/gdnative/nativescript/nativescript.cpp
  34. 1 1
      modules/gdnative/nativescript/nativescript.h
  35. 1 1
      modules/gdnative/pluginscript/pluginscript_loader.cpp
  36. 1 1
      modules/gdnative/pluginscript/pluginscript_loader.h
  37. 1 1
      modules/gdnative/videodecoder/video_stream_gdnative.cpp
  38. 1 1
      modules/gdnative/videodecoder/video_stream_gdnative.h
  39. 1 1
      modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
  40. 1 1
      modules/gdscript/gdscript.cpp
  41. 1 1
      modules/gdscript/gdscript.h
  42. 1 1
      modules/mono/csharp_script.cpp
  43. 1 1
      modules/mono/csharp_script.h
  44. 1 1
      modules/pvr/texture_loader_pvr.cpp
  45. 1 1
      modules/pvr/texture_loader_pvr.h
  46. 1 1
      modules/theora/video_stream_theora.cpp
  47. 1 1
      modules/theora/video_stream_theora.h
  48. 13 0
      modules/visual_script/visual_script_expression.cpp
  49. 2 0
      modules/visual_script/visual_script_expression.h
  50. 4 0
      modules/visual_script/visual_script_flow_control.cpp
  51. 2 0
      modules/visual_script/visual_script_flow_control.h
  52. 15 0
      modules/visual_script/visual_script_nodes.cpp
  53. 4 0
      modules/visual_script/visual_script_nodes.h
  54. 1 1
      modules/webm/video_stream_webm.cpp
  55. 1 1
      modules/webm/video_stream_webm.h
  56. 7 0
      scene/animation/animation_blend_tree.cpp
  57. 2 0
      scene/animation/animation_blend_tree.h
  58. 12 0
      scene/animation/animation_node_state_machine.cpp
  59. 2 0
      scene/animation/animation_node_state_machine.h
  60. 4 0
      scene/resources/animation.cpp
  61. 2 0
      scene/resources/animation.h
  62. 18 2
      scene/resources/font.cpp
  63. 6 2
      scene/resources/font.h
  64. 9 0
      scene/resources/mesh.cpp
  65. 2 0
      scene/resources/mesh.h
  66. 3 0
      scene/resources/mesh_library.cpp
  67. 1 0
      scene/resources/mesh_library.h
  68. 3 0
      scene/resources/packed_scene.cpp
  69. 1 0
      scene/resources/packed_scene.h
  70. 71 56
      scene/resources/resource_format_text.cpp
  71. 2 2
      scene/resources/resource_format_text.h
  72. 1 1
      scene/resources/shader.cpp
  73. 1 1
      scene/resources/shader.h
  74. 4 0
      scene/resources/skin.cpp
  75. 1 0
      scene/resources/skin.h
  76. 3 3
      scene/resources/texture.cpp
  77. 3 3
      scene/resources/texture.h
  78. 3 0
      scene/resources/theme.cpp
  79. 2 0
      scene/resources/theme.h
  80. 4 0
      scene/resources/tile_set.cpp
  81. 2 0
      scene/resources/tile_set.h
  82. 6 0
      scene/resources/visual_shader.cpp
  83. 2 0
      scene/resources/visual_shader.h

+ 7 - 3
core/core_bind.cpp

@@ -86,9 +86,9 @@ RES _ResourceLoader::load_threaded_get(const String &p_path) {
 	return res;
 }
 
-RES _ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache) {
+RES _ResourceLoader::load(const String &p_path, const String &p_type_hint, CacheMode p_cache_mode) {
 	Error err = OK;
-	RES ret = ResourceLoader::load(p_path, p_type_hint, p_no_cache, &err);
+	RES ret = ResourceLoader::load(p_path, p_type_hint, ResourceFormatLoader::CacheMode(p_cache_mode), &err);
 
 	ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'.");
 	return ret;
@@ -135,7 +135,7 @@ void _ResourceLoader::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &_ResourceLoader::load_threaded_get_status, DEFVAL(Array()));
 	ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &_ResourceLoader::load_threaded_get);
 
-	ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "no_cache"), &_ResourceLoader::load, DEFVAL(""), DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "no_cache"), &_ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE));
 	ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &_ResourceLoader::get_recognized_extensions_for_type);
 	ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &_ResourceLoader::set_abort_on_missing_resources);
 	ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies);
@@ -146,6 +146,10 @@ void _ResourceLoader::_bind_methods() {
 	BIND_ENUM_CONSTANT(THREAD_LOAD_IN_PROGRESS);
 	BIND_ENUM_CONSTANT(THREAD_LOAD_FAILED);
 	BIND_ENUM_CONSTANT(THREAD_LOAD_LOADED);
+
+	BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE);
+	BIND_ENUM_CONSTANT(CACHE_MODE_REUSE);
+	BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE);
 }
 
 ////// _ResourceSaver //////

+ 8 - 1
core/core_bind.h

@@ -56,13 +56,19 @@ public:
 		THREAD_LOAD_LOADED
 	};
 
+	enum CacheMode {
+		CACHE_MODE_IGNORE, //resource and subresources do not use path cache, no path is set into resource.
+		CACHE_MODE_REUSE, //resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available
+		CACHE_MODE_REPLACE, //resource and and subresource use path cache, but replace existing loaded resources when available with information from disk
+	};
+
 	static _ResourceLoader *get_singleton() { return singleton; }
 
 	Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false);
 	ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = Array());
 	RES load_threaded_get(const String &p_path);
 
-	RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false);
+	RES load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE);
 	Vector<String> get_recognized_extensions_for_type(const String &p_type);
 	void set_abort_on_missing_resources(bool p_abort);
 	PackedStringArray get_dependencies(const String &p_path);
@@ -73,6 +79,7 @@ public:
 };
 
 VARIANT_ENUM_CAST(_ResourceLoader::ThreadLoadStatus);
+VARIANT_ENUM_CAST(_ResourceLoader::CacheMode);
 
 class _ResourceSaver : public Object {
 	GDCLASS(_ResourceSaver, Object);

+ 1 - 1
core/crypto/crypto.cpp

@@ -141,7 +141,7 @@ void Crypto::_bind_methods() {
 
 /// Resource loader/saver
 
-RES ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderCrypto::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) {
 	String el = p_path.get_extension().to_lower();
 	if (el == "crt") {
 		X509Certificate *cert = X509Certificate::create();

+ 1 - 1
core/crypto/crypto.h

@@ -116,7 +116,7 @@ public:
 
 class ResourceFormatLoaderCrypto : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 1 - 1
core/io/image_loader.cpp

@@ -122,7 +122,7 @@ void ImageLoader::cleanup() {
 
 /////////////////
 
-RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderImage::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) {
 	FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
 	if (!f) {
 		if (r_error) {

+ 1 - 1
core/io/image_loader.h

@@ -72,7 +72,7 @@ public:
 
 class ResourceFormatLoaderImage : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 24 - 11
core/io/resource.cpp

@@ -114,20 +114,18 @@ bool Resource::editor_can_reload_from_file() {
 	return true; //by default yes
 }
 
-void Resource::reload_from_file() {
-	String path = get_path();
-	if (!path.is_resource_file()) {
-		return;
+void Resource::reset_state() {
+}
+Error Resource::copy_from(const Ref<Resource> &p_resource) {
+	ERR_FAIL_COND_V(p_resource.is_null(), ERR_INVALID_PARAMETER);
+	if (get_class() != p_resource->get_class()) {
+		return ERR_INVALID_PARAMETER;
 	}
 
-	Ref<Resource> s = ResourceLoader::load(ResourceLoader::path_remap(path), get_class(), true);
-
-	if (!s.is_valid()) {
-		return;
-	}
+	reset_state(); //may want to reset state
 
 	List<PropertyInfo> pi;
-	s->get_property_list(&pi);
+	p_resource->get_property_list(&pi);
 
 	for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
 		if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
@@ -137,8 +135,23 @@ void Resource::reload_from_file() {
 			continue; //do not change path
 		}
 
-		set(E->get().name, s->get(E->get().name));
+		set(E->get().name, p_resource->get(E->get().name));
 	}
+	return OK;
+}
+void Resource::reload_from_file() {
+	String path = get_path();
+	if (!path.is_resource_file()) {
+		return;
+	}
+
+	Ref<Resource> s = ResourceLoader::load(ResourceLoader::path_remap(path), get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
+
+	if (!s.is_valid()) {
+		return;
+	}
+
+	copy_from(s);
 }
 
 Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource>> &remap_cache) {

+ 2 - 0
core/io/resource.h

@@ -90,6 +90,8 @@ public:
 	static Node *(*_get_local_scene_func)(); //used by editor
 
 	virtual bool editor_can_reload_from_file();
+	virtual void reset_state(); //for resources that use variable amount of properties, either via _validate_property or _get_property_list, this function needs to be implemented to correctly clear state
+	virtual Error copy_from(const Ref<Resource> &p_resource);
 	virtual void reload_from_file();
 
 	void register_owner(Object *p_owner);

+ 39 - 30
core/io/resource_format_binary.cpp

@@ -313,17 +313,12 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
 					uint32_t index = f->get_32();
 					String path = res_path + "::" + itos(index);
 
-					if (use_nocache) {
-						if (!internal_index_cache.has(path)) {
-							WARN_PRINT(String("Couldn't load resource (no cache): " + path).utf8().get_data());
-						}
-						r_v = internal_index_cache[path];
+					//always use internal cache for loading internal resources
+					if (!internal_index_cache.has(path)) {
+						WARN_PRINT(String("Couldn't load resource (no cache): " + path).utf8().get_data());
+						r_v = Variant();
 					} else {
-						RES res = ResourceLoader::load(path);
-						if (res.is_null()) {
-							WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
-						}
-						r_v = res;
+						r_v = internal_index_cache[path];
 					}
 
 				} break;
@@ -645,7 +640,7 @@ Error ResourceLoaderBinary::load() {
 			}
 
 		} else {
-			Error err = ResourceLoader::load_threaded_request(path, external_resources[i].type, use_sub_threads, local_path);
+			Error err = ResourceLoader::load_threaded_request(path, external_resources[i].type, use_sub_threads, ResourceFormatLoader::CACHE_MODE_REUSE, local_path);
 			if (err != OK) {
 				if (!ResourceLoader::get_abort_on_missing_resources()) {
 					ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type);
@@ -675,7 +670,7 @@ Error ResourceLoaderBinary::load() {
 				path = res_path + "::" + path;
 			}
 
-			if (!use_nocache) {
+			if (cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) {
 				if (ResourceCache::has(path)) {
 					//already loaded, don't do anything
 					stage++;
@@ -684,7 +679,7 @@ Error ResourceLoaderBinary::load() {
 				}
 			}
 		} else {
-			if (!use_nocache && !ResourceCache::has(res_path)) {
+			if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && !ResourceCache::has(res_path)) {
 				path = res_path;
 			}
 		}
@@ -695,26 +690,40 @@ Error ResourceLoaderBinary::load() {
 
 		String t = get_unicode_string();
 
-		Object *obj = ClassDB::instance(t);
-		if (!obj) {
-			error = ERR_FILE_CORRUPT;
-			ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + ".");
-		}
+		RES res;
 
-		Resource *r = Object::cast_to<Resource>(obj);
-		if (!r) {
-			String obj_class = obj->get_class();
-			error = ERR_FILE_CORRUPT;
-			memdelete(obj); //bye
-			ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + ".");
+		if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(path)) {
+			//use the existing one
+			Resource *r = ResourceCache::get(path);
+			if (r->get_class() == t) {
+				r->reset_state();
+				res = Ref<Resource>(r);
+			}
 		}
 
-		RES res = RES(r);
+		if (res.is_null()) {
+			//did not replace
 
-		if (path != String()) {
-			r->set_path(path);
+			Object *obj = ClassDB::instance(t);
+			if (!obj) {
+				error = ERR_FILE_CORRUPT;
+				ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + ".");
+			}
+
+			Resource *r = Object::cast_to<Resource>(obj);
+			if (!r) {
+				String obj_class = obj->get_class();
+				error = ERR_FILE_CORRUPT;
+				memdelete(obj); //bye
+				ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + ".");
+			}
+
+			res = RES(r);
+			if (path != String() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+				r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it
+			}
+			r->set_subindex(subindex);
 		}
-		r->set_subindex(subindex);
 
 		if (!main) {
 			internal_index_cache[path] = res;
@@ -961,7 +970,7 @@ ResourceLoaderBinary::~ResourceLoaderBinary() {
 	}
 }
 
-RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderBinary::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) {
 	if (r_error) {
 		*r_error = ERR_FILE_CANT_OPEN;
 	}
@@ -972,7 +981,7 @@ RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_origi
 	ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot open file '" + p_path + "'.");
 
 	ResourceLoaderBinary loader;
-	loader.use_nocache = p_no_cache;
+	loader.cache_mode = p_cache_mode;
 	loader.use_sub_threads = p_use_sub_threads;
 	loader.progress = r_progress;
 	String path = p_original_path != "" ? p_original_path : p_path;

+ 2 - 2
core/io/resource_format_binary.h

@@ -78,7 +78,7 @@ class ResourceLoaderBinary {
 	Map<String, String> remaps;
 	Error error = OK;
 
-	bool use_nocache = false;
+	ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
 
 	friend class ResourceFormatLoaderBinary;
 
@@ -103,7 +103,7 @@ public:
 
 class ResourceFormatLoaderBinary : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
 	virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual bool handles_type(const String &p_type) const;

+ 2 - 2
core/io/resource_importer.cpp

@@ -116,7 +116,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
 	return OK;
 }
 
-RES ResourceFormatImporter::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatImporter::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) {
 	PathAndType pat;
 	Error err = _get_path_and_type(p_path, pat);
 
@@ -128,7 +128,7 @@ RES ResourceFormatImporter::load(const String &p_path, const String &p_original_
 		return RES();
 	}
 
-	RES res = ResourceLoader::_load(pat.path, p_path, pat.type, p_no_cache, r_error, p_use_sub_threads, r_progress);
+	RES res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, r_error, p_use_sub_threads, r_progress);
 
 #ifdef TOOLS_ENABLED
 	if (res.is_valid()) {

+ 1 - 1
core/io/resource_importer.h

@@ -57,7 +57,7 @@ class ResourceFormatImporter : public ResourceFormatLoader {
 
 public:
 	static ResourceFormatImporter *get_singleton() { return singleton; }
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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 void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
 	virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;

+ 12 - 10
core/io/resource_loader.cpp

@@ -113,9 +113,9 @@ void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions)
 	}
 }
 
-RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoader::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) {
 	if (get_script_instance() && get_script_instance()->has_method("load")) {
-		Variant res = get_script_instance()->call("load", p_path, p_original_path, p_use_sub_threads);
+		Variant res = get_script_instance()->call("load", p_path, p_original_path, p_use_sub_threads, p_cache_mode);
 
 		if (res.get_type() == Variant::INT) {
 			if (r_error) {
@@ -164,7 +164,7 @@ Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map<
 
 void ResourceFormatLoader::_bind_methods() {
 	{
-		MethodInfo info = MethodInfo(Variant::NIL, "load", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "original_path"));
+		MethodInfo info = MethodInfo(Variant::NIL, "load", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "original_path"), PropertyInfo(Variant::BOOL, "use_sub_threads"), PropertyInfo(Variant::INT, "cache_mode"));
 		info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
 		ClassDB::add_virtual_method(get_class_static(), info);
 	}
@@ -178,7 +178,7 @@ void ResourceFormatLoader::_bind_methods() {
 
 ///////////////////////////////////
 
-RES ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error, bool p_use_sub_threads, float *r_progress) {
+RES ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) {
 	bool found = false;
 
 	// Try all loaders and pick the first match for the type hint
@@ -187,7 +187,7 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c
 			continue;
 		}
 		found = true;
-		RES res = loader[i]->load(p_path, p_original_path != String() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_no_cache);
+		RES res = loader[i]->load(p_path, p_original_path != String() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
 		if (res.is_null()) {
 			continue;
 		}
@@ -214,7 +214,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
 		//this is an actual thread, so wait for Ok fom semaphore
 		thread_load_semaphore->wait(); //wait until its ok to start loading
 	}
-	load_task.resource = _load(load_task.remapped_path, load_task.remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, false, &load_task.error, load_task.use_sub_threads, &load_task.progress);
+	load_task.resource = _load(load_task.remapped_path, load_task.remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_task.error, load_task.use_sub_threads, &load_task.progress);
 
 	load_task.progress = 1.0; //it was fully loaded at this point, so force progress to 1.0
 
@@ -267,7 +267,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
 	thread_load_mutex->unlock();
 }
 
-Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, const String &p_source_resource) {
+Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) {
 	String local_path;
 	if (p_path.is_rel_path()) {
 		local_path = "res://" + p_path;
@@ -314,6 +314,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &
 		load_task.remapped_path = _path_remap(local_path, &load_task.xl_remapped);
 		load_task.local_path = local_path;
 		load_task.type_hint = p_type_hint;
+		load_task.cache_mode = p_cache_mode;
 		load_task.use_sub_threads = p_use_sub_threads;
 
 		{ //must check if resource is already loaded before attempting to load it in a thread
@@ -501,7 +502,7 @@ RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) {
 	return resource;
 }
 
-RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) {
+RES ResourceLoader::load(const String &p_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error) {
 	if (r_error) {
 		*r_error = ERR_CANT_OPEN;
 	}
@@ -513,7 +514,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
 		local_path = ProjectSettings::get_singleton()->localize_path(p_path);
 	}
 
-	if (!p_no_cache) {
+	if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE) {
 		thread_load_mutex->lock();
 
 		//Is it already being loaded? poll until done
@@ -561,6 +562,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
 		load_task.local_path = local_path;
 		load_task.remapped_path = _path_remap(local_path, &load_task.xl_remapped);
 		load_task.type_hint = p_type_hint;
+		load_task.cache_mode = p_cache_mode; //ignore
 		load_task.loader_id = Thread::get_caller_id();
 
 		thread_load_tasks[local_path] = load_task;
@@ -581,7 +583,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
 
 		print_verbose("Loading resource: " + path);
 		float p;
-		RES res = _load(path, local_path, p_type_hint, p_no_cache, r_error, false, &p);
+		RES res = _load(path, local_path, p_type_hint, p_cache_mode, r_error, false, &p);
 
 		if (res.is_null()) {
 			print_verbose("Failed loading resource: " + path);

+ 12 - 4
core/io/resource_loader.h

@@ -38,11 +38,18 @@
 class ResourceFormatLoader : public Reference {
 	GDCLASS(ResourceFormatLoader, Reference);
 
+public:
+	enum CacheMode {
+		CACHE_MODE_IGNORE, //resource and subresources do not use path cache, no path is set into resource.
+		CACHE_MODE_REUSE, //resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available
+		CACHE_MODE_REPLACE, //resource and and subresource use path cache, but replace existing loaded resources when available with information from disk
+	};
+
 protected:
 	static void _bind_methods();
 
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
 	virtual bool exists(const String &p_path) const;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
@@ -99,7 +106,7 @@ private:
 	friend class ResourceFormatImporter;
 	friend class ResourceInteractiveLoader;
 	//internal load function
-	static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error, bool p_use_sub_threads, float *r_progress);
+	static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress);
 
 	static ResourceLoadedCallback _loaded_callback;
 
@@ -114,6 +121,7 @@ private:
 		String type_hint;
 		float progress = 0.0;
 		ThreadLoadStatus status = THREAD_LOAD_IN_PROGRESS;
+		ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
 		Error error = OK;
 		RES resource;
 		bool xl_remapped = false;
@@ -136,11 +144,11 @@ private:
 	static float _dependency_get_progress(const String &p_path);
 
 public:
-	static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, const String &p_source_resource = String());
+	static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE, const String &p_source_resource = String());
 	static ThreadLoadStatus load_threaded_get_status(const String &p_path, float *r_progress = nullptr);
 	static RES load_threaded_get(const String &p_path, Error *r_error = nullptr);
 
-	static RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = nullptr);
+	static RES load(const String &p_path, const String &p_type_hint = "", ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE, Error *r_error = nullptr);
 	static bool exists(const String &p_path, const String &p_type_hint = "");
 
 	static void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions);

+ 1 - 1
core/io/translation_loader_po.cpp

@@ -277,7 +277,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {
 	return translation;
 }
 
-RES TranslationLoaderPO::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES TranslationLoaderPO::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) {
 	if (r_error) {
 		*r_error = ERR_CANT_OPEN;
 	}

+ 1 - 1
core/io/translation_loader_po.h

@@ -38,7 +38,7 @@
 class TranslationLoaderPO : public ResourceFormatLoader {
 public:
 	static RES load_translation(FileAccess *f, Error *r_error = nullptr);
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 1 - 1
drivers/dummy/texture_loader_dummy.cpp

@@ -35,7 +35,7 @@
 
 #include <string.h>
 
-RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatDummyTexture::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) {
 	unsigned int width = 8;
 	unsigned int height = 8;
 

+ 1 - 1
drivers/dummy/texture_loader_dummy.h

@@ -36,7 +36,7 @@
 
 class ResourceFormatDummyTexture : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 3 - 3
editor/editor_audio_buses.cpp

@@ -1193,7 +1193,7 @@ void EditorAudioBuses::_load_layout() {
 void EditorAudioBuses::_load_default_layout() {
 	String layout_path = ProjectSettings::get_singleton()->get("audio/default_bus_layout");
 
-	Ref<AudioBusLayout> state = ResourceLoader::load(layout_path, "", true);
+	Ref<AudioBusLayout> state = ResourceLoader::load(layout_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
 	if (state.is_null()) {
 		EditorNode::get_singleton()->show_warning(vformat(TTR("There is no '%s' file."), layout_path));
 		return;
@@ -1209,7 +1209,7 @@ void EditorAudioBuses::_load_default_layout() {
 
 void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
 	if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_OPEN_FILE) {
-		Ref<AudioBusLayout> state = ResourceLoader::load(p_string, "", true);
+		Ref<AudioBusLayout> state = ResourceLoader::load(p_string, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
 		if (state.is_null()) {
 			EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an audio bus layout."));
 			return;
@@ -1330,7 +1330,7 @@ EditorAudioBuses::EditorAudioBuses() {
 void EditorAudioBuses::open_layout(const String &p_path) {
 	EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
 
-	Ref<AudioBusLayout> state = ResourceLoader::load(p_path, "", true);
+	Ref<AudioBusLayout> state = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
 	if (state.is_null()) {
 		EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an audio bus layout."));
 		return;

+ 10 - 6
editor/editor_folding.cpp

@@ -259,13 +259,17 @@ void EditorFolding::_do_object_unfolds(Object *p_object, Set<RES> &resources) {
 					}
 				}
 			}
-		}
 
-		if (E->get().type == Variant::OBJECT) {
-			RES res = p_object->get(E->get().name);
-			if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) {
-				resources.insert(res);
-				_do_object_unfolds(res.ptr(), resources);
+			if (E->get().type == Variant::OBJECT) {
+				RES res = p_object->get(E->get().name);
+				print_line("res: " + String(E->get().name) + " valid " + itos(res.is_valid()));
+				if (res.is_valid()) {
+					print_line("path " + res->get_path());
+				}
+				if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) {
+					resources.insert(res);
+					_do_object_unfolds(res.ptr(), resources);
+				}
 			}
 		}
 	}

+ 6 - 2
editor/editor_node.cpp

@@ -1003,7 +1003,7 @@ Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_d
 	dependency_errors.clear();
 
 	Error err;
-	RES res = ResourceLoader::load(p_resource, "", false, &err);
+	RES res = ResourceLoader::load(p_resource, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
 	ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN);
 
 	if (!p_ignore_broken_deps && dependency_errors.has(p_resource)) {
@@ -3452,7 +3452,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
 	dependency_errors.clear();
 
 	Error err;
-	Ref<PackedScene> sdata = ResourceLoader::load(lpath, "", true, &err);
+	Ref<PackedScene> sdata = ResourceLoader::load(lpath, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
 	if (!sdata.is_valid()) {
 		_dialog_display_load_error(lpath, err);
 		opening_prev = false;
@@ -5274,6 +5274,8 @@ void EditorNode::_file_access_close_error_notify(const String &p_str) {
 }
 
 void EditorNode::reload_scene(const String &p_path) {
+	/*
+	 * No longer necesary since scenes now reset and reload their internal resource if needed.
 	//first of all, reload internal textures, materials, meshes, etc. as they might have changed on disk
 
 	List<Ref<Resource>> cached;
@@ -5291,6 +5293,8 @@ void EditorNode::reload_scene(const String &p_path) {
 		to_clear.pop_front();
 	}
 
+	*/
+
 	int scene_idx = -1;
 	for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
 		if (editor_data.get_scene_path(i) == p_path) {

+ 3 - 3
editor/import/resource_importer_scene.cpp

@@ -964,7 +964,7 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String
 
 					if (FileAccess::exists(ext_name) && p_keep_animations) {
 						// Copy custom animation tracks from previously imported files.
-						Ref<Animation> old_anim = ResourceLoader::load(ext_name, "Animation", true);
+						Ref<Animation> old_anim = ResourceLoader::load(ext_name, "Animation", ResourceFormatLoader::CACHE_MODE_IGNORE);
 						if (old_anim.is_valid()) {
 							for (int i = 0; i < old_anim->get_track_count(); i++) {
 								if (!old_anim->track_is_imported(i)) {
@@ -1004,7 +1004,7 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String
 						p_materials[mat] = ResourceLoader::load(ext_name);
 					} else {
 						ResourceSaver::save(ext_name, mat, ResourceSaver::FLAG_CHANGE_PATH);
-						p_materials[mat] = ResourceLoader::load(ext_name, "", true); // disable loading from the cache.
+						p_materials[mat] = ResourceLoader::load(ext_name, "", ResourceFormatLoader::CACHE_MODE_IGNORE); // disable loading from the cache.
 					}
 				}
 
@@ -1061,7 +1061,7 @@ void ResourceImporterScene::_make_external_resources(Node *p_node, const String
 										p_materials[mat] = ResourceLoader::load(ext_name);
 									} else {
 										ResourceSaver::save(ext_name, mat, ResourceSaver::FLAG_CHANGE_PATH);
-										p_materials[mat] = ResourceLoader::load(ext_name, "", true); // disable loading from the cache.
+										p_materials[mat] = ResourceLoader::load(ext_name, "", ResourceFormatLoader::CACHE_MODE_IGNORE); // disable loading from the cache.
 									}
 								}
 

+ 1 - 1
editor/plugins/packed_scene_translation_parser_plugin.cpp

@@ -42,7 +42,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
 	// These properties are translated with the tr() function in the C++ code when being set or updated.
 
 	Error err;
-	RES loaded_res = ResourceLoader::load(p_path, "PackedScene", false, &err);
+	RES loaded_res = ResourceLoader::load(p_path, "PackedScene", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
 	if (err) {
 		ERR_PRINT("Failed to load " + p_path);
 		return err;

+ 1 - 1
editor/plugins/script_editor_plugin.cpp

@@ -894,7 +894,7 @@ void ScriptEditor::_reload_scripts() {
 
 		Ref<Script> script = edited_res;
 		if (script != nullptr) {
-			Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), true);
+			Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
 			ERR_CONTINUE(!rel_script.is_valid());
 			script->set_source_code(rel_script->get_source_code());
 			script->set_last_modified_time(rel_script->get_last_modified_time());

+ 1 - 1
editor/plugins/script_text_editor.cpp

@@ -688,7 +688,7 @@ void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_fo
 		uint64_t date = FileAccess::get_modified_time(script->get_path());
 
 		if (last_date != date) {
-			Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), true);
+			Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
 			ERR_CONTINUE(!rel_script.is_valid());
 			script->set_source_code(rel_script->get_source_code());
 			script->set_last_modified_time(rel_script->get_last_modified_time());

+ 1 - 1
editor/plugins/shader_editor_plugin.cpp

@@ -405,7 +405,7 @@ void ShaderEditor::_check_for_external_edit() {
 }
 
 void ShaderEditor::_reload_shader_from_disk() {
-	Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), true);
+	Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
 	ERR_FAIL_COND(!rel_shader.is_valid());
 
 	shader->set_code(rel_shader->get_code());

+ 1 - 1
modules/dds/texture_loader_dds.cpp

@@ -94,7 +94,7 @@ static const DDSFormatInfo dds_format_info[DDS_MAX] = {
 	{ "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 }
 };
 
-RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatDDS::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) {
 	if (r_error) {
 		*r_error = ERR_CANT_OPEN;
 	}

+ 1 - 1
modules/dds/texture_loader_dds.h

@@ -36,7 +36,7 @@
 
 class ResourceFormatDDS : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 1 - 1
modules/etc/texture_loader_pkm.cpp

@@ -42,7 +42,7 @@ struct ETC1Header {
 	uint16_t origHeight = 0;
 };
 
-RES ResourceFormatPKM::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatPKM::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) {
 	if (r_error) {
 		*r_error = ERR_CANT_OPEN;
 	}

+ 1 - 1
modules/etc/texture_loader_pkm.h

@@ -36,7 +36,7 @@
 
 class ResourceFormatPKM : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 11 - 1
modules/gdnative/gdnative.cpp

@@ -110,6 +110,16 @@ bool GDNativeLibrary::_get(const StringName &p_name, Variant &r_property) const
 	return false;
 }
 
+void GDNativeLibrary::reset_state() {
+	config_file.instance();
+	current_library_path = "";
+	current_dependencies.clear();
+	symbol_prefix = default_symbol_prefix;
+	load_once = default_load_once;
+	singleton = default_singleton;
+	reloadable = default_reloadable;
+}
+
 void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
 	// set entries
 	List<String> entry_key_list;
@@ -508,7 +518,7 @@ Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_
 	return result;
 }
 
-RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES GDNativeLibraryResourceLoader::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<GDNativeLibrary> lib;
 	lib.instance();
 

+ 3 - 1
modules/gdnative/gdnative.h

@@ -63,6 +63,8 @@ class GDNativeLibrary : public Resource {
 	bool reloadable;
 
 public:
+	virtual void reset_state() override;
+
 	GDNativeLibrary();
 	~GDNativeLibrary();
 
@@ -166,7 +168,7 @@ public:
 
 class GDNativeLibraryResourceLoader : 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, bool p_no_cache = false);
+	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;

+ 1 - 1
modules/gdnative/nativescript/nativescript.cpp

@@ -1940,7 +1940,7 @@ void NativeReloadNode::_notification(int p_what) {
 #endif
 }
 
-RES ResourceFormatLoaderNativeScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderNativeScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_no_cache) {
 	return ResourceFormatLoaderText::singleton->load(p_path, p_original_path, r_error);
 }
 

+ 1 - 1
modules/gdnative/nativescript/nativescript.h

@@ -402,7 +402,7 @@ public:
 
 class ResourceFormatLoaderNativeScript : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 1 - 1
modules/gdnative/pluginscript/pluginscript_loader.cpp

@@ -39,7 +39,7 @@ ResourceFormatLoaderPluginScript::ResourceFormatLoaderPluginScript(PluginScriptL
 	_language = language;
 }
 
-RES ResourceFormatLoaderPluginScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderPluginScript::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) {
 	if (r_error) {
 		*r_error = ERR_FILE_CANT_OPEN;
 	}

+ 1 - 1
modules/gdnative/pluginscript/pluginscript_loader.h

@@ -43,7 +43,7 @@ class ResourceFormatLoaderPluginScript : public ResourceFormatLoader {
 
 public:
 	ResourceFormatLoaderPluginScript(PluginScriptLanguage *language);
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 1 - 1
modules/gdnative/videodecoder/video_stream_gdnative.cpp

@@ -360,7 +360,7 @@ void VideoStreamGDNative::set_audio_track(int p_track) {
 
 /* --- NOTE ResourceFormatLoaderVideoStreamGDNative starts here. ----- */
 
-RES ResourceFormatLoaderVideoStreamGDNative::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderVideoStreamGDNative::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) {
 	FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
 	if (!f) {
 		if (r_error) {

+ 1 - 1
modules/gdnative/videodecoder/video_stream_gdnative.h

@@ -196,7 +196,7 @@ public:
 
 class ResourceFormatLoaderVideoStreamGDNative : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 1 - 1
modules/gdscript/editor/gdscript_translation_parser_plugin.cpp

@@ -44,7 +44,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
 	// Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc.
 
 	Error err;
-	RES loaded_res = ResourceLoader::load(p_path, "", false, &err);
+	RES loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
 	if (err) {
 		ERR_PRINT("Failed to load " + p_path);
 		return err;

+ 1 - 1
modules/gdscript/gdscript.cpp

@@ -2316,7 +2316,7 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na
 
 /*************** RESOURCE ***************/
 
-RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderGDScript::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) {
 	if (r_error) {
 		*r_error = ERR_FILE_CANT_OPEN;
 	}

+ 1 - 1
modules/gdscript/gdscript.h

@@ -529,7 +529,7 @@ public:
 
 class ResourceFormatLoaderGDScript : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 1 - 1
modules/mono/csharp_script.cpp

@@ -3644,7 +3644,7 @@ void CSharpScript::get_members(Set<StringName> *p_members) {
 
 /*************** RESOURCE ***************/
 
-RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderCSharpScript::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) {
 	if (r_error) {
 		*r_error = ERR_FILE_CANT_OPEN;
 	}

+ 1 - 1
modules/mono/csharp_script.h

@@ -542,7 +542,7 @@ public:
 
 class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
 public:
-	RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false) override;
+	RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
 	void get_recognized_extensions(List<String> *p_extensions) const override;
 	bool handles_type(const String &p_type) const override;
 	String get_resource_type(const String &p_path) const override;

+ 1 - 1
modules/pvr/texture_loader_pvr.cpp

@@ -46,7 +46,7 @@ enum PVRFLags {
 	PVR_VFLIP = 0x00010000
 };
 
-RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatPVR::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) {
 	if (r_error) {
 		*r_error = ERR_CANT_OPEN;
 	}

+ 1 - 1
modules/pvr/texture_loader_pvr.h

@@ -36,7 +36,7 @@
 
 class ResourceFormatPVR : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = nullptr, 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;

+ 1 - 1
modules/theora/video_stream_theora.cpp

@@ -677,7 +677,7 @@ void VideoStreamTheora::_bind_methods() {
 
 ////////////
 
-RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderTheora::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) {
 	FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
 	if (!f) {
 		if (r_error) {

+ 1 - 1
modules/theora/video_stream_theora.h

@@ -185,7 +185,7 @@ public:
 
 class ResourceFormatLoaderTheora : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 13 - 0
modules/visual_script/visual_script_expression.cpp

@@ -1506,6 +1506,19 @@ VisualScriptNodeInstance *VisualScriptExpression::instance(VisualScriptInstance
 	return instance;
 }
 
+void VisualScriptExpression::reset_state() {
+	if (nodes) {
+		memdelete(nodes);
+		nodes = nullptr;
+		root = nullptr;
+	}
+
+	error_str = String();
+	error_set = false;
+	str_ofs = 0;
+	inputs.clear();
+}
+
 VisualScriptExpression::VisualScriptExpression() {
 }
 

+ 2 - 0
modules/visual_script/visual_script_expression.h

@@ -256,6 +256,8 @@ protected:
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
 public:
+	virtual void reset_state() override;
+
 	virtual int get_output_sequence_port_count() const override;
 	virtual bool has_input_sequence_port() const override;
 

+ 4 - 0
modules/visual_script/visual_script_flow_control.cpp

@@ -677,6 +677,10 @@ void VisualScriptSwitch::_get_property_list(List<PropertyInfo> *p_list) const {
 	}
 }
 
+void VisualScriptSwitch::reset_state() {
+	case_values.clear();
+}
+
 void VisualScriptSwitch::_bind_methods() {
 }
 

+ 2 - 0
modules/visual_script/visual_script_flow_control.h

@@ -202,6 +202,8 @@ protected:
 	static void _bind_methods();
 
 public:
+	virtual void reset_state() override;
+
 	virtual int get_output_sequence_port_count() const override;
 	virtual bool has_input_sequence_port() const override;
 

+ 15 - 0
modules/visual_script/visual_script_nodes.cpp

@@ -303,6 +303,14 @@ VisualScriptNodeInstance *VisualScriptFunction::instance(VisualScriptInstance *p
 	return instance;
 }
 
+void VisualScriptFunction::reset_state() {
+	arguments.clear();
+	stack_size = 256;
+	stack_less = false;
+	sequenced = true;
+	rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+}
+
 VisualScriptFunction::VisualScriptFunction() {
 	stack_size = 256;
 	stack_less = false;
@@ -687,6 +695,13 @@ bool VisualScriptLists::is_sequenced() const {
 	return sequenced;
 }
 
+void VisualScriptLists::reset_state() {
+	inputports.clear();
+	outputports.clear();
+	sequenced = false;
+	flags = 0;
+}
+
 VisualScriptLists::VisualScriptLists() {
 	// initialize
 	sequenced = false;

+ 4 - 0
modules/visual_script/visual_script_nodes.h

@@ -99,6 +99,8 @@ public:
 
 	virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
 
+	virtual void reset_state() override;
+
 	VisualScriptFunction();
 };
 
@@ -134,6 +136,8 @@ protected:
 	static void _bind_methods();
 
 public:
+	virtual void reset_state() override;
+
 	virtual bool is_output_port_editable() const;
 	virtual bool is_output_port_name_editable() const;
 	virtual bool is_output_port_type_editable() const;

+ 1 - 1
modules/webm/video_stream_webm.cpp

@@ -429,7 +429,7 @@ void VideoStreamWebm::set_audio_track(int p_track) {
 
 ////////////
 
-RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderWebm::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) {
 	FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
 	if (!f) {
 		if (r_error) {

+ 1 - 1
modules/webm/video_stream_webm.h

@@ -126,7 +126,7 @@ public:
 
 class ResourceFormatLoaderWebm : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 7 - 0
scene/animation/animation_blend_tree.cpp

@@ -1121,6 +1121,13 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons
 	p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
 }
 
+void AnimationNodeBlendTree::reset_state() {
+	graph_offset = Vector2();
+	nodes.clear();
+	emit_changed();
+	emit_signal("tree_changed");
+}
+
 void AnimationNodeBlendTree::_tree_changed() {
 	emit_signal("tree_changed");
 }

+ 2 - 0
scene/animation/animation_blend_tree.h

@@ -351,6 +351,8 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
+
 public:
 	enum ConnectionError {
 		CONNECTION_OK,

+ 12 - 0
scene/animation/animation_node_state_machine.cpp

@@ -914,6 +914,18 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c
 	p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
 }
 
+void AnimationNodeStateMachine::reset_state() {
+	states.clear();
+	transitions.clear();
+	playback = "playback";
+	start_node = StringName();
+	end_node = StringName();
+	graph_offset = Vector2();
+
+	emit_changed();
+	emit_signal("tree_changed");
+}
+
 void AnimationNodeStateMachine::set_node_position(const StringName &p_name, const Vector2 &p_position) {
 	ERR_FAIL_COND(!states.has(p_name));
 	states[p_name].position = p_position;

+ 2 - 0
scene/animation/animation_node_state_machine.h

@@ -171,6 +171,8 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
+
 public:
 	virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
 	virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;

+ 4 - 0
scene/resources/animation.cpp

@@ -580,6 +580,10 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
 	}
 }
 
+void Animation::reset_state() {
+	clear();
+}
+
 int Animation::add_track(TrackType p_type, int p_at_pos) {
 	if (p_at_pos < 0 || p_at_pos >= tracks.size()) {
 		p_at_pos = tracks.size();

+ 2 - 0
scene/resources/animation.h

@@ -252,6 +252,8 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
+
 	static void _bind_methods();
 
 public:

+ 18 - 2
scene/resources/font.cpp

@@ -177,6 +177,14 @@ void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
 	}
 }
 
+void FontData::reset_state() {
+	if (rid != RID()) {
+		TS->free(rid);
+	}
+	base_size = 16;
+	path = String();
+}
+
 RID FontData::get_rid() const {
 	return rid;
 }
@@ -588,6 +596,14 @@ void Font::_get_property_list(List<PropertyInfo> *p_list) const {
 	p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
 }
 
+void Font::reset_state() {
+	spacing_top = 0;
+	spacing_bottom = 0;
+	cache.clear();
+	cache_wrap.clear();
+	data.clear();
+}
+
 void Font::add_data(const Ref<FontData> &p_data) {
 	ERR_FAIL_COND(p_data.is_null());
 	data.push_back(p_data);
@@ -951,7 +967,7 @@ Font::~Font() {
 
 /*************************************************************************/
 
-RES ResourceFormatLoaderFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderFont::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) {
 	if (r_error) {
 		*r_error = ERR_FILE_CANT_OPEN;
 	}
@@ -1006,7 +1022,7 @@ String ResourceFormatLoaderFont::get_resource_type(const String &p_path) const {
 
 #ifndef DISABLE_DEPRECATED
 
-RES ResourceFormatLoaderCompatFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderCompatFont::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) {
 	if (r_error) {
 		*r_error = ERR_FILE_CANT_OPEN;
 	}

+ 6 - 2
scene/resources/font.h

@@ -53,6 +53,8 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
+
 public:
 	virtual RID get_rid() const override;
 
@@ -151,6 +153,8 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
+
 	void _data_changed();
 
 public:
@@ -201,7 +205,7 @@ VARIANT_ENUM_CAST(Font::SpacingType);
 
 class ResourceFormatLoaderFont : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
 	virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual bool handles_type(const String &p_type) const;
@@ -212,7 +216,7 @@ public:
 
 class ResourceFormatLoaderCompatFont : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
 	virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual bool handles_type(const String &p_type) const;

+ 9 - 0
scene/resources/mesh.cpp

@@ -1095,6 +1095,15 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
 	return true;
 }
 
+void ArrayMesh::reset_state() {
+	clear_surfaces();
+	clear_blend_shapes();
+
+	aabb = AABB();
+	blend_shape_mode = BLEND_SHAPE_MODE_RELATIVE;
+	custom_aabb = AABB();
+}
+
 void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const {
 	if (_is_generated()) {
 		return;

+ 2 - 0
scene/resources/mesh.h

@@ -207,6 +207,8 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
+
 	static void _bind_methods();
 
 public:

+ 3 - 0
scene/resources/mesh_library.cpp

@@ -264,6 +264,9 @@ Array MeshLibrary::_get_item_shapes(int p_item) const {
 	return ret;
 }
 
+void MeshLibrary::reset_state() {
+	clear();
+}
 void MeshLibrary::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("create_item", "id"), &MeshLibrary::create_item);
 	ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name);

+ 1 - 0
scene/resources/mesh_library.h

@@ -65,6 +65,7 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
 	static void _bind_methods();
 
 public:

+ 3 - 0
scene/resources/packed_scene.cpp

@@ -1661,6 +1661,9 @@ void PackedScene::set_path(const String &p_path, bool p_take_over) {
 	Resource::set_path(p_path, p_take_over);
 }
 
+void PackedScene::reset_state() {
+	clear();
+}
 void PackedScene::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("pack", "path"), &PackedScene::pack);
 	ClassDB::bind_method(D_METHOD("instance", "edit_state"), &PackedScene::instance, DEFVAL(GEN_EDIT_STATE_DISABLED));

+ 1 - 0
scene/resources/packed_scene.h

@@ -200,6 +200,7 @@ class PackedScene : public Resource {
 protected:
 	virtual bool editor_can_reload_from_file() override { return false; } // this is handled by editor better
 	static void _bind_methods();
+	virtual void reset_state() override;
 
 public:
 	enum GenEditState {

+ 71 - 56
scene/resources/resource_format_text.cpp

@@ -114,23 +114,8 @@ Error ResourceLoaderText::_parse_sub_resource(VariantParser::Stream *p_stream, R
 	}
 
 	int index = token.value;
-
-	if (use_nocache) {
-		r_res = int_resources[index];
-	} else {
-		String path = local_path + "::" + itos(index);
-
-		if (!ignore_resource_parsing) {
-			if (!ResourceCache::has(path)) {
-				r_err_str = "Can't load cached sub-resource: " + path;
-				return ERR_PARSE_ERROR;
-			}
-
-			r_res = RES(ResourceCache::get(path));
-		} else {
-			r_res = RES();
-		}
-	}
+	ERR_FAIL_COND_V(!int_resources.has(index), ERR_INVALID_PARAMETER);
+	r_res = int_resources[index];
 
 	VariantParser::get_token(p_stream, token, line, r_err_str);
 	if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
@@ -440,7 +425,7 @@ Error ResourceLoaderText::load() {
 		er.type = type;
 
 		if (use_sub_threads) {
-			Error err = ResourceLoader::load_threaded_request(path, type, use_sub_threads, local_path);
+			Error err = ResourceLoader::load_threaded_request(path, type, use_sub_threads, ResourceFormatLoader::CACHE_MODE_REUSE, local_path);
 
 			if (err != OK) {
 				if (ResourceLoader::get_abort_on_missing_resources()) {
@@ -517,29 +502,44 @@ Error ResourceLoaderText::load() {
 		//bool exists=ResourceCache::has(path);
 
 		Ref<Resource> res;
+		bool do_assign = false;
 
-		if (use_nocache || !ResourceCache::has(path)) { //only if it doesn't exist
-
-			Object *obj = ClassDB::instance(type);
-			if (!obj) {
-				error_text += "Can't create sub resource of type: " + type;
-				_printerr();
-				error = ERR_FILE_CORRUPT;
-				return error;
+		if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(path)) {
+			//reuse existing
+			Resource *r = ResourceCache::get(path);
+			if (r && r->get_class() == type) {
+				res = Ref<Resource>(r);
+				res->reset_state();
+				do_assign = true;
 			}
+		}
 
-			Resource *r = Object::cast_to<Resource>(obj);
-			if (!r) {
-				error_text += "Can't create sub resource of type, because not a resource: " + type;
-				_printerr();
-				error = ERR_FILE_CORRUPT;
-				return error;
-			}
+		if (res.is_null()) { //not reuse
+			if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && ResourceCache::has(path)) { //only if it doesn't exist
+				//cached, do not assign
+				Resource *r = ResourceCache::get(path);
+				res = Ref<Resource>(r);
+			} else {
+				//create
+
+				Object *obj = ClassDB::instance(type);
+				if (!obj) {
+					error_text += "Can't create sub resource of type: " + type;
+					_printerr();
+					error = ERR_FILE_CORRUPT;
+					return error;
+				}
+
+				Resource *r = Object::cast_to<Resource>(obj);
+				if (!r) {
+					error_text += "Can't create sub resource of type, because not a resource: " + type;
+					_printerr();
+					error = ERR_FILE_CORRUPT;
+					return error;
+				}
 
-			res = Ref<Resource>(r);
-			int_resources[id] = res;
-			if (!use_nocache) {
-				res->set_path(path);
+				res = Ref<Resource>(r);
+				do_assign = true;
 			}
 		}
 
@@ -557,7 +557,7 @@ Error ResourceLoaderText::load() {
 			}
 
 			if (assign != String()) {
-				if (res.is_valid()) {
+				if (do_assign) {
 					res->set(assign, value);
 				}
 				//it's assignment
@@ -572,6 +572,11 @@ Error ResourceLoaderText::load() {
 			}
 		}
 
+		int_resources[id] = res; //always assign int resources
+		if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+			res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
+		}
+
 		if (progress && resources_total > 0) {
 			*progress = resource_current / float(resources_total);
 		}
@@ -589,23 +594,33 @@ Error ResourceLoaderText::load() {
 			return error;
 		}
 
-		Object *obj = ClassDB::instance(res_type);
-		if (!obj) {
-			error_text += "Can't create sub resource of type: " + res_type;
-			_printerr();
-			error = ERR_FILE_CORRUPT;
-			return error;
+		if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(local_path)) {
+			Resource *r = ResourceCache::get(local_path);
+			if (r->get_class() == res_type) {
+				r->reset_state();
+				resource = Ref<Resource>(r);
+			}
 		}
 
-		Resource *r = Object::cast_to<Resource>(obj);
-		if (!r) {
-			error_text += "Can't create sub resource of type, because not a resource: " + res_type;
-			_printerr();
-			error = ERR_FILE_CORRUPT;
-			return error;
-		}
+		if (!resource.is_valid()) {
+			Object *obj = ClassDB::instance(res_type);
+			if (!obj) {
+				error_text += "Can't create sub resource of type: " + res_type;
+				_printerr();
+				error = ERR_FILE_CORRUPT;
+				return error;
+			}
 
-		resource = Ref<Resource>(r);
+			Resource *r = Object::cast_to<Resource>(obj);
+			if (!r) {
+				error_text += "Can't create sub resource of type, because not a resource: " + res_type;
+				_printerr();
+				error = ERR_FILE_CORRUPT;
+				return error;
+			}
+
+			resource = Ref<Resource>(r);
+		}
 
 		resource_current++;
 
@@ -620,7 +635,7 @@ Error ResourceLoaderText::load() {
 					_printerr();
 				} else {
 					error = OK;
-					if (!use_nocache) {
+					if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
 						if (!ResourceCache::has(res_path)) {
 							resource->set_path(res_path);
 						}
@@ -668,7 +683,7 @@ Error ResourceLoaderText::load() {
 		error = OK;
 		//get it here
 		resource = packed_scene;
-		if (!use_nocache && !ResourceCache::has(res_path)) {
+		if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && !ResourceCache::has(res_path)) {
 			packed_scene->set_path(res_path);
 		}
 
@@ -1241,7 +1256,7 @@ String ResourceLoaderText::recognize(FileAccess *p_f) {
 
 /////////////////////
 
-RES ResourceFormatLoaderText::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderText::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) {
 	if (r_error) {
 		*r_error = ERR_CANT_OPEN;
 	}
@@ -1254,7 +1269,7 @@ RES ResourceFormatLoaderText::load(const String &p_path, const String &p_origina
 
 	ResourceLoaderText loader;
 	String path = p_original_path != "" ? p_original_path : p_path;
-	loader.use_nocache = p_no_cache;
+	loader.cache_mode = p_cache_mode;
 	loader.use_sub_threads = p_use_sub_threads;
 	loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
 	loader.progress = r_progress;

+ 2 - 2
scene/resources/resource_format_text.h

@@ -69,7 +69,7 @@ class ResourceLoaderText {
 
 	VariantParser::Tag next_tag;
 
-	bool use_nocache = false;
+	ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
 
 	bool use_sub_threads = false;
 	float *progress = nullptr;
@@ -134,7 +134,7 @@ public:
 class ResourceFormatLoaderText : public ResourceFormatLoader {
 public:
 	static ResourceFormatLoaderText *singleton;
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
 	virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual bool handles_type(const String &p_type) const;

+ 1 - 1
scene/resources/shader.cpp

@@ -161,7 +161,7 @@ Shader::~Shader() {
 
 ////////////
 
-RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderShader::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) {
 	if (r_error) {
 		*r_error = ERR_FILE_CANT_OPEN;
 	}

+ 1 - 1
scene/resources/shader.h

@@ -102,7 +102,7 @@ VARIANT_ENUM_CAST(Shader::Mode);
 
 class ResourceFormatLoaderShader : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 4 - 0
scene/resources/skin.cpp

@@ -81,6 +81,10 @@ void Skin::clear_binds() {
 	emit_changed();
 }
 
+void Skin::reset_state() {
+	clear_binds();
+}
+
 bool Skin::_set(const StringName &p_name, const Variant &p_value) {
 	String name = p_name;
 	if (name == "bind_count") {

+ 1 - 0
scene/resources/skin.h

@@ -52,6 +52,7 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
 	static void _bind_methods();
 
 public:

+ 3 - 3
scene/resources/texture.cpp

@@ -736,7 +736,7 @@ StreamTexture2D::~StreamTexture2D() {
 	}
 }
 
-RES ResourceFormatLoaderStreamTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderStreamTexture2D::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<StreamTexture2D> st;
 	st.instance();
 	Error err = st->load(p_path);
@@ -1034,7 +1034,7 @@ StreamTexture3D::~StreamTexture3D() {
 
 /////////////////////////////
 
-RES ResourceFormatLoaderStreamTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderStreamTexture3D::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<StreamTexture3D> st;
 	st.instance();
 	Error err = st->load(p_path);
@@ -2432,7 +2432,7 @@ StreamTextureLayered::~StreamTextureLayered() {
 
 /////////////////////////////////////////////////
 
-RES ResourceFormatLoaderStreamTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderStreamTextureLayered::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<StreamTextureLayered> st;
 	if (p_path.get_extension().to_lower() == "stexarray") {
 		Ref<StreamTexture2DArray> s;

+ 3 - 3
scene/resources/texture.h

@@ -211,7 +211,7 @@ public:
 
 class ResourceFormatLoaderStreamTexture2D : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;
@@ -513,7 +513,7 @@ public:
 
 class ResourceFormatLoaderStreamTextureLayered : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;
@@ -631,7 +631,7 @@ public:
 
 class ResourceFormatLoaderStreamTexture3D : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, 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;

+ 3 - 0
scene/resources/theme.cpp

@@ -930,6 +930,9 @@ void Theme::get_type_list(List<StringName> *p_list) const {
 	}
 }
 
+void Theme::reset_state() {
+	clear();
+}
 void Theme::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_icon", "name", "node_type", "texture"), &Theme::set_icon);
 	ClassDB::bind_method(D_METHOD("get_icon", "name", "node_type"), &Theme::get_icon);

+ 2 - 0
scene/resources/theme.h

@@ -80,6 +80,8 @@ protected:
 
 	static void _bind_methods();
 
+	virtual void reset_state() override;
+
 public:
 	static Ref<Theme> get_default();
 	static void set_default(const Ref<Theme> &p_default);

+ 4 - 0
scene/resources/tile_set.cpp

@@ -1076,6 +1076,10 @@ int TileSet::find_tile_by_name(const String &p_name) const {
 	return -1;
 }
 
+void TileSet::reset_state() {
+	clear();
+}
+
 void TileSet::clear() {
 	tile_map.clear();
 	notify_property_list_changed();

+ 2 - 0
scene/resources/tile_set.h

@@ -136,6 +136,8 @@ protected:
 
 	static void _bind_methods();
 
+	virtual void reset_state() override;
+
 public:
 	void create_tile(int p_id);
 

+ 6 - 0
scene/resources/visual_shader.cpp

@@ -1095,6 +1095,12 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
 	return false;
 }
 
+void VisualShader::reset_state() {
+#ifndef _MSC_VER
+#warning everything needs to be cleared here
+#endif
+	emit_changed();
+}
 void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
 	//mode
 	p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky"));

+ 2 - 0
scene/resources/visual_shader.h

@@ -126,6 +126,8 @@ protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
 
+	virtual void reset_state() override;
+
 public: // internal methods
 	void set_shader_type(Type p_type);
 	Type get_shader_type() const;