Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make scan for projects threaded #91064

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 88 additions & 15 deletions editor/project_manager/project_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
#include "editor/project_manager/project_tag.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/progress_bar.h"
#include "scene/gui/texture_button.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/image_texture.h"
Expand Down Expand Up @@ -358,21 +360,70 @@ bool ProjectList::project_feature_looks_like_version(const String &p_feature) {
void ProjectList::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_PROCESS: {
// Load icons as a coroutine to speed up launch when you have hundreds of projects
// Load icons as a coroutine to speed up launch when you have hundreds of projects.
if (_icon_load_index < _projects.size()) {
Item &item = _projects.write[_icon_load_index];
if (item.control->should_load_project_icon()) {
_load_project_icon(_icon_load_index);
}
_icon_load_index++;

// Scan directories in thread to avoid blocking the window.
} else if (scan_data && scan_data->scan_in_progress.is_set()) {
// Wait for the thread.
} else {
set_process(false);
if (scan_data) {
_scan_finished();
}
}
} break;
}
}

// Projects scan.

void ProjectList::_scan_thread(void *p_scan_data) {
ScanData *scan_data = static_cast<ScanData *>(p_scan_data);

for (const String &base_path : scan_data->paths_to_scan) {
print_verbose(vformat("Scanning for projects in \"%s\".", base_path));
_scan_folder_recursive(base_path, &scan_data->found_projects, scan_data->scan_in_progress);

if (!scan_data->scan_in_progress.is_set()) {
print_verbose("Scan aborted.");
break;
}
}
print_verbose(vformat("Found %d project(s).", scan_data->found_projects.size()));
scan_data->scan_in_progress.clear();
}

void ProjectList::_scan_finished() {
if (scan_data->scan_in_progress.is_set()) {
// Abort scanning.
scan_data->scan_in_progress.clear();
}

scan_data->thread->wait_to_finish();
memdelete(scan_data->thread);
if (scan_progress) {
scan_progress->hide();
}

for (const String &E : scan_data->found_projects) {
add_project(E, false);
}
memdelete(scan_data);
scan_data = nullptr;

save_config();

if (ProjectManager::get_singleton()->is_initialized()) {
update_project_list();
}
}

// Initialization & loading.

