Overhaul the SurfaceUpgradeTool

This defers the update to a fresh restart of the editor (to ensure we aren't mid way through loading scenes anymore.

It also ensures that the popup can't be used by multiple threads at once

Co-authored-by: Yuri Sizov <yuris@humnom.net>
This commit is contained in:
clayjohn 2023-10-30 15:34:18 +01:00
parent 6afd320984
commit be386e1876
5 changed files with 157 additions and 37 deletions

View file

@ -623,6 +623,20 @@ void EditorNode::_notification(int p_what) {
ResourceImporterTexture::get_singleton()->update_imports();
bottom_panel_updating = false;
if (requested_first_scan) {
requested_first_scan = false;
OS::get_singleton()->benchmark_begin_measure("editor_scan_and_import");
if (run_surface_upgrade_tool) {
run_surface_upgrade_tool = false;
SurfaceUpgradeTool::get_singleton()->connect("upgrade_finished", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan), CONNECT_ONE_SHOT);
SurfaceUpgradeTool::get_singleton()->finish_upgrade();
} else {
EditorFileSystem::get_singleton()->scan();
}
}
} break;
case NOTIFICATION_ENTER_TREE: {
@ -1042,6 +1056,10 @@ void EditorNode::_sources_changed(bool p_exist) {
OS::get_singleton()->benchmark_dump();
}
if (SurfaceUpgradeTool::get_singleton()->is_show_requested()) {
SurfaceUpgradeTool::get_singleton()->show_popup();
}
}
}
@ -4615,8 +4633,10 @@ void EditorNode::_editor_file_dialog_unregister(EditorFileDialog *p_dialog) {
Vector<EditorNodeInitCallback> EditorNode::_init_callbacks;
void EditorNode::_begin_first_scan() {
OS::get_singleton()->benchmark_begin_measure("editor_scan_and_import");
EditorFileSystem::get_singleton()->scan();
if (!waiting_for_first_scan) {
return;
}
requested_first_scan = true;
}
Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only) {
@ -6809,6 +6829,13 @@ EditorNode::EditorNode() {
FileAccess::set_backup_save(EDITOR_GET("filesystem/on_save/safe_save_on_backup_then_rename"));
// Warm up the surface upgrade tool as early as possible.
surface_upgrade_tool = memnew(SurfaceUpgradeTool);
run_surface_upgrade_tool = EditorSettings::get_singleton()->get_project_metadata("surface_upgrade_tool", "run_on_restart", false);
if (run_surface_upgrade_tool) {
SurfaceUpgradeTool::get_singleton()->begin_upgrade();
}
{
int display_scale = EDITOR_GET("interface/editor/display_scale");
@ -8051,8 +8078,6 @@ EditorNode::EditorNode() {
String exec = OS::get_singleton()->get_executable_path();
// Save editor executable path for third-party tools.
EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "executable_path", exec);
surface_upgrade_tool = memnew(SurfaceUpgradeTool);
}
EditorNode::~EditorNode() {

View file

@ -157,6 +157,7 @@ public:
private:
friend class EditorSceneTabs;
friend class SurfaceUpgradeTool;
enum MenuOptions {
FILE_NEW_SCENE,
@ -459,6 +460,8 @@ private:
bool opening_prev = false;
bool restoring_scenes = false;
bool unsaved_cache = true;
bool requested_first_scan = false;
bool waiting_for_first_scan = true;
int current_menu_option = 0;
@ -493,6 +496,7 @@ private:
HashMap<String, Ref<Texture2D>> icon_type_cache;
SurfaceUpgradeTool *surface_upgrade_tool = nullptr;
bool run_surface_upgrade_tool = false;
static EditorBuildCallback build_callbacks[MAX_BUILD_CALLBACKS];
static EditorPluginInitializeCallback plugin_init_callbacks[MAX_INIT_CALLBACKS];

View file

@ -32,11 +32,15 @@
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "scene/scene_string_names.h"
#include "servers/rendering_server.h"
void SurfaceUpgradeTool::_add_files(EditorFileSystemDirectory *p_dir, HashSet<String> &r_paths, PackedStringArray &r_files) {
SurfaceUpgradeTool *SurfaceUpgradeTool::singleton = nullptr;
void SurfaceUpgradeTool::_add_files(EditorFileSystemDirectory *p_dir, Vector<String> &r_reimport_paths, Vector<String> &r_resave_paths) {
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
_add_files(p_dir->get_subdir(i), r_paths, r_files);
_add_files(p_dir->get_subdir(i), r_reimport_paths, r_resave_paths);
}
for (int i = 0; i < p_dir->get_file_count(); i++) {
@ -44,47 +48,116 @@ void SurfaceUpgradeTool::_add_files(EditorFileSystemDirectory *p_dir, HashSet<St
p_dir->get_file_type(i) == "ArrayMesh" ||
p_dir->get_file_type(i) == "PackedScene") {
if (FileAccess::exists(p_dir->get_file_path(i) + ".import")) {
r_files.push_back(p_dir->get_file_path(i));
r_reimport_paths.append(p_dir->get_file_path(i) + ".import");
} else {
r_paths.insert(p_dir->get_file_path(i));
r_resave_paths.append(p_dir->get_file_path(i));
}
}
}
}
void SurfaceUpgradeTool::upgrade_all_meshes() {
void SurfaceUpgradeTool::_try_show_popup() {
if (singleton->show_requested || singleton->popped_up) {
return;
}
singleton->show_requested = true;
RS::get_singleton()->set_warn_on_surface_upgrade(false);
if (EditorFileSystem::get_singleton()->is_importing()) {
EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(singleton, &SurfaceUpgradeTool::_show_popup), CONNECT_ONE_SHOT);
} else if (EditorNode::get_singleton()->is_inside_tree()) {
singleton->_show_popup();
}
// EditorNode may not be ready yet. It will call this tool when it is.
}
void SurfaceUpgradeTool::_show_popup() {
MutexLock lock(mutex);
if (!show_requested || popped_up) {
return;
}
show_requested = false;
popped_up = true;
bool accepted = EditorNode::immediate_confirmation_dialog(TTR("This project uses meshes with an outdated mesh format from previous Godot versions. The engine needs to update the format in order to use those meshes.\n\nPress 'Restart & Upgrade' to run the surface upgrade tool which will update and re-save all meshes and scenes. This update will restart the editor and may take several minutes. Upgrading will make the meshes incompatible with previous versions of Godot.\n\nPress 'Upgrade Only' to continue opening the scene as normal. The engine will update each mesh in memory, but the update will not be saved. Choosing this option will lead to slower load times every time this project is loaded."), TTR("Restart & Upgrade"), TTR("Upgrade Only"), 500);
if (accepted) {
EditorSettings::get_singleton()->set_project_metadata("surface_upgrade_tool", "run_on_restart", true);
Vector<String> reimport_paths;
Vector<String> resave_paths;
_add_files(EditorFileSystem::get_singleton()->get_filesystem(), reimport_paths, resave_paths);
EditorSettings::get_singleton()->set_project_metadata("surface_upgrade_tool", "reimport_paths", reimport_paths);
EditorSettings::get_singleton()->set_project_metadata("surface_upgrade_tool", "resave_paths", resave_paths);
EditorNode::get_singleton()->restart_editor();
} else {
RS::get_singleton()->set_warn_on_surface_upgrade(true);
}
}
// Ensure that the warnings and popups are skipped.
void SurfaceUpgradeTool::begin_upgrade() {
EditorSettings::get_singleton()->set_project_metadata("surface_upgrade_tool", "run_on_restart", false);
RS::get_singleton()->set_surface_upgrade_callback(nullptr);
RS::get_singleton()->set_warn_on_surface_upgrade(false);
popped_up = true;
}
void SurfaceUpgradeTool::finish_upgrade() {
EditorNode::get_singleton()->trigger_menu_option(EditorNode::FILE_CLOSE_ALL, true);
// Update all meshes here.
HashSet<String> paths;
PackedStringArray files_to_import;
_add_files(EditorFileSystem::get_singleton()->get_filesystem(), paths, files_to_import);
Vector<String> resave_paths = EditorSettings::get_singleton()->get_project_metadata("surface_upgrade_tool", "resave_paths", Vector<String>());
EditorProgress ep("surface_upgrade_resave", TTR("Upgrading All Meshes in Project"), resave_paths.size());
EditorProgress ep("Re-saving all scenes and meshes", TTR("Upgrading All Meshes in Project"), paths.size());
ep.step(TTR("Re-importing meshes"), 0);
EditorFileSystem::get_singleton()->reimport_files(files_to_import);
uint32_t step = 1;
for (const String &file : paths) {
Ref<Resource> res = ResourceLoader::load(file);
ep.step(TTR("Attempting to re-save ") + file, step++);
for (const String &file_path : resave_paths) {
Ref<Resource> res = ResourceLoader::load(file_path);
ep.step(TTR("Attempting to re-save ") + file_path);
if (res.is_valid()) {
// Ignore things that fail to load.
ResourceSaver::save(res);
}
}
EditorSettings::get_singleton()->set_project_metadata("surface_upgrade_tool", "resave_paths", Vector<String>());
// Remove the imported scenes/meshes from .import so they will be reimported automatically after this.
Vector<String> reimport_paths = EditorSettings::get_singleton()->get_project_metadata("surface_upgrade_tool", "reimport_paths", Vector<String>());
for (const String &file_path : reimport_paths) {
Ref<ConfigFile> config;
config.instantiate();
Error err = config->load(file_path);
if (err != OK) {
ERR_PRINT("Could not open " + file_path + " for upgrade.");
continue;
}
String remap_path = config->get_value("remap", "path", "");
if (remap_path.is_empty()) {
continue;
}
String path = OS::get_singleton()->get_resource_dir() + remap_path.replace_first("res://", "/");
print_verbose("Moving to trash: " + path);
err = OS::get_singleton()->move_to_trash(path);
if (err != OK) {
EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + remap_path + "\n");
}
}
EditorSettings::get_singleton()->set_project_metadata("surface_upgrade_tool", "reimport_paths", Vector<String>());
emit_signal(SNAME("upgrade_finished"));
}
void SurfaceUpgradeTool::_show_popup() {
RS::get_singleton()->set_surface_upgrade_callback(nullptr);
bool accepted = EditorNode::immediate_confirmation_dialog(TTR("This project uses meshes with an outdated mesh format from previous Godot versions. The engine needs to update the format in order to use those meshes.\n\nPress 'Upgrade & Re-save' to have the engine scan the project folder and automatically update and re-save all meshes and scenes. This update may take a few minutes. Upgrading will make the meshes incompatible with previous versions of Godot.\n\nPress 'Upgrade Only' to continue opening the scene as normal. The engine will update each mesh in memory, but the update will not be saved. Choosing this option will lead to slower load times every time this project is loaded."), TTR("Upgrade & Re-save"), TTR("Upgrade Only"), 500);
if (accepted) {
RS::get_singleton()->set_warn_on_surface_upgrade(false);
upgrade_all_meshes();
}
void SurfaceUpgradeTool::_bind_methods() {
ADD_SIGNAL(MethodInfo("upgrade_finished"));
}
SurfaceUpgradeTool::SurfaceUpgradeTool() {
RS::get_singleton()->set_surface_upgrade_callback(_show_popup);
singleton = this;
RS::get_singleton()->set_surface_upgrade_callback(_try_show_popup);
}
SurfaceUpgradeTool::~SurfaceUpgradeTool() {}

View file

@ -35,13 +35,31 @@
class EditorFileSystemDirectory;
class SurfaceUpgradeTool {
static void upgrade_all_meshes();
class SurfaceUpgradeTool : public Object {
GDCLASS(SurfaceUpgradeTool, Object);
static void _show_popup();
static void _add_files(EditorFileSystemDirectory *p_dir, HashSet<String> &r_paths, PackedStringArray &r_files);
static SurfaceUpgradeTool *singleton;
bool show_requested = false;
bool popped_up = false;
Mutex mutex;
static void _try_show_popup();
void _show_popup();
void _add_files(EditorFileSystemDirectory *p_dir, Vector<String> &r_reimport_paths, Vector<String> &r_resave_paths);
protected:
static void _bind_methods();
public:
static SurfaceUpgradeTool *get_singleton() { return singleton; };
bool is_show_requested() const { return show_requested; };
void show_popup() { _show_popup(); }
void begin_upgrade();
void finish_upgrade();
SurfaceUpgradeTool();
~SurfaceUpgradeTool();
};

View file

@ -2076,10 +2076,10 @@ void RenderingServer::fix_surface_compatibility(SurfaceData &p_surface, const St
}
if (warn_on_surface_upgrade) {
if (p_path.is_empty()) {
WARN_PRINT("A surface uses an old surface format and needs to be upgraded. The upgrade happens automatically at load time every time until the mesh is saved again or re-imported. Once saved (or re-imported), this mesh will be incompatible with earlier versions of Godot.");
} else {
WARN_PRINT("A surface of " + p_path + " uses an old surface format and needs to be upgraded. The upgrade happens automatically at load time every time until the mesh is saved again or re-imported. Once saved (or re-imported), this mesh will be incompatible with earlier versions of Godot.");
WARN_PRINT_ONCE_ED("At least one surface uses an old surface format and needs to be upgraded. The upgrade happens automatically at load time every time until the mesh is saved again or re-imported. Once saved (or re-imported), this mesh will be incompatible with earlier versions of Godot.");
if (!p_path.is_empty()) {
WARN_PRINT("A surface of " + p_path + " uses an old surface format and needs to be upgraded.");
}
}
#endif