[Bf-blender-cvs] [188419a] asset-experiments: WIP cache system for filebrowser entries.

Bastien Montagne noreply at git.blender.org
Fri Mar 27 22:38:59 CET 2015


Commit: 188419ac3d940174d28e279a19621292210af7ec
Author: Bastien Montagne
Date:   Fri Mar 27 10:50:43 2015 +0100
Branches: asset-experiments
https://developer.blender.org/rB188419ac3d940174d28e279a19621292210af7ec

WIP cache system for filebrowser entries.

Core part seems to be working, but this is still heavy WIP, much to do
to resume complete features from existing code. Mainly:

* Previews (those should only be ran on cached items now).
* Use block-caching to load in-display entries.
* Rework things like selection (cannot store selected states in entires anymore).

Also, internal listing is stupid currently (since it still stores everything
as FileDirEntry & co), not really crucial currently, but ultimately it'll
use its own, compact struct to keep full listing!

Also did some minor cleanup/renaming.

===================================================================

M	source/blender/editors/space_file/filelist.c
M	source/blender/editors/space_file/filelist.h
M	source/blender/editors/space_file/filesel.c
M	source/blender/editors/space_file/space_file.c

===================================================================

diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 57ca8b8..64f166d 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -47,6 +47,7 @@
 #include "BLI_blenlib.h"
 #include "BLI_fileops_types.h"
 #include "BLI_fnmatch.h"
+#include "BLI_ghash.h"
 #include "BLI_linklist.h"
 #include "BLI_math.h"
 #include "BLI_stack.h"
@@ -201,11 +202,24 @@ ListBase *folderlist_duplicate(ListBase *folderlist)
 /* ------------------FILELIST------------------------ */
 
 typedef struct FileListIntern {
-	/* XXX This must be reworked to keep 'all entries' storage to a minimum memory space! */
+	/* XXX This will be reworked to keep 'all entries' storage to a minimum memory space! */
 	ListBase entries;
 	FileDirEntry **filtered;
 } FileListIntern;
 