void ProjectList::_migrate_config() {
Expand Down Expand Up @@ -624,25 +675,39 @@ void ProjectList::find_projects(const String &p_path) {
}

void ProjectList::find_projects_multiple(const PackedStringArray &p_paths) {
List<String> projects;
if (!scan_progress && is_inside_tree()) {
scan_progress = memnew(AcceptDialog);
scan_progress->set_title(TTR("Scanning"));
scan_progress->set_ok_button_text(TTR("Cancel"));

for (int i = 0; i < p_paths.size(); i++) {
const String &base_path = p_paths.get(i);
print_verbose(vformat("Scanning for projects in \"%s\".", base_path));
VBoxContainer *vb = memnew(VBoxContainer);
scan_progress->add_child(vb);

_scan_folder_recursive(base_path, &projects);
print_verbose(vformat("Found %d project(s).", projects.size()));
}
Label *label = memnew(Label);
label->set_text(TTR("Scanning for projects..."));
vb->add_child(label);

for (const String &E : projects) {
add_project(E, false);
ProgressBar *progress = memnew(ProgressBar);
progress->set_indeterminate(true);
vb->add_child(progress);

add_child(scan_progress);
scan_progress->connect(SceneStringName(confirmed), callable_mp(this, &ProjectList::_scan_finished));
scan_progress->connect("canceled", callable_mp(this, &ProjectList::_scan_finished));
}

save_config();
scan_data = memnew(ScanData);
scan_data->paths_to_scan = p_paths;
scan_data->scan_in_progress.set();

if (ProjectManager::get_singleton()->is_initialized()) {
update_project_list();
scan_data->thread = memnew(Thread);
scan_data->thread->start(_scan_thread, scan_data);

if (scan_progress) {
scan_progress->reset_size();
scan_progress->popup_centered();
}
set_process(true);
}

void ProjectList::load_project_list() {
Expand All @@ -656,16 +721,24 @@ void ProjectList::load_project_list() {
}
}

void ProjectList::_scan_folder_recursive(const String &p_path, List<String> *r_projects) {
void ProjectList::_scan_folder_recursive(const String &p_path, List<String> *r_projects, const SafeFlag &p_scan_active) {
if (!p_scan_active.is_set()) {
return;
}

Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
Error error = da->change_dir(p_path);
ERR_FAIL_COND_MSG(error != OK, vformat("Failed to open the path \"%s\" for scanning (code %d).", p_path, error));

da->list_dir_begin();
String n = da->get_next();
while (!n.is_empty()) {
if (!p_scan_active.is_set()) {
return;
}

if (da->current_is_dir() && n[0] != '.') {
_scan_folder_recursive(da->get_current_dir().path_join(n), r_projects);
_scan_folder_recursive(da->get_current_dir().path_join(n), r_projects, p_scan_active);
} else if (n == "project.godot") {
r_projects->push_back(da->get_current_dir());
}
Expand Down
17 changes: 16 additions & 1 deletion editor/project_manager/project_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "scene/gui/box_container.h"
#include "scene/gui/scroll_container.h"

class AcceptDialog;
class Button;
class Label;
class ProjectList;
Expand Down Expand Up @@ -176,6 +177,20 @@ class ProjectList : public ScrollContainer {

VBoxContainer *project_list_vbox = nullptr;

// Projects scan.

struct ScanData {
Thread *thread = nullptr;
PackedStringArray paths_to_scan;
List<String> found_projects;
SafeFlag scan_in_progress;
};
ScanData *scan_data = nullptr;
AcceptDialog *scan_progress = nullptr;

static void _scan_thread(void *p_scan_data);
void _scan_finished();

// Initialization & loading.

void _migrate_config();
Expand All @@ -186,7 +201,7 @@ class ProjectList : public ScrollContainer {

// Project list updates.

void _scan_folder_recursive(const String &p_path, List<String> *r_projects);
static void _scan_folder_recursive(const String &p_path, List<String> *r_projects, const SafeFlag &p_scan_active);

// Project list items.

Expand Down
6 changes: 3 additions & 3 deletions scene/gui/progress_bar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void ProgressBar::_notification(int p_what) {
Size2 size = get_size();
real_t fill_size = MIN(size.width, size.height) * 2;

if (Engine::get_singleton()->is_editor_hint() && !editor_preview_indeterminate) {
if (is_part_of_edited_scene() && !editor_preview_indeterminate) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes were needed to run indeterminate progress in Project Manager.
Looking at the code, I don't get how it already works in the editor (in template download).

// Center the filled bar when we're not previewing the animation.
_inderminate_fill_progress = (MAX(size.width, size.height) / 2) + (fill_size / 2);
}
Expand Down Expand Up @@ -217,7 +217,7 @@ void ProgressBar::set_indeterminate(bool p_indeterminate) {
indeterminate = p_indeterminate;
_inderminate_fill_progress = 0;

bool should_process = !Engine::get_singleton()->is_editor_hint() || editor_preview_indeterminate;
bool should_process = !is_part_of_edited_scene() || editor_preview_indeterminate;
set_process_internal(indeterminate && should_process);

notify_property_list_changed();
Expand All @@ -235,7 +235,7 @@ void ProgressBar::set_editor_preview_indeterminate(bool p_preview_indeterminate)
}
editor_preview_indeterminate = p_preview_indeterminate;

if (Engine::get_singleton()->is_editor_hint()) {
if (is_part_of_edited_scene()) {
_inderminate_fill_progress = 0;
set_process_internal(indeterminate && editor_preview_indeterminate);
queue_redraw();
Expand Down