diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index dc59bb1286d..4a27893e985 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -2888,7 +2888,65 @@ struct TodoDir { char *dir; }; -static int filelist_readjob_list_dir(const char *root, +struct FileListReadJob { + ThreadMutex lock; + char main_name[FILE_MAX]; + Main *current_main; + FileList *filelist; + + /** The path currently being read, relative to the filelist root directory. Needed for recursive + * reading. The full file path is then composed like: `//. + * (whereby the file name may also be a library path within a .blend, e.g. + * `Materials/Material.001`). */ + char cur_relbase[FILE_MAX_LIBEXTRA]; + + /** Set to request a partial read that only adds files representing #Main data (IDs). Used when + * #Main may have received changes of interest (e.g. asset removed or renamed). */ + bool only_main_data; + + /** Shallow copy of #filelist for thread-safe access. + * + * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist + * into #filelist in a thread-safe way. + * + * #tmp_filelist also keeps an `AssetLibrary *` so that it can be loaded in the same thread, + * and moved to #filelist once all categories are loaded. + * + * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be + * set to nullptr to avoid double-freeing them. */ + FileList *tmp_filelist; +}; + +/** + * Append \a filename (or even a path inside of a .blend, like `Material/Material.001`), to the + * current relative path being read within the filelist root. The returned string needs freeing + * with #MEM_freeN(). + */ +static char *current_relpath_append(const FileListReadJob *job_params, const char *filename) +{ + const char *relbase = job_params->cur_relbase; + + /* Early exit, nothing to join. */ + if (!relbase[0]) { + return BLI_strdup(filename); + } + + BLI_assert(relbase[strlen(relbase) - 1] == SEP); + BLI_assert(BLI_path_is_rel(relbase)); + + char relpath[FILE_MAX_LIBEXTRA]; + /* Using #BLI_path_join works but isn't needed as `rel_subdir` has a trailing slash. */ + BLI_string_join(relpath, + sizeof(relpath), + /* + 2 to remove "//" relative path prefix. */ + relbase + 2, + filename); + + return BLI_strdup(relpath); +} + +static int filelist_readjob_list_dir(FileListReadJob *job_params, + const char *root, ListBase *entries, const char *filter_glob, const bool do_lib, @@ -2911,7 +2969,7 @@ static int filelist_readjob_list_dir(const char *root, } entry = MEM_cnew(__func__); - entry->relpath = static_cast(MEM_dupallocN(files[i].relname)); + entry->relpath = current_relpath_append(job_params, files[i].relname); entry->st = files[i].s; BLI_path_join(full_path, FILE_MAX, root, entry->relpath); @@ -2998,11 +3056,11 @@ enum ListLibOptions { }; ENUM_OPERATORS(ListLibOptions, LIST_LIB_ADD_PARENT); -static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idcode, - const char *group_name) +static FileListInternEntry *filelist_readjob_list_lib_group_create( + const FileListReadJob *job_params, const int idcode, const char *group_name) { FileListInternEntry *entry = MEM_cnew(__func__); - entry->relpath = BLI_strdup(group_name); + entry->relpath = current_relpath_append(job_params, group_name); entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR; entry->blentype = idcode; return entry; @@ -3012,19 +3070,21 @@ static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idc * \warning: This "steals" the asset metadata from \a datablock_info. Not great design but fixing * this requires redesigning things on the caller side for proper ownership management. */ -static void filelist_readjob_list_lib_add_datablock(FileList *filelist, +static void filelist_readjob_list_lib_add_datablock(FileListReadJob *job_params, ListBase *entries, BLODataBlockInfo *datablock_info, const bool prefix_relpath_with_group_name, const int idcode, const char *group_name) { + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ FileListInternEntry *entry = MEM_cnew(__func__); if (prefix_relpath_with_group_name) { - entry->relpath = BLI_sprintfN("%s/%s", group_name, datablock_info->name); + std::string datablock_path = StringRef(group_name) + "/" + datablock_info->name; + entry->relpath = current_relpath_append(job_params, datablock_path.c_str()); } else { - entry->relpath = BLI_strdup(datablock_info->name); + entry->relpath = current_relpath_append(job_params, datablock_info->name); } entry->typeflag |= FILE_TYPE_BLENDERLIB; if (datablock_info) { @@ -3048,7 +3108,7 @@ static void filelist_readjob_list_lib_add_datablock(FileList *filelist, BLI_addtail(entries, entry); } -static void filelist_readjob_list_lib_add_datablocks(FileList *filelist, +static void filelist_readjob_list_lib_add_datablocks(FileListReadJob *job_params, ListBase *entries, LinkNode *datablock_infos, const bool prefix_relpath_with_group_name, @@ -3058,12 +3118,12 @@ static void filelist_readjob_list_lib_add_datablocks(FileList *filelist, for (LinkNode *ln = datablock_infos; ln; ln = ln->next) { struct BLODataBlockInfo *datablock_info = static_cast(ln->link); filelist_readjob_list_lib_add_datablock( - filelist, entries, datablock_info, prefix_relpath_with_group_name, idcode, group_name); + job_params, entries, datablock_info, prefix_relpath_with_group_name, idcode, group_name); } } static void filelist_readjob_list_lib_add_from_indexer_entries( - FileList *filelist, + FileListReadJob *job_params, ListBase *entries, const FileIndexerEntries *indexer_entries, const bool prefix_relpath_with_group_name) @@ -3071,7 +3131,7 @@ static void filelist_readjob_list_lib_add_from_indexer_entries( for (const LinkNode *ln = indexer_entries->entries; ln; ln = ln->next) { FileIndexerEntry *indexer_entry = static_cast(ln->link); const char *group_name = BKE_idtype_idcode_to_name(indexer_entry->idcode); - filelist_readjob_list_lib_add_datablock(filelist, + filelist_readjob_list_lib_add_datablock(job_params, entries, &indexer_entry->datablock_info, prefix_relpath_with_group_name, @@ -3080,10 +3140,11 @@ static void filelist_readjob_list_lib_add_from_indexer_entries( } } -static FileListInternEntry *filelist_readjob_list_lib_navigate_to_parent_entry_create(void) +static FileListInternEntry *filelist_readjob_list_lib_navigate_to_parent_entry_create( + const FileListReadJob *job_params) { FileListInternEntry *entry = MEM_cnew(__func__); - entry->relpath = BLI_strdup(FILENAME_PARENT); + entry->relpath = current_relpath_append(job_params, FILENAME_PARENT); entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR); return entry; } @@ -3100,7 +3161,7 @@ typedef struct FileIndexer { void *user_data; } FileIndexer; -static int filelist_readjob_list_lib_populate_from_index(FileList *filelist, +static int filelist_readjob_list_lib_populate_from_index(FileListReadJob *job_params, ListBase *entries, const ListLibOptions options, const int read_from_index, @@ -3108,12 +3169,13 @@ static int filelist_readjob_list_lib_populate_from_index(FileList *filelist, { int navigate_to_parent_len = 0; if (options & LIST_LIB_ADD_PARENT) { - FileListInternEntry *entry = filelist_readjob_list_lib_navigate_to_parent_entry_create(); + FileListInternEntry *entry = filelist_readjob_list_lib_navigate_to_parent_entry_create( + job_params); BLI_addtail(entries, entry); navigate_to_parent_len = 1; } - filelist_readjob_list_lib_add_from_indexer_entries(filelist, entries, indexer_entries, true); + filelist_readjob_list_lib_add_from_indexer_entries(job_params, entries, indexer_entries, true); return read_from_index + navigate_to_parent_len; } @@ -3121,7 +3183,7 @@ static int filelist_readjob_list_lib_populate_from_index(FileList *filelist, * \return The number of entries found if the \a root path points to a valid library file. * Otherwise returns no value (#std::nullopt). */ -static std::optional filelist_readjob_list_lib(FileList *filelist, +static std::optional filelist_readjob_list_lib(FileListReadJob *job_params, const char *root, ListBase *entries, const ListLibOptions options, @@ -3161,7 +3223,7 @@ static std::optional filelist_readjob_list_lib(FileList *filelist, dir, &indexer_entries, &read_from_index, indexer_runtime->user_data); if (indexer_result == FILE_INDEXER_ENTRIES_LOADED) { int entries_read = filelist_readjob_list_lib_populate_from_index( - filelist, entries, options, read_from_index, &indexer_entries); + job_params, entries, options, read_from_index, &indexer_entries); ED_file_indexer_entries_clear(&indexer_entries); return entries_read; } @@ -3180,7 +3242,8 @@ static std::optional filelist_readjob_list_lib(FileList *filelist, * the code clean and readable and not counting in a single variable. */ int navigate_to_parent_len = 0; if (options & LIST_LIB_ADD_PARENT) { - FileListInternEntry *entry = filelist_readjob_list_lib_navigate_to_parent_entry_create(); + FileListInternEntry *entry = filelist_readjob_list_lib_navigate_to_parent_entry_create( + job_params); BLI_addtail(entries, entry); navigate_to_parent_len = 1; } @@ -3192,7 +3255,7 @@ static std::optional filelist_readjob_list_lib(FileList *filelist, LinkNode *datablock_infos = BLO_blendhandle_get_datablock_info( libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &datablock_len); filelist_readjob_list_lib_add_datablocks( - filelist, entries, datablock_infos, false, idcode, group); + job_params, entries, datablock_infos, false, idcode, group); BLI_linklist_freeN(datablock_infos); } else { @@ -3202,8 +3265,8 @@ static std::optional filelist_readjob_list_lib(FileList *filelist, for (LinkNode *ln = groups; ln; ln = ln->next) { const char *group_name = static_cast(ln->link); const int idcode = groupname_to_code(group_name); - FileListInternEntry *group_entry = filelist_readjob_list_lib_group_create(idcode, - group_name); + FileListInternEntry *group_entry = filelist_readjob_list_lib_group_create( + job_params, idcode, group_name); BLI_addtail(entries, group_entry); if (options & LIST_LIB_RECURSIVE) { @@ -3211,7 +3274,7 @@ static std::optional filelist_readjob_list_lib(FileList *filelist, LinkNode *group_datablock_infos = BLO_blendhandle_get_datablock_info( libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &group_datablock_len); filelist_readjob_list_lib_add_datablocks( - filelist, entries, group_datablock_infos, true, idcode, group_name); + job_params, entries, group_datablock_infos, true, idcode, group_name); if (use_indexer) { ED_file_indexer_entries_extend_from_datablock_infos( &indexer_entries, group_datablock_infos, idcode); @@ -3410,28 +3473,6 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) } #endif -struct FileListReadJob { - ThreadMutex lock; - char main_name[FILE_MAX]; - Main *current_main; - FileList *filelist; - /** Set to request a partial read that only adds files representing #Main data (IDs). Used when - * #Main may have received changes of interest (e.g. asset removed or renamed). */ - bool only_main_data; - - /** Shallow copy of #filelist for thread-safe access. - * - * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist - * into #filelist in a thread-safe way. - * - * #tmp_filelist also keeps an `AssetLibrary *` so that it can be loaded in the same thread, - * and moved to #filelist once all categories are loaded. - * - * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be - * set to nullptr to avoid double-freeing them. */ - FileList *tmp_filelist; -}; - static void filelist_readjob_append_entries(FileListReadJob *job_params, ListBase *from_entries, int from_entries_num, @@ -3545,6 +3586,9 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, BLI_path_normalize_dir(root, rel_subdir, sizeof(rel_subdir)); BLI_path_rel(rel_subdir, root); + /* Update the current relative base path within the filelist root. */ + BLI_strncpy(job_params->cur_relbase, rel_subdir, sizeof(job_params->cur_relbase)); + bool is_lib = false; if (do_lib) { ListLibOptions list_lib_options = LIST_LIB_OPTION_NONE; @@ -3563,7 +3607,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, list_lib_options |= LIST_LIB_ASSETS_ONLY; } std::optional lib_entries_num = filelist_readjob_list_lib( - filelist, subdir, &entries, list_lib_options, &indexer_runtime); + job_params, subdir, &entries, list_lib_options, &indexer_runtime); if (lib_entries_num) { is_lib = true; entries_num += *lib_entries_num; @@ -3572,19 +3616,11 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, if (!is_lib && BLI_is_dir(subdir)) { entries_num = filelist_readjob_list_dir( - subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar); + job_params, subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar); } LISTBASE_FOREACH (FileListInternEntry *, entry, &entries) { entry->uid = filelist_uid_generate(filelist); - - /* When loading entries recursive, the `rel_path` should be relative from the root dir. - * we combine the relative path to the `subdir` with the relative path of the entry. - * Using #BLI_path_join works but isn't needed as `rel_subdir` has a trailing slash. */ - BLI_string_join(dir, sizeof(dir), rel_subdir, entry->relpath); - MEM_freeN(entry->relpath); - entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//' - * added by BLI_path_rel to rel_subdir. */ entry->name = fileentry_uiname(root, entry, dir); entry->free_name = true; @@ -3709,7 +3745,7 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params, const char *id_code_name = BKE_idtype_idcode_to_name(GS(id_iter->name)); entry = MEM_cnew(__func__); - entry->relpath = BLI_strdup(id_code_name); + entry->relpath = current_relpath_append(job_params, id_code_name); entry->name = id_iter->name + 2; entry->free_name = false; entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_ASSET;