Browse Source

Fix slow editor load on large projects (v2)

Hilderin 11 months ago
parent
commit
21f7c8a25f

+ 1 - 1
core/io/resource_importer.cpp

@@ -507,7 +507,7 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con
 
 
 	for (int i = 0; i < importers.size(); i++) {
 	for (int i = 0; i < importers.size(); i++) {
 		if (importers[i]->get_importer_name() == pat.importer) {
 		if (importers[i]->get_importer_name() == pat.importer) {
-			if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid
+			if (!importers[i]->are_import_settings_valid(p_path, pat.metadata)) { //importer thinks this is not valid
 				return false;
 				return false;
 			}
 			}
 		}
 		}

+ 1 - 1
core/io/resource_importer.h

@@ -153,7 +153,7 @@ public:
 	virtual void import_threaded_end() {}
 	virtual void import_threaded_end() {}
 
 
 	virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; }
 	virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; }
-	virtual bool are_import_settings_valid(const String &p_path) const { return true; }
+	virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; }
 	virtual String get_import_settings_string() const { return String(); }
 	virtual String get_import_settings_string() const { return String(); }
 };
 };
 
 

+ 0 - 1
drivers/unix/file_access_unix.cpp

@@ -294,7 +294,6 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
 	if (!err) {
 	if (!err) {
 		return status.st_mtime;
 		return status.st_mtime;
 	} else {
 	} else {
-		WARN_PRINT("Failed to get modified time for: " + p_file);
 		return 0;
 		return 0;
 	}
 	}
 }
 }

+ 0 - 1
drivers/windows/file_access_windows.cpp

@@ -445,7 +445,6 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
 		}
 		}
 	}
 	}
 
 
-	print_verbose("Failed to get modified time for: " + p_file);
 	return 0;
 	return 0;
 }
 }
 
 

+ 202 - 96
editor/editor_file_system.cpp