+#define FILELIST_ENTRYCACHESIZE 1024  /* Keep it a power of two! */
+typedef struct FileListEntryCache {
+	/* Block cache: all entries between start and end index. used for part of the list on diplay. */
+	FileDirEntry (*block_entries)[FILELIST_ENTRYCACHESIZE];
+	int block_start_index, block_end_index, block_cursor;
+
+	/* Misc cache: random indices, FIFO behavior.
+	 * Note: Not 100% sure we actually need that, time will say. */
+	int misc_cursor;
+	int misc_entries_indices[FILELIST_ENTRYCACHESIZE];
+	GHash *misc_entries;
+} FileListEntryCache;
+
 typedef struct FileListFilter {
 	bool hide_dot;
 	bool hide_parent;
@@ -237,6 +251,8 @@ typedef struct FileList {
 
 	struct FileListIntern filelist_intern;
 
+	struct FileListEntryCache filelist_cache;
+
 	bool need_thumbnails;
 
 	short max_recursion;
@@ -940,7 +956,7 @@ static void filelist_checkdir_main(struct FileList *filelist, char *r_dir)
 	filelist_checkdir_lib(filelist, r_dir);
 }
 
-static void filelist_entry_free(FileDirEntry *entry, const bool clear)
+static void filelist_entry_free(FileDirEntry *entry)
 {
 	if (entry->name) {
 		MEM_freeN(entry->name);
@@ -987,17 +1003,22 @@ static void filelist_entry_free(FileDirEntry *entry, const bool clear)
 	else if (entry->entry){
 		MEM_freeN(entry->entry);
 	}
-	if (clear) {
-		memset(entry, 0, sizeof(*entry));
-	}
 }
 
-static void filedirentryarr_free(FileDirEntryArr *array)
+#if 0  /* UNUSED */
+static void filelist_entry_clear(FileDirEntry *entry)
+{
+	filelist_entry_free(entry);
+	memset(entry, 0, sizeof(*entry));
+}
+#endif
+
+static void filelist_direntryarr_free(FileDirEntryArr *array)
 {
 	FileDirEntry *entry;
 
 	for (entry = array->entries.first; entry; entry = entry->next) {
-		filelist_entry_free(entry, false);
+		filelist_entry_free(entry);
 	}
 	BLI_freelistN(&array->entries);
 	array->nbr_entries = 0;
@@ -1011,17 +1032,60 @@ static void filelist_intern_free(FileListIntern *filelist_intern)
 	FileDirEntry *entry;
 
 	for (entry = filelist_intern->entries.first; entry; entry = entry->next) {
-		filelist_entry_free(entry, false);
+		filelist_entry_free(entry);
 	}
 	BLI_freelistN(&filelist_intern->entries);
 
 	MEM_SAFE_FREE(filelist_intern->filtered);
 }
 
+static FileDirEntry *filelist_intern_create_entry(FileList *filelist, const int index)
+{
+	/* Stupid code for now, later we will actually generate a new entry (from mempool)... */
+	return filelist->filelist_intern.filtered[index];
+}
+
+static void filelist_intern_release_entry(FileList *UNUSED(filelist), FileDirEntry *UNUSED(old))
+{
+	/* We do nothing here actually, later we'll give back the mem to the mempool... */
+}
+
+static void filelist_cache_init(FileListEntryCache *cache)
+{
+	cache->block_cursor = cache->block_start_index = cache->block_end_index = 0;
+
+	cache->misc_entries = BLI_ghash_ptr_new_ex(__func__, FILELIST_ENTRYCACHESIZE);
+	cache->misc_cursor = 0;
+}
+
+static void filelist_cache_free(FileListEntryCache *cache)
+{
+	/* Note we nearly have nothing to do here, entries are just 'borrowed', not owned by cache... */
+	if (cache->misc_entries) {
+		BLI_ghash_free(cache->misc_entries, NULL, NULL);
+	}
+}
+
+static void filelist_cache_clear(FileListEntryCache *cache)
+{
+	/* Note we nearly have nothing to do here, entries are just 'borrowed', not owned by cache... */
+	cache->block_cursor = cache->block_start_index = cache->block_end_index = 0;
+
+	if (cache->misc_entries) {
+		BLI_ghash_clear_ex(cache->misc_entries, NULL, NULL, FILELIST_ENTRYCACHESIZE);
+	}
+	else {
+		cache->misc_entries = BLI_ghash_ptr_new_ex(__func__, FILELIST_ENTRYCACHESIZE);
+		cache->misc_cursor = 0;
+	}
+}
+
 FileList *filelist_new(short type)
 {
 	FileList *p = MEM_callocN(sizeof(*p), __func__);
 
+	filelist_cache_init(&p->filelist_cache);
+
 	switch (type) {
 		case FILE_MAIN:
 			p->checkdirf = filelist_checkdir_main;
@@ -1050,7 +1114,9 @@ void filelist_clear(struct FileList *filelist)
 
 	filelist_filter_clear(filelist);
 
-	filedirentryarr_free(&filelist->filelist);
+	filelist_direntryarr_free(&filelist->filelist);
+
+	filelist_cache_clear(&filelist->filelist_cache);
 
 	filelist_intern_free(&filelist->filelist_intern);
 }
@@ -1063,6 +1129,7 @@ void filelist_free(struct FileList *filelist)
 	}
 	
 	filelist_clear(filelist);
+	filelist_cache_free(&filelist->filelist_cache);  /* XXX TODO stupid! */
 
 	memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
 
@@ -1176,18 +1243,48 @@ void filelist_clear_refresh(struct FileList *filelist)
 
 FileDirEntry *filelist_file(struct FileList *filelist, int index)
 {
+	FileDirEntry *ret = NULL, *old;
+	FileListEntryCache *cache = &filelist->filelist_cache;
+	int old_index;
+
 	if ((index < 0) || (index >= filelist->filelist.nbr_entries_filtered)) {
-		return NULL;
+		return ret;
 	}
-	return filelist->filelist_intern.filtered[index];
+
+	if (index >= cache->block_start_index && index < cache->block_end_index) {
+		const int idx = (index - cache->block_start_index + cache->block_cursor) % FILELIST_ENTRYCACHESIZE;
+		return cache->block_entries[idx];
+	}
+
+	if ((ret = BLI_ghash_lookup(cache->misc_entries, SET_INT_IN_POINTER(index)))) {
+		return ret;
+	}
+
+//	printf("requesting file %d (not yet cached)\n", index);
+
+	/* Else, we have to add new entry to 'misc' cache - and possibly make room for it first! */
+	ret = filelist_intern_create_entry(filelist, index);
+	old_index = cache->misc_entries_indices[cache->misc_cursor];
+	if ((old = BLI_ghash_popkey(cache->misc_entries, SET_INT_IN_POINTER(old_index), NULL))) {
+		filelist_intern_release_entry(filelist, old);
+	}
+	BLI_ghash_insert(cache->misc_entries, SET_INT_IN_POINTER(index), ret);
+	cache->misc_entries_indices[cache->misc_cursor] = index;
+	cache->misc_cursor = (cache->misc_cursor + 1) % FILELIST_ENTRYCACHESIZE;
+
+	return ret;
 }
 
-int filelist_find(struct FileList *filelist, const char *filename)
+int filelist_file_findpath(struct FileList *filelist, const char *filename)
 {
 	int fidx = -1;
 	
-	if (filelist->filelist.nbr_entries_filtered < 0)
+	if (filelist->filelist.nbr_entries_filtered < 0) {
 		return fidx;
+	}
+
+	/* XXX TODO Cache could probably use a ghash on paths too? Not really urgent though.
+     *          In fact, we may get rid of this func in the end (only used to find again renamed entry afaik). */
 
 	for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
 		if (STREQ(filelist->filelist_intern.filtered[fidx]->relpath, filename)) {
@@ -1198,6 +1295,108 @@ int filelist_find(struct FileList *filelist, const char *filename)
 	return -1;
 }
 
+/* Load in cache all entries "around" given index (as much as block cache may hold). */
+bool filelist_file_cache_block(struct FileList *filelist, const int index)
+{
+	FileListEntryCache *cache = &filelist->filelist_cache;
+
+	const int nbr_entries = filelist->filelist.nbr_entries_filtered;
+	int start_index = max_ii(0, index - (FILELIST_ENTRYCACHESIZE / 2));
+	int end_index = min_ii(nbr_entries, index + (FILELIST_ENTRYCACHESIZE / 2));
+	const int curr_block_size = cache->block_end_index - cache->block_start_index;
+
+	if ((index < 0) || (index >= nbr_entries)) {
+		return false;
+	}
+
+	/* Maximize cached range! */
+	if ((end_index - start_index) < FILELIST_ENTRYCACHESIZE) {
+		if (start_index == 0) {
+			end_index = min_ii(nbr_entries, start_index + FILELIST_ENTRYCACHESIZE);
+		}
+		else if (end_index == nbr_entries) {
+			start_index = max_ii(0, end_index - FILELIST_ENTRYCACHESIZE);
+		}
+	}
+
+	BLI_assert((end_index - start_index) <= FILELIST_ENTRYCACHESIZE) ;
+
+	if ((start_index == cache->block_end_index) && (end_index == cache->block_start_index)) {
+		/* Nothing to do! */
+		return true;
+	}
+
+	if ((start_index >= cache->block_end_index) || (end_index <= cache->block_start_index)) {
+		/* New cached block does not overlap existing one, simple. */
+		memcpy(cache->block_entries, &filelist->filelist_intern.filtered[start_index],
+		       sizeof(*cache->block_entries) * (end_index - start_index));
+		cache->block_start_index = start_index;
+		cache->block_end_index = end_index;
+		cache->block_cursor = 0;
+		return true;
+	}
+
+	if (end_index > cache->block_end_index) {
+		/* Add (request) needed entries after already cached ones. */
+		/* Note: We need some index black magic to wrap around (cycle) inside our FILELIST_ENTRYCACHESIZE array... */
+		int size1 = end_index - cache->block_end_index;
+		int size2 = 0;
+		int idx1, idx2;
+
+		idx1 = (cache->block_cursor + curr_block_size) % FILELIST_ENTRYCACHESIZE;
+		if ((idx1 + size1) > FILELIST_ENTRYCACHESIZE) {
+			size2 = size1;
+			size1 = FILELIST_ENTRYCACHESIZE - idx1;
+			size2 -= size1;
+			idx2 = 0;
+		}
+
+		if (size2) {
+			memcpy(&cache->block_entries[idx2], &filelist->filelist_intern.filtered[end_index - size2],
+			       sizeof(*cache->block_entries) * size2);
+		}
+		memcpy(&cache->block_entries[idx1], &filelist->filelist_intern.filtered[end_index - size1 - size2],
+		       sizeof(*cache->block_entries) * size1);
+	}
+	cache->block_end_index = end_index;
+
+	if (start_index < cache->block_start_index) {
+		/* Add (request) needed entries before already cached ones. */
+		/* Note: We need some index black magic to wrap around (cycle) inside our FILELIST_ENTRYCACHESIZE array... */
+		int size1 = cache->block_start_index - start_index;
+		int size2 = 0;
+		int idx1, idx2;
+
+		if (size1 > cache->bl

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list