@@ -282,6 +282,21 @@ void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_sca
 	}
 	}
 
 
 	for (const String &scan_file : p_scan_dir->files) {
 	for (const String &scan_file : p_scan_dir->files) {
+		// Optimization to skip the ResourceLoader::get_resource_type for files
+		// that are not scripts. Some loader get_resource_type methods read the file
+		// which can be very slow on large projects.
+		String ext = scan_file.get_extension().to_lower();
+		bool is_script = false;
+		for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+			if (ScriptServer::get_language(i)->get_extension() == ext) {
+				is_script = true;
+				break;
+			}
+		}
+		if (!is_script) {
+			continue; // Not a script.
+		}
+
 		String path = p_scan_dir->full_path.path_join(scan_file);
 		String path = p_scan_dir->full_path.path_join(scan_file);
 		String type = ResourceLoader::get_resource_type(path);
 		String type = ResourceLoader::get_resource_type(path);
 
 
@@ -371,6 +386,11 @@ void EditorFileSystem::_scan_filesystem() {
 					fc.script_class_name = split[7].get_slice("<>", 0);
 					fc.script_class_name = split[7].get_slice("<>", 0);
 					fc.script_class_extends = split[7].get_slice("<>", 1);
 					fc.script_class_extends = split[7].get_slice("<>", 1);
 					fc.script_class_icon_path = split[7].get_slice("<>", 2);
 					fc.script_class_icon_path = split[7].get_slice("<>", 2);
+					fc.import_md5 = split[7].get_slice("<>", 3);
+					String dest_paths = split[7].get_slice("<>", 4);
+					if (!dest_paths.is_empty()) {
+						fc.import_dest_paths = dest_paths.split("<*>");
+					}
 
 
 					String deps = split[8].strip_edges();
 					String deps = split[8].strip_edges();
 					if (deps.length()) {
 					if (deps.length()) {
@@ -463,12 +483,33 @@ void EditorFileSystem::_thread_func(void *_userdata) {
 	sd->_scan_filesystem();
 	sd->_scan_filesystem();
 }
 }
 
 
-bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_imported_files) {
-	if (!reimport_on_missing_imported_files && p_only_imported_files) {
-		return false;
+bool EditorFileSystem::_is_test_for_reimport_needed(const String &p_path, uint64_t p_last_modification_time, uint64_t p_modification_time, uint64_t p_last_import_modification_time, uint64_t p_import_modification_time, const Vector<String> &p_import_dest_paths) {
+	// The idea here is to trust the cache. If the last modification times in the cache correspond
+	// to the last modification times of the files on disk, it means the files have not changed since
+	// the last import, and the files in .godot/imported (p_import_dest_paths) should all be valid.
+	if (p_last_modification_time != p_modification_time) {
+		return true;
+	}
+	if (p_last_import_modification_time != p_import_modification_time) {
+		return true;
 	}
 	}
+	if (reimport_on_missing_imported_files) {
+		for (const String &path : p_import_dest_paths) {
+			if (!FileAccess::exists(path)) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
 
 
-	if (!FileAccess::exists(p_path + ".import")) {
+bool EditorFileSystem::_test_for_reimport(const String &p_path, const String &p_expected_import_md5) {
+	if (p_expected_import_md5.is_empty()) {
+		// Marked as reimportation needed.
+		return true;
+	}
+	String new_md5 = FileAccess::get_md5(p_path + ".import");
+	if (p_expected_import_md5 != new_md5) {
 		return true;
 		return true;
 	}
 	}
 
 
@@ -489,7 +530,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
 	int lines = 0;
 	int lines = 0;
 	String error_text;
 	String error_text;
 
 
-	List<String> to_check;
+	Vector<String> to_check;
 
 
 	String importer_name;
 	String importer_name;
 	String source_file = "";
 	String source_file = "";
@@ -498,6 +539,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
 	String dest_md5 = "";
 	String dest_md5 = "";
 	int version = 0;
 	int version = 0;
 	bool found_uid = false;
 	bool found_uid = false;
+	Variant meta;
 
 
 	while (true) {
 	while (true) {
 		assign = Variant();
 		assign = Variant();
@@ -522,8 +564,8 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
 				to_check.push_back(value);
 				to_check.push_back(value);
 			} else if (assign == "files") {
 			} else if (assign == "files") {
 				Array fa = value;
 				Array fa = value;
-				for (int i = 0; i < fa.size(); i++) {
-					to_check.push_back(fa[i]);
+				for (const Variant &check_path : fa) {
+					to_check.push_back(check_path);
 				}
 				}
 			} else if (assign == "importer_version") {
 			} else if (assign == "importer_version") {
 				version = value;
 				version = value;
@@ -531,12 +573,12 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
 				importer_name = value;
 				importer_name = value;
 			} else if (assign == "uid") {
 			} else if (assign == "uid") {
 				found_uid = true;
 				found_uid = true;
-			} else if (!p_only_imported_files) {
-				if (assign == "source_file") {
-					source_file = value;
-				} else if (assign == "dest_files") {
-					dest_files = value;
-				}
+			} else if (assign == "source_file") {
+				source_file = value;
+			} else if (assign == "dest_files") {
+				dest_files = value;
+			} else if (assign == "metadata") {
+				meta = value;
 			}
 			}
 
 
 		} else if (next_tag.name != "remap" && next_tag.name != "deps") {
 		} else if (next_tag.name != "remap" && next_tag.name != "deps") {
@@ -544,33 +586,40 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
 		}
 		}
 	}
 	}
 
 
-	if (!ResourceFormatImporter::get_singleton()->are_import_settings_valid(p_path)) {
-		// Reimport settings are out of sync with project settings, reimport.
-		return true;
-	}
-
 	if (importer_name == "keep" || importer_name == "skip") {
 	if (importer_name == "keep" || importer_name == "skip") {
-		return false; //keep mode, do not reimport
+		return false; // Keep mode, do not reimport.
 	}
 	}
 
 
 	if (!found_uid) {
 	if (!found_uid) {
-		return true; //UID not found, old format, reimport.
+		return true; // UID not found, old format, reimport.
+	}
+
+	// Imported files are gone, reimport.
+	for (const String &E : to_check) {
+		if (!FileAccess::exists(E)) {
+			return true;
+		}
 	}
 	}
 
 
 	Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
 	Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
 
 
 	if (importer.is_null()) {
 	if (importer.is_null()) {
-		return true; // the importer has possibly changed, try to reimport.
+		return true; // The importer has possibly changed, try to reimport.
 	}
 	}
 
 
 	if (importer->get_format_version() > version) {
 	if (importer->get_format_version() > version) {
-		return true; // version changed, reimport
+		return true; // Version changed, reimport.
+	}
+
+	if (!importer->are_import_settings_valid(p_path, meta)) {
+		// Reimport settings are out of sync with project settings, reimport.
+		return true;
 	}
 	}
 
 
-	// Read the md5's from a separate file (so the import parameters aren't dependent on the file version
+	// Read the md5's from a separate file (so the import parameters aren't dependent on the file version).
 	String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_path);
 	String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_path);
 	Ref<FileAccess> md5s = FileAccess::open(base_path + ".md5", FileAccess::READ, &err);
 	Ref<FileAccess> md5s = FileAccess::open(base_path + ".md5", FileAccess::READ, &err);
-	if (md5s.is_null()) { // No md5's stored for this resource
+	if (md5s.is_null()) { // No md5's stored for this resource.
 		return true;
 		return true;
 	}
 	}
 
 
@@ -588,50 +637,101 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
 			break;
 			break;
 		} else if (err != OK) {
 		} else if (err != OK) {
 			ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import.md5:" + itos(lines) + "' error '" + error_text + "'.");
 			ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import.md5:" + itos(lines) + "' error '" + error_text + "'.");
-			return false; // parse error
+			return false; // Parse error.
 		}
 		}
 		if (!assign.is_empty()) {
 		if (!assign.is_empty()) {
-			if (!p_only_imported_files) {
-				if (assign == "source_md5") {
-					source_md5 = value;
-				} else if (assign == "dest_md5") {
-					dest_md5 = value;
-				}
+			if (assign == "source_md5") {
+				source_md5 = value;
+			} else if (assign == "dest_md5") {
+				dest_md5 = value;
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	//imported files are gone, reimport
-	for (const String &E : to_check) {
-		if (!FileAccess::exists(E)) {
+	// Check source md5 matching.
+	if (!source_file.is_empty() && source_file != p_path) {
+		return true; // File was moved, reimport.
+	}
+
+	if (source_md5.is_empty()) {
+		return true; // Lacks md5, so just reimport.
+	}
+
+	String md5 = FileAccess::get_md5(p_path);
+	if (md5 != source_md5) {
+		return true;
+	}
+
+	if (!dest_files.is_empty() && !dest_md5.is_empty()) {
+		md5 = FileAccess::get_multiple_md5(dest_files);
+		if (md5 != dest_md5) {
 			return true;
 			return true;
 		}
 		}
 	}
 	}
 
 
-	//check source md5 matching
-	if (!p_only_imported_files) {
-		if (!source_file.is_empty() && source_file != p_path) {
-			return true; //file was moved, reimport
-		}
+	return false; // Nothing changed.
+}
 
 
-		if (source_md5.is_empty()) {
-			return true; //lacks md5, so just reimport
-		}
+Vector<String> EditorFileSystem::_get_import_dest_paths(const String &p_path) {
+	Error err;
+	Ref<FileAccess> f = FileAccess::open(p_path + ".import", FileAccess::READ, &err);
 
 
-		String md5 = FileAccess::get_md5(p_path);
-		if (md5 != source_md5) {
-			return true;
+	if (f.is_null()) { // No import file, reimport.
+		return Vector<String>();
+	}
+
+	VariantParser::StreamFile stream;
+	stream.f = f;
+
+	String assign;
+	Variant value;
+	VariantParser::Tag next_tag;
+
+	int lines = 0;
+	String error_text;
+
+	Vector<String> dest_paths;
+	String importer_name;
+
+	while (true) {
+		assign = Variant();
+		next_tag.fields.clear();
+		next_tag.name = String();
+
+		err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true);
+		if (err == ERR_FILE_EOF) {
+			break;
+		} else if (err != OK) {
+			ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import:" + itos(lines) + "' error '" + error_text + "'.");
+			// Parse error, skip and let user attempt manual reimport to avoid reimport loop.
+			return Vector<String>();
 		}
 		}
 
 
-		if (dest_files.size() && !dest_md5.is_empty()) {
-			md5 = FileAccess::get_multiple_md5(dest_files);
-			if (md5 != dest_md5) {
-				return true;
+		if (!assign.is_empty()) {
+			if (assign == "valid" && value.operator bool() == false) {
+				// Invalid import (failed previous import), skip and let user attempt manual reimport to avoid reimport loop.
+				return Vector<String>();
 			}
 			}
+			if (assign.begins_with("path")) {
+				dest_paths.push_back(value);
+			} else if (assign == "files") {
+				Array fa = value;
+				for (const Variant &dest_path : fa) {
+					dest_paths.push_back(dest_path);
+				}
+			} else if (assign == "importer") {
+				importer_name = value;
+			}
+		} else if (next_tag.name != "remap" && next_tag.name != "deps") {
+			break;
 		}
 		}
 	}
 	}
 
 
-	return false; //nothing changed
+	if (importer_name == "keep" || importer_name == "skip") {
+		return Vector<String>();
+	}
+
+	return dest_paths;
 }
 }
 
 
 bool EditorFileSystem::_scan_import_support(const Vector<String> &reimports) {
 bool EditorFileSystem::_scan_import_support(const Vector<String> &reimports) {
@@ -774,20 +874,9 @@ bool EditorFileSystem::_update_scan_actions() {
 				ERR_CONTINUE(idx == -1);
 				ERR_CONTINUE(idx == -1);
 				String full_path = ia.dir->get_file_path(idx);
 				String full_path = ia.dir->get_file_path(idx);
 
 
-				bool need_reimport = _test_for_reimport(full_path, false);
-				// Workaround GH-94416 for the Android editor for now.
-				// `import_mt` seems to always be 0 and force a reimport on any fs scan.
-#ifndef ANDROID_ENABLED
-				if (!need_reimport && FileAccess::exists(full_path + ".import")) {
-					uint64_t import_mt = ia.dir->get_file_import_modified_time(idx);
-					if (import_mt != FileAccess::get_modified_time(full_path + ".import")) {
-						need_reimport = true;
-					}
-				}
-#endif
-
+				bool need_reimport = _test_for_reimport(full_path, ia.dir->files[idx]->import_md5);
 				if (need_reimport) {
 				if (need_reimport) {
-					//must reimport
+					// Must reimport.
 					reimports.push_back(full_path);
 					reimports.push_back(full_path);
 					Vector<String> dependencies = _get_dependencies(full_path);
 					Vector<String> dependencies = _get_dependencies(full_path);
 					for (const String &dep : dependencies) {
 					for (const String &dep : dependencies) {
@@ -797,10 +886,14 @@ bool EditorFileSystem::_update_scan_actions() {
 						}
 						}
 					}
 					}
 				} else {
 				} else {
-					//must not reimport, all was good
-					//update modified times, to avoid reimport
+					// Must not reimport, all was good.
+					// Update modified times, md5 and destination paths, to avoid reimport.
 					ia.dir->files[idx]->modified_time = FileAccess::get_modified_time(full_path);
 					ia.dir->files[idx]->modified_time = FileAccess::get_modified_time(full_path);
 					ia.dir->files[idx]->import_modified_time = FileAccess::get_modified_time(full_path + ".import");
 					ia.dir->files[idx]->import_modified_time = FileAccess::get_modified_time(full_path + ".import");
+					if (ia.dir->files[idx]->import_md5.is_empty()) {
+						ia.dir->files[idx]->import_md5 = FileAccess::get_md5(full_path + ".import");
+					}
+					ia.dir->files[idx]->import_dest_paths = _get_import_dest_paths(full_path);
 				}
 				}
 
 
 				fs_changed = true;
 				fs_changed = true;
@@ -1029,26 +1122,36 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
 
 
 		if (import_extensions.has(ext)) {
 		if (import_extensions.has(ext)) {
 			//is imported
 			//is imported
-			uint64_t import_mt = 0;
-			if (FileAccess::exists(path + ".import")) {
-				import_mt = FileAccess::get_modified_time(path + ".import");
-			}
+			uint64_t import_mt = FileAccess::get_modified_time(path + ".import");
 
 
-			if (fc && fc->modification_time == mt && fc->import_modification_time == import_mt && !_test_for_reimport(path, true)) {
+			if (fc) {
 				fi->type = fc->type;
 				fi->type = fc->type;
 				fi->resource_script_class = fc->resource_script_class;
 				fi->resource_script_class = fc->resource_script_class;
 				fi->uid = fc->uid;
 				fi->uid = fc->uid;
 				fi->deps = fc->deps;
 				fi->deps = fc->deps;
-				fi->modified_time = fc->modification_time;
-				fi->import_modified_time = fc->import_modification_time;
-
+				fi->modified_time = mt;
+				fi->import_modified_time = import_mt;
+				fi->import_md5 = fc->import_md5;
+				fi->import_dest_paths = fc->import_dest_paths;
 				fi->import_valid = fc->import_valid;
 				fi->import_valid = fc->import_valid;
 				fi->script_class_name = fc->script_class_name;
 				fi->script_class_name = fc->script_class_name;
 				fi->import_group_file = fc->import_group_file;
 				fi->import_group_file = fc->import_group_file;
 				fi->script_class_extends = fc->script_class_extends;
 				fi->script_class_extends = fc->script_class_extends;
 				fi->script_class_icon_path = fc->script_class_icon_path;
 				fi->script_class_icon_path = fc->script_class_icon_path;
 
 
-				if (revalidate_import_files && !ResourceFormatImporter::get_singleton()->are_import_settings_valid(path)) {
+				// Ensures backward compatibility when the project is loaded for the first time with the added import_md5
+				// and import_dest_paths properties in the file cache.
+				if (fc->import_md5.is_empty()) {
+					fi->import_md5 = FileAccess::get_md5(path + ".import");
+					fi->import_dest_paths = _get_import_dest_paths(path);
+				}
+
+				// The method _is_test_for_reimport_needed checks if the files were modified and ensures that
+				// all the destination files still exist without reading the .import file.
+				// If something is different, we will queue a test for reimportation that will check
+				// the md5 of all files and import settings and, if necessary, execute a reimportation.
+				if (_is_test_for_reimport_needed(path, fc->modification_time, mt, fc->import_modification_time, import_mt, fi->import_dest_paths) ||
+						(revalidate_import_files && !ResourceFormatImporter::get_singleton()->are_import_settings_valid(path))) {
 					ItemAction ia;
 					ItemAction ia;
 					ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT;
 					ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT;
 					ia.dir = p_dir;
 					ia.dir = p_dir;
@@ -1075,6 +1178,8 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
 				fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
 				fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
 				fi->modified_time = 0;
 				fi->modified_time = 0;
 				fi->import_modified_time = 0;
 				fi->import_modified_time = 0;
+				fi->import_md5 = "";
+				fi->import_dest_paths = Vector<String>();
 				fi->import_valid = (fi->type == "TextFile" || fi->type == "OtherFile") ? true : ResourceLoader::is_import_valid(path);
 				fi->import_valid = (fi->type == "TextFile" || fi->type == "OtherFile") ? true : ResourceLoader::is_import_valid(path);
 
 
 				ItemAction ia;
 				ItemAction ia;
@@ -1089,9 +1194,11 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
 				fi->type = fc->type;
 				fi->type = fc->type;
 				fi->resource_script_class = fc->resource_script_class;
 				fi->resource_script_class = fc->resource_script_class;
 				fi->uid = fc->uid;
 				fi->uid = fc->uid;
-				fi->modified_time = fc->modification_time;
+				fi->modified_time = mt;
 				fi->deps = fc->deps;
 				fi->deps = fc->deps;
 				fi->import_modified_time = 0;
 				fi->import_modified_time = 0;
+				fi->import_md5 = "";
+				fi->import_dest_paths = Vector<String>();
 				fi->import_valid = true;
 				fi->import_valid = true;
 				fi->script_class_name = fc->script_class_name;
 				fi->script_class_name = fc->script_class_name;
 				fi->script_class_extends = fc->script_class_extends;
 				fi->script_class_extends = fc->script_class_extends;
@@ -1126,6 +1233,8 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
 				fi->deps = _get_dependencies(path);
 				fi->deps = _get_dependencies(path);
 				fi->modified_time = mt;
 				fi->modified_time = mt;
 				fi->import_modified_time = 0;
 				fi->import_modified_time = 0;
+				fi->import_md5 = "";
+				fi->import_dest_paths = Vector<String>();
 				fi->import_valid = true;
 				fi->import_valid = true;
 
 
 				// Files in dep_update_list are forced for rescan to update dependencies. They don't need other updates.
 				// Files in dep_update_list are forced for rescan to update dependencies. They don't need other updates.
@@ -1269,6 +1378,8 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
 					String path = cd.path_join(fi->file);
 					String path = cd.path_join(fi->file);
 					fi->modified_time = FileAccess::get_modified_time(path);
 					fi->modified_time = FileAccess::get_modified_time(path);
 					fi->import_modified_time = 0;
 					fi->import_modified_time = 0;
+					fi->import_md5 = "";
+					fi->import_dest_paths = Vector<String>();
 					fi->type = ResourceLoader::get_resource_type(path);
 					fi->type = ResourceLoader::get_resource_type(path);
 					fi->resource_script_class = ResourceLoader::get_resource_script_class(path);
 					fi->resource_script_class = ResourceLoader::get_resource_script_class(path);
 					if (fi->type == "" && textfile_extensions.has(ext)) {
 					if (fi->type == "" && textfile_extensions.has(ext)) {
@@ -1323,26 +1434,13 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
 		String path = cd.path_join(p_dir->files[i]->file);
 		String path = cd.path_join(p_dir->files[i]->file);
 
 
 		if (import_extensions.has(p_dir->files[i]->file.get_extension().to_lower())) {
 		if (import_extensions.has(p_dir->files[i]->file.get_extension().to_lower())) {
-			//check here if file must be imported or not
-
+			// Check here if file must be imported or not.
+			// Same logic as in _process_file_system, the last modifications dates
+			// needs to be trusted to prevent reading all the .import files and the md5
+			// each time the user switch back to Godot.
 			uint64_t mt = FileAccess::get_modified_time(path);
 			uint64_t mt = FileAccess::get_modified_time(path);
-
-			bool reimport = false;
-
-			if (mt != p_dir->files[i]->modified_time) {
-				reimport = true; //it was modified, must be reimported.
-			} else if (!FileAccess::exists(path + ".import")) {
-				reimport = true; //no .import file, obviously reimport
-			} else {
-				uint64_t import_mt = FileAccess::get_modified_time(path + ".import");
-				if (import_mt != p_dir->files[i]->import_modified_time) {
-					reimport = true;
-				} else if (_test_for_reimport(path, true)) {
-					reimport = true;
-				}
-			}
-
-			if (reimport) {
+			uint64_t import_mt = FileAccess::get_modified_time(path + ".import");
+			if (_is_test_for_reimport_needed(path, p_dir->files[i]->modified_time, mt, p_dir->files[i]->import_modified_time, import_mt, p_dir->files[i]->import_dest_paths)) {
 				ItemAction ia;
 				ItemAction ia;
 				ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT;
 				ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT;
 				ia.dir = p_dir;
 				ia.dir = p_dir;
@@ -1625,7 +1723,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
 		cache_string.append(itos(file_info->import_modified_time));
 		cache_string.append(itos(file_info->import_modified_time));
 		cache_string.append(itos(file_info->import_valid));
 		cache_string.append(itos(file_info->import_valid));
 		cache_string.append(file_info->import_group_file);
 		cache_string.append(file_info->import_group_file);
-		cache_string.append(String("<>").join({ file_info->script_class_name, file_info->script_class_extends, file_info->script_class_icon_path }));
+		cache_string.append(String("<>").join({ file_info->script_class_name, file_info->script_class_extends, file_info->script_class_icon_path, file_info->import_md5, String("<*>").join(file_info->import_dest_paths) }));
 		cache_string.append(String("<>").join(file_info->deps));
 		cache_string.append(String("<>").join(file_info->deps));
 
 
 		p_file->store_line(String("::").join(cache_string));
 		p_file->store_line(String("::").join(cache_string));
@@ -2166,6 +2264,8 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
 				fi->file = file_name;
 				fi->file = file_name;
 				fi->import_modified_time = 0;
 				fi->import_modified_time = 0;
 				fi->import_valid = (type == "TextFile" || type == "OtherFile") ? true : ResourceLoader::is_import_valid(file);
 				fi->import_valid = (type == "TextFile" || type == "OtherFile") ? true : ResourceLoader::is_import_valid(file);
+				fi->import_md5 = "";
+				fi->import_dest_paths = Vector<String>();
 
 
 				if (idx == fs->files.size()) {
 				if (idx == fs->files.size()) {
 					fs->files.push_back(fi);
 					fs->files.push_back(fi);
@@ -2441,6 +2541,8 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
 		//update modified times, to avoid reimport
 		//update modified times, to avoid reimport
 		fs->files[cpos]->modified_time = FileAccess::get_modified_time(file);
 		fs->files[cpos]->modified_time = FileAccess::get_modified_time(file);
 		fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(file + ".import");
 		fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(file + ".import");
+		fs->files[cpos]->import_md5 = FileAccess::get_md5(file + ".import");
+		fs->files[cpos]->import_dest_paths = dest_paths;
 		fs->files[cpos]->deps = _get_dependencies(file);
 		fs->files[cpos]->deps = _get_dependencies(file);
 		fs->files[cpos]->uid = uid;
 		fs->files[cpos]->uid = uid;
 		fs->files[cpos]->type = importer->get_resource_type();
 		fs->files[cpos]->type = importer->get_resource_type();
@@ -2542,11 +2644,13 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
 		if (p_update_file_system) {
 		if (p_update_file_system) {
 			fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
 			fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
 			fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
 			fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
+			fs->files[cpos]->import_md5 = FileAccess::get_md5(p_file + ".import");
+			fs->files[cpos]->import_dest_paths = Vector<String>();
 			fs->files[cpos]->deps.clear();
 			fs->files[cpos]->deps.clear();
 			fs->files[cpos]->type = "";
 			fs->files[cpos]->type = "";
 			fs->files[cpos]->import_valid = false;
 			fs->files[cpos]->import_valid = false;
+			EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
 		}
 		}
-		EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
 		return OK;
 		return OK;
 	}
 	}
 	Ref<ResourceImporter> importer;
 	Ref<ResourceImporter> importer;
@@ -2712,6 +2816,8 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
 		// Update modified times, to avoid reimport.
 		// Update modified times, to avoid reimport.
 		fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
 		fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
 		fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
 		fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
+		fs->files[cpos]->import_md5 = FileAccess::get_md5(p_file + ".import");
+		fs->files[cpos]->import_dest_paths = dest_paths;
 		fs->files[cpos]->deps = _get_dependencies(p_file);
 		fs->files[cpos]->deps = _get_dependencies(p_file);
 		fs->files[cpos]->type = importer->get_resource_type();
 		fs->files[cpos]->type = importer->get_resource_type();
 		fs->files[cpos]->uid = uid;
 		fs->files[cpos]->uid = uid;

+ 7 - 1
editor/editor_file_system.h

@@ -60,6 +60,8 @@ class EditorFileSystemDirectory : public Object {
 		ResourceUID::ID uid = ResourceUID::INVALID_ID;
 		ResourceUID::ID uid = ResourceUID::INVALID_ID;
 		uint64_t modified_time = 0;
 		uint64_t modified_time = 0;
 		uint64_t import_modified_time = 0;
 		uint64_t import_modified_time = 0;
+		String import_md5;
+		Vector<String> import_dest_paths;
 		bool import_valid = false;
 		bool import_valid = false;
 		String import_group_file;
 		String import_group_file;
 		Vector<String> deps;
 		Vector<String> deps;
@@ -206,6 +208,8 @@ class EditorFileSystem : public Node {
 		ResourceUID::ID uid = ResourceUID::INVALID_ID;
 		ResourceUID::ID uid = ResourceUID::INVALID_ID;
 		uint64_t modification_time = 0;
 		uint64_t modification_time = 0;
 		uint64_t import_modification_time = 0;
 		uint64_t import_modification_time = 0;
+		String import_md5;
+		Vector<String> import_dest_paths;
 		Vector<String> deps;
 		Vector<String> deps;
 		bool import_valid = false;
 		bool import_valid = false;
 		String import_group_file;
 		String import_group_file;
@@ -264,7 +268,9 @@ class EditorFileSystem : public Node {
 	Error _reimport_file(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr, bool p_update_file_system = true);
 	Error _reimport_file(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr, bool p_update_file_system = true);
 	Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
 	Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
 
 
-	bool _test_for_reimport(const String &p_path, bool p_only_imported_files);
+	bool _test_for_reimport(const String &p_path, const String &p_expected_import_md5);
+	bool _is_test_for_reimport_needed(const String &p_path, uint64_t p_last_modification_time, uint64_t p_modification_time, uint64_t p_last_import_modification_time, uint64_t p_import_modification_time, const Vector<String> &p_import_dest_paths);
+	Vector<String> _get_import_dest_paths(const String &p_path);
 
 
 	bool reimport_on_missing_imported_files;
 	bool reimport_on_missing_imported_files;
 
 

+ 5 - 7
editor/import/resource_importer_layered_texture.cpp

@@ -431,22 +431,20 @@ String ResourceImporterLayeredTexture::get_import_settings_string() const {
 	return s;
 	return s;
 }
 }
 
 
-bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path) const {
+bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const {
 	//will become invalid if formats are missing to import
 	//will become invalid if formats are missing to import
-	Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
-
-	if (!meta.has("vram_texture")) {
+	if (!p_meta.has("vram_texture")) {
 		return false;
 		return false;
 	}
 	}
 
 
-	bool vram = meta["vram_texture"];
+	bool vram = p_meta["vram_texture"];
 	if (!vram) {
 	if (!vram) {
 		return true; //do not care about non vram
 		return true; //do not care about non vram
 	}
 	}
 
 
 	Vector<String> formats_imported;
 	Vector<String> formats_imported;
-	if (meta.has("imported_formats")) {
-		formats_imported = meta["imported_formats"];
+	if (p_meta.has("imported_formats")) {
+		formats_imported = p_meta["imported_formats"];
 	}
 	}
 
 
 	int index = 0;
 	int index = 0;

+ 1 - 1
editor/import/resource_importer_layered_texture.h

@@ -114,7 +114,7 @@ public:
 
 
 	virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
 	virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
 
 
-	virtual bool are_import_settings_valid(const String &p_path) const override;
+	virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override;
 	virtual String get_import_settings_string() const override;
 	virtual String get_import_settings_string() const override;
 
 
 	void set_mode(Mode p_mode) { mode = p_mode; }
 	void set_mode(Mode p_mode) { mode = p_mode; }

+ 6 - 8
editor/import/resource_importer_texture.cpp

@@ -740,10 +740,8 @@ String ResourceImporterTexture::get_import_settings_string() const {
 	return s;
 	return s;
 }
 }
 
 
-bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) const {
-	Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
-
-	if (meta.has("has_editor_variant")) {
+bool ResourceImporterTexture::are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const {
+	if (p_meta.has("has_editor_variant")) {
 		String imported_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path);
 		String imported_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path);
 		if (!FileAccess::exists(imported_path)) {
 		if (!FileAccess::exists(imported_path)) {
 			return false;
 			return false;
@@ -760,19 +758,19 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
 		}
 		}
 	}
 	}
 
 
-	if (!meta.has("vram_texture")) {
+	if (!p_meta.has("vram_texture")) {
 		return false;
 		return false;
 	}
 	}
 
 
-	bool vram = meta["vram_texture"];
+	bool vram = p_meta["vram_texture"];
 	if (!vram) {
 	if (!vram) {
 		return true; // Do not care about non-VRAM.
 		return true; // Do not care about non-VRAM.
 	}
 	}
 
 
 	// Will become invalid if formats are missing to import.
 	// Will become invalid if formats are missing to import.
 	Vector<String> formats_imported;
 	Vector<String> formats_imported;
-	if (meta.has("imported_formats")) {
-		formats_imported = meta["imported_formats"];
+	if (p_meta.has("imported_formats")) {
+		formats_imported = p_meta["imported_formats"];
 	}
 	}
 
 
 	int index = 0;
 	int index = 0;

+ 1 - 1
editor/import/resource_importer_texture.h

@@ -104,7 +104,7 @@ public:
 
 
 	void update_imports();
 	void update_imports();
 
 
-	virtual bool are_import_settings_valid(const String &p_path) const override;
+	virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override;
 	virtual String get_import_settings_string() const override;
 	virtual String get_import_settings_string() const override;
 
 
 	ResourceImporterTexture(bool p_singleton = false);
 	ResourceImporterTexture(bool p_singleton = false);