Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/datafiles/userdef/userdef_default.c5
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py9
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h9
-rw-r--r--source/blender/blenkernel/intern/seqcache.c772
-rw-r--r--source/blender/blenkernel/intern/sequencer.c32
-rw-r--r--source/blender/blenlib/BLI_fileops.h8
-rw-r--r--source/blender/blenlib/intern/fileops.c95
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h4
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h14
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c59
11 files changed, 948 insertions, 61 deletions
diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c
index 18750c12d55..ec4b214c5f5 100644
--- a/release/datafiles/userdef/userdef_default.c
+++ b/release/datafiles/userdef/userdef_default.c
@@ -221,6 +221,11 @@ const UserDef U_default = {
.temp_win_sizey = 600,
},
+ .sequencer_disk_cache_dir = "",
+ .sequencer_disk_cache_compression = 0,
+ .sequencer_disk_cache_size_limit = 100,
+ .sequencer_disk_cache_flag = 0,
+
.runtime =
{
.is_dirty = 0,
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 4cccf429179..03828632d64 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -628,6 +628,15 @@ class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel):
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.prop(system, "use_sequencer_disk_cache")
+ flow.prop(system, "sequencer_disk_cache_dir")
+ flow.prop(system, "sequencer_disk_cache_size_limit")
+ flow.prop(system, "sequencer_disk_cache_compression")
+
+ layout.separator()
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+
flow.prop(system, "texture_time_out", text="Texture Time Out")
flow.prop(system, "texture_collection_rate", text="Garbage Collection Rate")
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index 31951cc101a..df0f0e730a5 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -312,19 +312,22 @@ void BKE_sequencer_proxy_set(struct Sequence *seq, bool value);
struct ImBuf *BKE_sequencer_cache_get(const SeqRenderData *context,
struct Sequence *seq,
float cfra,
- int type);
+ int type,
+ bool skip_disk_cache);
void BKE_sequencer_cache_put(const SeqRenderData *context,
struct Sequence *seq,
float cfra,
int type,
struct ImBuf *nval,
- float cost);
+ float cost,
+ bool skip_disk_cache);
bool BKE_sequencer_cache_put_if_possible(const SeqRenderData *context,
struct Sequence *seq,
float cfra,
int type,
struct ImBuf *nval,
- float cost);
+ float cost,
+ bool skip_disk_cache);
bool BKE_sequencer_cache_recycle_item(struct Scene *scene);
void BKE_sequencer_cache_free_temp_cache(struct Scene *scene, short id, int cfra);
void BKE_sequencer_cache_destruct(struct Scene *scene);
diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c
index aa4066b643f..f98af4a9406 100644
--- a/source/blender/blenkernel/intern/seqcache.c
+++ b/source/blender/blenkernel/intern/seqcache.c
@@ -22,20 +22,29 @@
#include <stddef.h>
#include <memory.h>
+#include <time.h>
#include "MEM_guardedalloc.h"
#include "DNA_sequence_types.h"
#include "DNA_scene_types.h"
+#include "DNA_space_types.h" /* for FILE_MAX. */
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+#include "IMB_colormanagement.h"
#include "BLI_mempool.h"
#include "BLI_threads.h"
#include "BLI_listbase.h"
#include "BLI_ghash.h"
+#include "BLI_blenlib.h"
+#include "BLI_endian_switch.h"
+#include "BLI_fileops.h"
+#include "BLI_fileops_types.h"
+#include "BLI_path_util.h"
+#include "BKE_global.h"
#include "BKE_sequencer.h"
#include "BKE_scene.h"
#include "BKE_main.h"
@@ -63,15 +72,75 @@
* entries one by one in reverse order to their creation.
*
* User can exclude caching of some images. Such entries will have is_temp_cache set.
+ *
+ *
+ * Disk Cache Design Notes
+ * =======================
+ *
+ * Disk cache uses directory specified in user preferences
+ * For each cached non-temp image, image data and supplementary info are written to HDD.
+ * Multiple(DCACHE_IMAGES_PER_FILE) images share the same file.
+ * Each of these files contains header DiskCacheHeader followed by image data.
+ * Zlib compression with user definable level can be used to compress image data(per image)
+ * Images are written in oreder in which they are rendered.
+ * Overwriting of individual entry is not possible.
+ * Stored images are deleted by invalidation, or when size of all files exceeds maximum
+ * size specified in user preferences.
+ * To distinguish 2 blend files with same name, scene->ed->disk_cache_timestamp
+ * is used as UID. Blend file can still be copied manually which may cause conflict.
+ *
*/
+/* <cache type>-<resolution X>x<resolution Y>-<rendersize>%(<view_id>)-<frame no>.dcf */
+#define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf"
+#define DCACHE_IMAGES_PER_FILE 100
+#define DCACHE_CURRENT_VERSION 1
+#define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */
+
+typedef struct DiskCacheHeaderEntry {
+ unsigned char encoding;
+ uint64_t frameno;
+ uint64_t size_compressed;
+ uint64_t size_raw;
+ uint64_t offset;
+ char colorspace_name[COLORSPACE_NAME_MAX];
+} DiskCacheHeaderEntry;
+
+typedef struct DiskCacheHeader {
+ DiskCacheHeaderEntry entry[DCACHE_IMAGES_PER_FILE];
+} DiskCacheHeader;
+
+typedef struct SeqDiskCache {
+ Main *bmain;
+ int64_t timestamp;
+ ListBase files;
+ ThreadMutex read_write_mutex;
+ size_t size_total;
+} SeqDiskCache;
+
+typedef struct DiskCacheFile {
+ struct DiskCacheFile *next, *prev;
+ char path[FILE_MAX];
+ char dir[FILE_MAXDIR];
+ char file[FILE_MAX];
+ BLI_stat_t fstat;
+ int cache_type;
+ int rectx;
+ int recty;
+ int render_size;
+ int view_id;
+ int start_frame;
+} DiskCacheFile;
+
typedef struct SeqCache {
+ Main *bmain;
struct GHash *hash;
ThreadMutex iterator_mutex;
struct BLI_mempool *keys_pool;
struct BLI_mempool *items_pool;
struct SeqCacheKey *last_key;
size_t memory_used;
+ SeqDiskCache *disk_cache;
} SeqCache;
typedef struct SeqCacheItem {
@@ -82,8 +151,8 @@ typedef struct SeqCacheItem {
typedef struct SeqCacheKey {
struct SeqCache *cache_owner;
void *userkey;
- struct SeqCacheKey *link_prev; /* Used for linking intermediate items to final frame */
- struct SeqCacheKey *link_next; /* Used for linking intermediate items to final frame */
+ struct SeqCacheKey *link_prev; /* Used for linking intermediate items to final frame. */
+ struct SeqCacheKey *link_next; /* Used for linking intermediate items to final frame. */
struct Sequence *seq;
SeqRenderData context;
float nfra;
@@ -95,6 +164,530 @@ typedef struct SeqCacheKey {
} SeqCacheKey;
static ThreadMutex cache_create_lock = BLI_MUTEX_INITIALIZER;
+static float seq_cache_cfra_to_frame_index(Sequence *seq, float cfra);
+static float seq_cache_frame_index_to_cfra(Sequence *seq, float nfra);
+
+static char *seq_disk_cache_base_dir(void)
+{
+ return U.sequencer_disk_cache_dir;
+}
+
+static int seq_disk_cache_compression_level(void)
+{
+ switch (U.sequencer_disk_cache_compression) {
+ case USER_SEQ_DISK_CACHE_COMPRESSION_NONE:
+ return 0;
+ case USER_SEQ_DISK_CACHE_COMPRESSION_LOW:
+ return 1;
+ case USER_SEQ_DISK_CACHE_COMPRESSION_HIGH:
+ return 9;
+ }
+
+ return U.sequencer_disk_cache_compression;
+}
+
+static size_t seq_disk_cache_size_limit(void)
+{
+ return (size_t)U.sequencer_disk_cache_size_limit * (1024 * 1024 * 1024);
+}
+
+static bool seq_disk_cache_is_enabled(Main *bmain)
+{
+ return (U.sequencer_disk_cache_dir[0] != '\0' && U.sequencer_disk_cache_size_limit != 0 &&
+ (U.sequencer_disk_cache_flag & SEQ_CACHE_DISK_CACHE_ENABLE) != 0 &&
+ bmain->name[0] != '\0');
+}
+
+static DiskCacheFile *seq_disk_cache_add_file_to_list(SeqDiskCache *disk_cache, const char *path)
+{
+
+ DiskCacheFile *cache_file = MEM_callocN(sizeof(DiskCacheFile), "SeqDiskCacheFile");
+ char dir[FILE_MAXDIR], file[FILE_MAX];
+ BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
+ BLI_strncpy(cache_file->path, path, sizeof(cache_file->path));
+ BLI_strncpy(cache_file->dir, dir, sizeof(cache_file->dir));
+ BLI_strncpy(cache_file->file, file, sizeof(cache_file->file));
+ sscanf(file,
+ DCACHE_FNAME_FORMAT,
+ &cache_file->cache_type,
+ &cache_file->rectx,
+ &cache_file->recty,
+ &cache_file->render_size,
+ &cache_file->view_id,
+ &cache_file->start_frame);
+ cache_file->start_frame *= DCACHE_IMAGES_PER_FILE;
+ BLI_addtail(&disk_cache->files, cache_file);
+ return cache_file;
+}
+
+static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, char *path)
+{
+ struct direntry *filelist, *fl;
+ uint nbr, i;
+ disk_cache->size_total = 0;
+
+ i = nbr = BLI_filelist_dir_contents(path, &filelist);
+ fl = filelist;
+ while (i--) {
+ /* Don't follow links. */
+ const eFileAttributes file_attrs = BLI_file_attributes(fl->path);
+ if (file_attrs & FILE_ATTR_ANY_LINK) {
+ fl++;
+ continue;
+ }
+
+ char file[FILE_MAX];
+ BLI_split_dirfile(fl->path, NULL, file, 0, sizeof(file));
+
+ bool is_dir = BLI_is_dir(fl->path);
+ if (is_dir && !FILENAME_IS_CURRPAR(file)) {
+ char subpath[FILE_MAX];
+ BLI_strncpy(subpath, fl->path, sizeof(subpath));
+ BLI_add_slash(subpath);
+ seq_disk_cache_get_files(disk_cache, subpath);
+ }
+
+ if (!is_dir) {
+ const char *ext = BLI_path_extension(fl->path);
+ if (ext && ext[1] == 'd' && ext[2] == 'c' && ext[3] == 'f') {
+ DiskCacheFile *cache_file = seq_disk_cache_add_file_to_list(disk_cache, fl->path);
+ cache_file->fstat = fl->s;
+ disk_cache->size_total += cache_file->fstat.st_size;
+ }
+ }
+ fl++;
+ }
+ BLI_filelist_free(filelist, nbr);
+}
+
+static DiskCacheFile *seq_disk_cache_get_oldest_file(SeqDiskCache *disk_cache)
+{
+ DiskCacheFile *cache_file = disk_cache->files.first;
+ if (!cache_file) {
+ return NULL;
+ }
+
+ DiskCacheFile *oldest_file = cache_file;
+ __time64_t oldest_timestamp = cache_file->fstat.st_mtime;
+
+ for (; cache_file; cache_file = cache_file->next) {
+ if (cache_file->fstat.st_mtime < oldest_timestamp) {
+ oldest_timestamp = cache_file->fstat.st_mtime;
+ oldest_file = cache_file;
+ }
+ }
+
+ return oldest_file;
+}
+
+static void seq_disk_cache_delete_file(SeqDiskCache *disk_cache, DiskCacheFile *file)
+{
+ disk_cache->size_total -= file->fstat.st_size;
+ BLI_delete(file->path, false, false);
+ BLI_remlink(&disk_cache->files, file);
+ MEM_freeN(file);
+}
+
+static bool seq_disk_cache_enforce_limits(SeqDiskCache *disk_cache)
+{
+ BLI_mutex_lock(&disk_cache->read_write_mutex);
+ while (disk_cache->size_total > seq_disk_cache_size_limit()) {
+ DiskCacheFile *oldest_file = seq_disk_cache_get_oldest_file(disk_cache);
+
+ if (!oldest_file) {
+ /* We shouldn't enforce limits with no files, do re-scan. */
+ seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir());
+ continue;
+ }
+
+ if (BLI_exists(oldest_file->path) == 0) {
+ /* File may have been manually deleted during runtime, do re-scan. */
+ BLI_freelistN(&disk_cache->files);
+ seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir());
+ continue;
+ }
+
+ seq_disk_cache_delete_file(disk_cache, oldest_file);
+ }
+ BLI_mutex_unlock(&disk_cache->read_write_mutex);
+
+ return true;
+}
+
+static DiskCacheFile *seq_disk_cache_get_file_entry_by_path(SeqDiskCache *disk_cache, char *path)
+{
+ DiskCacheFile *cache_file = disk_cache->files.first;
+
+ for (; cache_file; cache_file = cache_file->next) {
+ if (BLI_strcasecmp(cache_file->path, path) == 0) {
+ return cache_file;
+ }
+ }
+
+ return NULL;
+}
+
+/* Update file size and timestamp. */
+static void seq_disk_cache_update_file(SeqDiskCache *disk_cache, char *path)
+{
+ DiskCacheFile *cache_file;
+ int64_t size_before;
+ int64_t size_after;
+
+ cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path);
+ size_before = cache_file->fstat.st_size;
+
+ if (BLI_stat(path, &cache_file->fstat) == -1) {
+ BLI_assert(false);
+ memset(&cache_file->fstat, 0, sizeof(BLI_stat_t));
+ }
+
+ size_after = cache_file->fstat.st_size;
+ disk_cache->size_total += size_after - size_before;
+}
+
+/* Path format:
+ * <cache dir>/<project name>/<scene name>-<timestamp>/<seq name>/DCACHE_FNAME_FORMAT
+ */
+
+static void seq_disk_cache_get_project_dir(SeqDiskCache *disk_cache, char *path, size_t path_len)
+{
+ char main_name[FILE_MAX];
+ BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), main_name, sizeof(main_name));
+ BLI_strncpy(path, seq_disk_cache_base_dir(), path_len);
+ BLI_path_append(path, path_len, main_name);
+}
+
+static void seq_disk_cache_get_dir(
+ SeqDiskCache *disk_cache, Scene *scene, Sequence *seq, char *path, size_t path_len)
+{
+ char scene_name[MAX_ID_NAME + 22]; /* + -%PRId64 */
+ char seq_name[SEQ_NAME_MAXSTR];
+ char project_dir[FILE_MAX];
+
+ seq_disk_cache_get_project_dir(disk_cache, project_dir, sizeof(project_dir));
+ sprintf(scene_name, "%s-%" PRId64, scene->id.name, disk_cache->timestamp);
+ BLI_strncpy(seq_name, seq->name, sizeof(seq_name));
+ BLI_filename_make_safe(scene_name);
+ BLI_filename_make_safe(seq_name);
+ BLI_strncpy(path, project_dir, path_len);
+ BLI_path_append(path, path_len, scene_name);
+ BLI_path_append(path, path_len, seq_name);
+}
+
+static void seq_disk_cache_get_file_path(SeqDiskCache *disk_cache,
+ SeqCacheKey *key,
+ char *path,
+ size_t path_len)
+{
+ seq_disk_cache_get_dir(disk_cache, key->context.scene, key->seq, path, path_len);
+ int frameno = (int)key->nfra / DCACHE_IMAGES_PER_FILE;
+ char cache_filename[FILE_MAXFILE];
+ sprintf(cache_filename,
+ DCACHE_FNAME_FORMAT,
+ key->type,
+ key->context.rectx,
+ key->context.recty,
+ key->context.preview_render_size,
+ key->context.view_id,
+ frameno);
+
+ BLI_path_append(path, path_len, cache_filename);
+}
+
+static void seq_disk_cache_create_version_file(char *path)
+{
+ BLI_make_existing_file(path);
+
+ FILE *file = BLI_fopen(path, "w");
+ if (file) {
+ fprintf(file, "%d", DCACHE_CURRENT_VERSION);
+ fclose(file);
+ }
+}
+
+static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache)
+{
+ char path[FILE_MAX];
+ char path_version_file[FILE_MAX];
+ int version = 0;
+
+ seq_disk_cache_get_project_dir(disk_cache, path, sizeof(path));
+ BLI_strncpy(path_version_file, path, sizeof(path_version_file));
+ BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version");
+
+ if (BLI_exists(path)) {
+ FILE *file = BLI_fopen(path_version_file, "r");
+
+ if (file) {
+ fscanf(file, "%d", &version);
+ fclose(file);
+ }
+
+ if (version != DCACHE_CURRENT_VERSION) {
+ BLI_delete(path, false, true);
+ seq_disk_cache_create_version_file(path_version_file);
+ }
+ }
+ else {
+ seq_disk_cache_create_version_file(path_version_file);
+ }
+}
+
+static void seq_disk_cache_delete_invalid_files(SeqDiskCache *disk_cache,
+ Scene *scene,
+ Sequence *seq,
+ int invalidate_types,
+ int range_start,
+ int range_end)
+{
+ DiskCacheFile *next_file, *cache_file = disk_cache->files.first;
+ char cache_dir[FILE_MAX];
+ seq_disk_cache_get_dir(disk_cache, scene, seq, cache_dir, sizeof(cache_dir));
+ BLI_add_slash(cache_dir);
+
+ while (cache_file) {
+ next_file = cache_file->next;
+ if (cache_file->cache_type & invalidate_types) {
+ if (strcmp(cache_dir, cache_file->dir) == 0) {
+ int cfra_start = seq_cache_frame_index_to_cfra(seq, cache_file->start_frame);
+ if (cfra_start > range_start && cfra_start <= range_end) {
+ seq_disk_cache_delete_file(disk_cache, cache_file);
+ }
+ }
+ }
+ cache_file = next_file;
+ }
+}
+
+static void seq_disk_cache_invalidate(Scene *scene,
+ Sequence *seq,
+ Sequence *seq_changed,
+ int invalidate_types)
+{
+ int start;
+ int end;
+ SeqDiskCache *disk_cache = scene->ed->cache->disk_cache;
+
+ BLI_mutex_lock(&disk_cache->read_write_mutex);
+
+ start = seq_changed->startdisp - DCACHE_IMAGES_PER_FILE;
+ end = seq_changed->enddisp;
+
+ seq_disk_cache_delete_invalid_files(disk_cache, scene, seq, invalidate_types, start, end);
+
+ BLI_mutex_unlock(&disk_cache->read_write_mutex);
+}
+
+static size_t deflate_imbuf_to_file(ImBuf *ibuf,
+ FILE *file,
+ int level,
+ DiskCacheHeaderEntry *header_entry)
+{
+ if (ibuf->rect) {
+ return BLI_gzip_mem_to_file_at_pos(
+ ibuf->rect, header_entry->size_raw, file, header_entry->offset, level);
+ }
+ else {
+ return BLI_gzip_mem_to_file_at_pos(
+ ibuf->rect_float, header_entry->size_raw, file, header_entry->offset, level);
+ }
+}
+
+static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry)
+{
+ if (ibuf->rect) {
+ return BLI_ungzip_file_to_mem_at_pos(
+ ibuf->rect, header_entry->size_raw, file, header_entry->offset);
+ }
+ else {
+ return BLI_ungzip_file_to_mem_at_pos(
+ ibuf->rect_float, header_entry->size_raw, file, header_entry->offset);
+ }
+}
+
+static void seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header)
+{
+ fseek(file, 0, 0);
+ fread(header, sizeof(*header), 1, file);
+
+ for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) {
+ if ((ENDIAN_ORDER == B_ENDIAN) && header->entry[i].encoding == 0) {
+ BLI_endian_switch_uint64(&header->entry[i].frameno);
+ BLI_endian_switch_uint64(&header->entry[i].offset);
+ BLI_endian_switch_uint64(&header->entry[i].size_compressed);
+ BLI_endian_switch_uint64(&header->entry[i].size_raw);
+ }
+ }
+}
+
+static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header)
+{
+ fseek(file, 0, 0);
+ return fwrite(header, sizeof(*header), 1, file);
+}
+
+static int seq_disk_cache_add_header_entry(SeqCacheKey *key, ImBuf *ibuf, DiskCacheHeader *header)
+{
+ int i;
+ uint64_t offset = sizeof(*header);
+
+ /* Lookup free entry, get offset for new data. */
+ for (i = 0; i < DCACHE_IMAGES_PER_FILE; i++) {
+ if (header->entry[i].size_compressed == 0) {
+ break;
+ }
+ }
+
+ /* Attempt to write beyond set entry limit.
+ * Reset file header and start writing from beginning.
+ */
+ if (i == DCACHE_IMAGES_PER_FILE) {
+ i = 0;
+ memset(header, 0, sizeof(*header));
+ }
+
+ /* Calculate offset for image data. */
+ if (i > 0) {
+ offset = header->entry[i - 1].offset + header->entry[i - 1].size_compressed;
+ }
+
+ if (ENDIAN_ORDER == B_ENDIAN) {
+ header->entry[i].encoding = 255;
+ }
+ else {
+ header->entry[i].encoding = 0;
+ }
+
+ header->entry[i].offset = offset;
+ header->entry[i].frameno = key->nfra;
+
+ /* Store colorspace name of ibuf. */
+ const char *colorspace_name;
+ if (ibuf->rect) {
+ header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels;
+ colorspace_name = IMB_colormanagement_get_rect_colorspace(ibuf);
+ }
+ else {
+ header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels * 4;
+ colorspace_name = IMB_colormanagement_get_float_colorspace(ibuf);
+ }
+ BLI_strncpy(
+ header->entry[i].colorspace_name, colorspace_name, sizeof(header->entry[i].colorspace_name));
+
+ return i;
+}
+
+static int seq_disk_cache_get_header_entry(SeqCacheKey *key, DiskCacheHeader *header)
+{
+ for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) {
+ if (header->entry[i].frameno == key->nfra) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf *ibuf)
+{
+ char path[FILE_MAX];
+
+ seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path));
+ BLI_make_existing_file(path);
+
+ FILE *file = BLI_fopen(path, "rb+");
+ if (!file) {
+ file = BLI_fopen(path, "wb+");
+ if (!file) {
+ return false;
+ }
+ seq_disk_cache_add_file_to_list(disk_cache, path);
+ }
+
+ DiskCacheHeader header;
+ memset(&header, 0, sizeof(header));
+ seq_disk_cache_read_header(file, &header);
+ int entry_index = seq_disk_cache_add_header_entry(key, ibuf, &header);
+ size_t bytes_written = deflate_imbuf_to_file(
+ ibuf, file, seq_disk_cache_compression_level(), &header.entry[entry_index]);
+
+ if (bytes_written != 0) {
+ /* Last step is writing header, as image data can be overwritten,
+ * but missing data would cause problems.
+ */
+ header.entry[entry_index].size_compressed = bytes_written;
+ seq_disk_cache_write_header(file, &header);
+ seq_disk_cache_update_file(disk_cache, path);
+ fclose(file);
+
+ return true;
+ }
+
+ return false;
+}
+
+static ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key)
+{
+ char path[FILE_MAX];
+ DiskCacheHeader header;
+
+ seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path));
+ BLI_make_existing_file(path);
+
+ FILE *file = BLI_fopen(path, "rb");
+ if (!file) {
+ return NULL;
+ }
+
+ seq_disk_cache_read_header(file, &header);
+ int entry_index = seq_disk_cache_get_header_entry(key, &header);
+
+ /* Item not found. */
+ if (entry_index < 0) {
+ fclose(file);
+ return NULL;
+ }
+
+ ImBuf *ibuf;
+ uint64_t size_char = (uint64_t)key->context.rectx * key->context.recty * 4;
+ uint64_t size_float = (uint64_t)key->context.rectx * key->context.recty * 16;
+ size_t expected_size;
+
+ if (header.entry[entry_index].size_raw == size_char) {
+ expected_size = size_char;
+ ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rect);
+ IMB_colormanagement_assign_rect_colorspace(ibuf, header.entry[entry_index].colorspace_name);
+ }
+ else if (header.entry[entry_index].size_raw == size_float) {
+ expected_size = size_float;
+ ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rectfloat);
+ IMB_colormanagement_assign_float_colorspace(ibuf, header.entry[entry_index].colorspace_name);
+ }
+ else {
+ fclose(file);
+ return NULL;
+ }
+
+ size_t bytes_read = inflate_file_to_imbuf(ibuf, file, &header.entry[entry_index]);
+
+ /* Sanity check. */
+ if (bytes_read != expected_size) {
+ fclose(file);
+ IMB_freeImBuf(ibuf);
+ return NULL;
+ }
+ BLI_file_touch(path);
+ seq_disk_cache_update_file(disk_cache, path);
+ fclose(file);
+
+ return ibuf;
+}
+
+#undef DCACHE_FNAME_FORMAT
+#undef DCACHE_IMAGES_PER_FILE
+#undef COLORSPACE_NAME_MAX
+#undef DCACHE_CURRENT_VERSION
static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b)
{
@@ -140,6 +733,16 @@ static bool seq_cache_hashcmp(const void *a_, const void *b_)
seq_cmp_render_data(&a->context, &b->context));
}
+static float seq_cache_cfra_to_frame_index(Sequence *seq, float cfra)
+{
+ return cfra - seq->start;
+}
+
+static float seq_cache_frame_index_to_cfra(Sequence *seq, float nfra)
+{
+ return nfra + seq->start;
+}
+
static SeqCache *seq_cache_get_from_scene(Scene *scene)
{
if (scene && scene->ed && scene->ed->cache) {
@@ -205,7 +808,7 @@ static void seq_cache_put(SeqCache *cache, SeqCacheKey *key, ImBuf *ibuf)
}
}
-static ImBuf *seq_cache_get(SeqCache *cache, void *key)
+static ImBuf *seq_cache_get(SeqCache *cache, SeqCacheKey *key)
{
SeqCacheItem *item = BLI_ghash_lookup(cache->hash, key);
@@ -250,14 +853,14 @@ static SeqCacheKey *seq_cache_choose_key(Scene *scene, SeqCacheKey *lkey, SeqCac
BKE_sequencer_prefetch_get_time_range(scene, &pfjob_start, &pfjob_end);
if (lkey) {
- int lkey_cfra = lkey->seq->start + lkey->nfra;
+ int lkey_cfra = seq_cache_frame_index_to_cfra(lkey->seq, lkey->nfra);
if (lkey_cfra < pfjob_start || lkey_cfra > pfjob_end) {
return lkey;
}
}
if (rkey) {
- int rkey_cfra = rkey->seq->start + rkey->nfra;
+ int rkey_cfra = seq_cache_frame_index_to_cfra(rkey->seq, rkey->nfra);
if (rkey_cfra < pfjob_start || rkey_cfra > pfjob_end) {
return rkey;
}
@@ -267,8 +870,8 @@ static SeqCacheKey *seq_cache_choose_key(Scene *scene, SeqCacheKey *lkey, SeqCac
}
if (rkey && lkey) {
- int lkey_cfra = lkey->seq->start + lkey->nfra;
- int rkey_cfra = rkey->seq->start + rkey->nfra;
+ int lkey_cfra = seq_cache_frame_index_to_cfra(lkey->seq, lkey->nfra);
+ int rkey_cfra = seq_cache_frame_index_to_cfra(rkey->seq, rkey->nfra);
if (lkey_cfra > rkey_cfra) {
SeqCacheKey *swapkey = lkey;
@@ -324,9 +927,9 @@ static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene)
{
SeqCache *cache = seq_cache_get_from_scene(scene);
SeqCacheKey *finalkey = NULL;
- /*leftmost key*/
+ /* Leftmost key. */
SeqCacheKey *lkey = NULL;
- /*rightmost key*/
+ /* Rightmost key. */
SeqCacheKey *rkey = NULL;
SeqCacheKey *key = NULL;
@@ -340,10 +943,10 @@ static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene)
SeqCacheItem *item = BLI_ghashIterator_getValue(&gh_iter);
BLI_ghashIterator_step(&gh_iter);
- /* this shouldn't happen, but better be safe than sorry */
+ /* This shouldn't happen, but better be safe than sorry. */
if (!item->ibuf) {
seq_cache_recycle_linked(scene, key);
- /* can not continue iterating after linked remove */
+ /* Can not continue iterating after linked remove. */
BLI_ghashIterator_init(&gh_iter, cache->hash);
continue;
}
@@ -357,7 +960,8 @@ static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene)
if (key->cost <= scene->ed->recycle_max_cost) {
cheap_count++;
if (lkey) {
- if (key->seq->start + key->nfra < lkey->seq->start + lkey->nfra) {
+ if (seq_cache_frame_index_to_cfra(key->seq, key->nfra) <
+ seq_cache_frame_index_to_cfra(lkey->seq, lkey->nfra)) {
lkey = key;
}
}
@@ -365,7 +969,8 @@ static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene)
lkey = key;
}
if (rkey) {
- if (key->seq->start + key->nfra > rkey->seq->start + rkey->nfra) {
+ if (seq_cache_frame_index_to_cfra(key->seq, key->nfra) >
+ seq_cache_frame_index_to_cfra(rkey->seq, rkey->nfra)) {
rkey = key;
}
}
@@ -380,8 +985,8 @@ static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene)
return finalkey;
}
-/* Find only "base" keys
- * Sources(other types) for a frame must be freed all at once
+/* Find only "base" keys.
+ * Sources(other types) for a frame must be freed all at once.
*/
bool BKE_sequencer_cache_recycle_item(Scene *scene)
{
@@ -432,7 +1037,29 @@ static void seq_cache_set_temp_cache_linked(Scene *scene, SeqCacheKey *base)
}
}
-static void BKE_sequencer_cache_create(Scene *scene)
+static void seq_disk_cache_create(Main *bmain, Scene *scene)
+{
+ BLI_mutex_lock(&cache_create_lock);
+ SeqCache *cache = seq_cache_get_from_scene(scene);
+
+ if (cache == NULL) {
+ return;
+ }
+
+ if (cache->disk_cache != NULL) {
+ return;
+ }
+
+ cache->disk_cache = MEM_callocN(sizeof(SeqDiskCache), "SeqDiskCache");
+ cache->disk_cache->bmain = bmain;
+ BLI_mutex_init(&cache->disk_cache->read_write_mutex);
+ seq_disk_cache_handle_versioning(cache->disk_cache);
+ seq_disk_cache_get_files(cache->disk_cache, seq_disk_cache_base_dir());
+ cache->disk_cache->timestamp = scene->ed->disk_cache_timestamp;
+ BLI_mutex_unlock(&cache_create_lock);
+}
+
+static void seq_cache_create(Main *bmain, Scene *scene)
{
BLI_mutex_lock(&cache_create_lock);
if (scene->ed->cache == NULL) {
@@ -441,8 +1068,13 @@ static void BKE_sequencer_cache_create(Scene *scene)
cache->items_pool = BLI_mempool_create(sizeof(SeqCacheItem), 0, 64, BLI_MEMPOOL_NOP);
cache->hash = BLI_ghash_new(seq_cache_hashhash, seq_cache_hashcmp, "SeqCache hash");
cache->last_key = NULL;
+ cache->bmain = bmain;
BLI_mutex_init(&cache->iterator_mutex);
scene->ed->cache = cache;
+
+ if (scene->ed->disk_cache_timestamp == 0) {
+ scene->ed->disk_cache_timestamp = time(NULL);
+ }
}
BLI_mutex_unlock(&cache_create_lock);
}
@@ -464,7 +1096,8 @@ void BKE_sequencer_cache_free_temp_cache(Scene *scene, short id, int cfra)
SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter);
BLI_ghashIterator_step(&gh_iter);
- if (key->is_temp_cache && key->task_id == id && key->seq->start + key->nfra != cfra) {
+ if (key->is_temp_cache && key->task_id == id &&
+ seq_cache_frame_index_to_cfra(key->seq, key->nfra) != cfra) {
BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree);
}
}
@@ -482,6 +1115,13 @@ void BKE_sequencer_cache_destruct(Scene *scene)
BLI_mempool_destroy(cache->keys_pool);
BLI_mempool_destroy(cache->items_pool);
BLI_mutex_end(&cache->iterator_mutex);
+
+ if (cache->disk_cache != NULL) {
+ BLI_freelistN(&cache->disk_cache->files);
+ BLI_mutex_end(&cache->disk_cache->read_write_mutex);
+ MEM_freeN(cache->disk_cache);
+ }
+
MEM_freeN(cache);
scene->ed->cache = NULL;
}
@@ -525,6 +1165,10 @@ void BKE_sequencer_cache_cleanup_sequence(Scene *scene,
return;
}
+ if (seq_disk_cache_is_enabled(cache->bmain) && cache->disk_cache != NULL) {
+ seq_disk_cache_invalidate(scene, seq, seq_changed, invalidate_types);
+ }
+
seq_cache_lock(scene);
int range_start = seq_changed->startdisp;
@@ -548,9 +1192,9 @@ void BKE_sequencer_cache_cleanup_sequence(Scene *scene,
SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter);
BLI_ghashIterator_step(&gh_iter);
- int key_cfra = key->seq->start + key->nfra;
+ int key_cfra = seq_cache_frame_index_to_cfra(key->seq, key->nfra);
- /* clean all final and composite in intersection of seq and seq_changed */
+ /* Clean all final and composite in intersection of seq and seq_changed. */
if (key->type & invalidate_composite && key_cfra >= range_start && key_cfra <= range_end) {
if (key->link_next || key->link_prev) {
seq_cache_relink_keys(key->link_next, key->link_prev);
@@ -572,10 +1216,8 @@ void BKE_sequencer_cache_cleanup_sequence(Scene *scene,
seq_cache_unlock(scene);
}
-struct ImBuf *BKE_sequencer_cache_get(const SeqRenderData *context,
- Sequence *seq,
- float cfra,
- int type)
+struct ImBuf *BKE_sequencer_cache_get(
+ const SeqRenderData *context, Sequence *seq, float cfra, int type, bool skip_disk_cache)
{
Scene *scene = context->scene;
@@ -586,31 +1228,58 @@ struct ImBuf *BKE_sequencer_cache_get(const SeqRenderData *context,
}
if (!scene->ed->cache) {
- BKE_sequencer_cache_create(scene);
- return NULL;
+ seq_cache_create(context->bmain, scene);
}
seq_cache_lock(scene);
SeqCache *cache = seq_cache_get_from_scene(scene);
ImBuf *ibuf = NULL;
+ SeqCacheKey key;
+ /* Try RAM cache: */
if (cache && seq) {
- SeqCacheKey key;
-
key.seq = seq;
key.context = *context;
- key.nfra = cfra - seq->start;
+ key.nfra = seq_cache_cfra_to_frame_index(seq, cfra);
key.type = type;
ibuf = seq_cache_get(cache, &key);
}
seq_cache_unlock(scene);
+ if (ibuf) {
+ return ibuf;
+ }
+
+ /* Try disk cache: */
+ if (!skip_disk_cache && seq_disk_cache_is_enabled(context->bmain)) {
+ if (cache->disk_cache == NULL) {
+ seq_disk_cache_create(context->bmain, context->scene);
+ }
+
+ BLI_mutex_lock(&cache->disk_cache->read_write_mutex);
+ ibuf = seq_disk_cache_read_file(cache->disk_cache, &key);
+ BLI_mutex_unlock(&cache->disk_cache->read_write_mutex);
+ if (ibuf) {
+ if (key.type == SEQ_CACHE_STORE_FINAL_OUT) {
+ BKE_sequencer_cache_put_if_possible(context, seq, cfra, type, ibuf, 0.0f, true);
+ }
+ else {
+ BKE_sequencer_cache_put(context, seq, cfra, type, ibuf, 0.0f, true);
+ }
+ }
+ }
+
return ibuf;
}
-bool BKE_sequencer_cache_put_if_possible(
- const SeqRenderData *context, Sequence *seq, float cfra, int type, ImBuf *ibuf, float cost)
+bool BKE_sequencer_cache_put_if_possible(const SeqRenderData *context,
+ Sequence *seq,
+ float cfra,
+ int type,
+ ImBuf *ibuf,
+ float cost,
+ bool skip_disk_cache)
{
Scene *scene = context->scene;
@@ -621,7 +1290,7 @@ bool BKE_sequencer_cache_put_if_possible(
}
if (BKE_sequencer_cache_recycle_item(scene)) {
- BKE_sequencer_cache_put(context, seq, cfra, type, ibuf, cost);
+ BKE_sequencer_cache_put(context, seq, cfra, type, ibuf, cost, skip_disk_cache);
return true;
}
else {
@@ -631,8 +1300,13 @@ bool BKE_sequencer_cache_put_if_possible(
}
}
-void BKE_sequencer_cache_put(
- const SeqRenderData *context, Sequence *seq, float cfra, int type, ImBuf *i, float cost)
+void BKE_sequencer_cache_put(const SeqRenderData *context,
+ Sequence *seq,
+ float cfra,
+ int type,
+ ImBuf *i,
+ float cost,
+ bool skip_disk_cache)
{
Scene *scene = context->scene;
@@ -646,15 +1320,15 @@ void BKE_sequencer_cache_put(
return;
}
- /* Prevent reinserting, it breaks cache key linking */
- ImBuf *test = BKE_sequencer_cache_get(context, seq, cfra, type);
+ /* Prevent reinserting, it breaks cache key linking. */
+ ImBuf *test = BKE_sequencer_cache_get(context, seq, cfra, type, true);
if (test) {
IMB_freeImBuf(test);
return;
}
if (!scene->ed->cache) {
- BKE_sequencer_cache_create(scene);
+ seq_cache_create(context->bmain, scene);
}
seq_cache_lock(scene);
@@ -664,6 +1338,9 @@ void BKE_sequencer_cache_put(
if (seq->cache_flag & SEQ_CACHE_OVERRIDE) {
flag = seq->cache_flag;
+ /* Final_out is invalid in context of sequence override. */
+ flag -= seq->cache_flag & SEQ_CACHE_STORE_FINAL_OUT;
+ /* If global setting is enabled however, use it. */
flag |= scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT;
}
else {
@@ -679,7 +1356,7 @@ void BKE_sequencer_cache_put(
key->cache_owner = cache;
key->seq = seq;
key->context = *context;
- key->nfra = cfra - seq->start;
+ key->nfra = seq_cache_cfra_to_frame_index(seq, cfra);
key->type = type;
key->cost = cost;
key->link_prev = NULL;
@@ -696,24 +1373,37 @@ void BKE_sequencer_cache_put(
SeqCacheKey *temp_last_key = cache->last_key;
seq_cache_put(cache, key, i);
- /* Restore pointer to previous item as this one will be freed when stack is rendered */
+ /* Restore pointer to previous item as this one will be freed when stack is rendered. */
if (key->is_temp_cache) {
cache->last_key = temp_last_key;
}
- /* Set last_key's reference to this key so we can look up chain backwards
- * Item is already put in cache, so cache->last_key points to current key;
+ /* Set last_key's reference to this key so we can look up chain backwards.
+ * Item is already put in cache, so cache->last_key points to current key.
*/
if (flag & type && temp_last_key) {
temp_last_key->link_next = cache->last_key;
}
- /* Reset linking */
+ /* Reset linking. */
if (key->type == SEQ_CACHE_STORE_FINAL_OUT) {
cache->last_key = NULL;
}
seq_cache_unlock(scene);
+
+ if (!key->is_temp_cache && !skip_disk_cache) {
+ if (seq_disk_cache_is_enabled(context->bmain)) {
+ if (cache->disk_cache == NULL) {
+ seq_disk_cache_create(context->bmain, context->scene);
+ }
+
+ BLI_mutex_lock(&cache->disk_cache->read_write_mutex);
+ seq_disk_cache_write_file(cache->disk_cache, key, i);
+ BLI_mutex_unlock(&cache->disk_cache->read_write_mutex);
+ seq_disk_cache_enforce_limits(cache->disk_cache);
+ }
+ }
}
size_t BKE_sequencer_cache_get_num_items(struct Scene *scene)
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 827cccc6bd6..a11326ef099 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -3081,7 +3081,7 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context,
if (i != context->view_id) {
BKE_sequencer_cache_put(
- &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibufs_arr[i], 0);
+ &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibufs_arr[i], 0, false);
}
}
}
@@ -3201,7 +3201,7 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
}
if (i != context->view_id) {
BKE_sequencer_cache_put(
- &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf_arr[i], 0);
+ &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf_arr[i], 0, false);
}
}
@@ -3600,7 +3600,8 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
}
if (i != context->view_id) {
- BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_CACHE_STORE_RAW, ibufs_arr[i], 0);
+ BKE_sequencer_cache_put(
+ &localcontext, seq, cfra, SEQ_CACHE_STORE_RAW, ibufs_arr[i], 0, false);
}
RE_ReleaseResultImage(re);
@@ -3813,10 +3814,10 @@ static ImBuf *seq_render_strip(const SeqRenderData *context,
clock_t begin = seq_estimate_render_cost_begin();
- ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED);
+ ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, false);
if (ibuf == NULL) {
- ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_RAW);
+ ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_RAW, false);
if (ibuf == NULL) {
/* MOVIECLIPs have their own proxy management */
if (seq->type != SEQ_TYPE_MOVIECLIP) {
@@ -3852,7 +3853,7 @@ static ImBuf *seq_render_strip(const SeqRenderData *context,
if (use_preprocess) {
float cost = seq_estimate_render_cost_end(context->scene, begin);
- BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost);
+ BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false);
/* reset timer so we can get partial render time */
begin = seq_estimate_render_cost_begin();
@@ -3860,7 +3861,7 @@ static ImBuf *seq_render_strip(const SeqRenderData *context,
}
float cost = seq_estimate_render_cost_end(context->scene, begin);
- BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf, cost);
+ BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf, cost, false);
}
return ibuf;
}
@@ -3954,7 +3955,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
int early_out;
Sequence *seq = seq_arr[i];
- out = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_COMPOSITE);
+ out = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_COMPOSITE, false);
if (out) {
break;
@@ -3986,7 +3987,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
out = seq_render_strip_stack_apply_effect(context, seq, cfra, ibuf1, ibuf2);
float cost = seq_estimate_render_cost_end(context->scene, begin);
- BKE_sequencer_cache_put(context, seq_arr[i], cfra, SEQ_CACHE_STORE_COMPOSITE, out, cost);
+ BKE_sequencer_cache_put(
+ context, seq_arr[i], cfra, SEQ_CACHE_STORE_COMPOSITE, out, cost, false);
IMB_freeImBuf(ibuf1);
IMB_freeImBuf(ibuf2);
@@ -4014,7 +4016,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
}
float cost = seq_estimate_render_cost_end(context->scene, begin);
- BKE_sequencer_cache_put(context, seq_arr[i], cfra, SEQ_CACHE_STORE_COMPOSITE, out, cost);
+ BKE_sequencer_cache_put(
+ context, seq_arr[i], cfra, SEQ_CACHE_STORE_COMPOSITE, out, cost, false);
}
return out;
@@ -4053,7 +4056,8 @@ ImBuf *BKE_sequencer_give_ibuf(const SeqRenderData *context, float cfra, int cha
count = get_shown_sequences(seqbasep, cfra, chanshown, seq_arr);
if (count) {
- out = BKE_sequencer_cache_get(context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT);
+ out = BKE_sequencer_cache_get(
+ context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT, false);
}
BKE_sequencer_cache_free_temp_cache(context->scene, context->task_id, cfra);
@@ -4068,11 +4072,11 @@ ImBuf *BKE_sequencer_give_ibuf(const SeqRenderData *context, float cfra, int cha
if (context->is_prefetch_render) {
BKE_sequencer_cache_put(
- context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT, out, cost);
+ context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT, out, cost, false);
}
else {
BKE_sequencer_cache_put_if_possible(
- context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT, out, cost);
+ context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT, out, cost, false);
}
BLI_mutex_unlock(&seq_render_mutex);
}
@@ -5294,7 +5298,7 @@ Sequence *BKE_sequence_alloc(ListBase *lb, int cfra, int machine, int type)
seq->strip = seq_strip_alloc(type);
seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format");
- seq->cache_flag = SEQ_CACHE_ALL_TYPES;
+ seq->cache_flag = SEQ_CACHE_STORE_RAW | SEQ_CACHE_STORE_PREPROCESSED | SEQ_CACHE_STORE_COMPOSITE;
return seq;
}
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index 8596a60bc6a..89f7d01ffd6 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -153,7 +153,13 @@ int BLI_file_gzip(const char *from, const char *to) ATTR_WARN_UNUSED_RESULT ATTR
#endif
char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
-
+size_t BLI_gzip_mem_to_file_at_pos(void *buf,
+ size_t len,
+ FILE *file,
+ size_t gz_stream_offset,
+ int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset)
+ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT;
size_t BLI_file_size(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index 3a45989fb63..afb46dd8d82 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -158,6 +158,101 @@ char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size)
return mem;
}
+#define CHUNK 256 * 1024
+
+/* gzip byte array from memory and write it to file at certain position.
+ * return size of gzip stream.
+ */
+size_t BLI_gzip_mem_to_file_at_pos(
+ void *buf, size_t len, FILE *file, size_t gz_stream_offset, int compression_level)
+{
+ int ret, flush;
+ unsigned have;
+ z_stream strm;
+ unsigned char out[CHUNK];
+
+ fseek(file, gz_stream_offset, 0);
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ ret = deflateInit(&strm, compression_level);
+ if (ret != Z_OK)
+ return 0;
+
+ strm.avail_in = len;
+ strm.next_in = (Bytef *)buf;
+ flush = Z_FINISH;
+
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = deflate(&strm, flush);
+ if (ret == Z_STREAM_ERROR) {
+ return 0;
+ }
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, file) != have || ferror(file)) {
+ deflateEnd(&strm);
+ return 0;
+ }
+ } while (strm.avail_out == 0);
+
+ if (strm.avail_in != 0 || ret != Z_STREAM_END) {
+ return 0;
+ }
+
+ deflateEnd(&strm);
+ return (size_t)strm.total_out;
+}
+
+/* read and decompress gzip stream from file at certain position to buffer.
+ * return size of decompressed data.
+ */
+size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset)
+{
+ int ret;
+ z_stream strm;
+ size_t chunk = 256 * 1024;
+ unsigned char in[CHUNK];
+
+ fseek(file, gz_stream_offset, 0);
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+ ret = inflateInit(&strm);
+ if (ret != Z_OK)
+ return 0;
+
+ do {
+ strm.avail_in = fread(in, 1, chunk, file);
+ strm.next_in = in;
+ if (ferror(file)) {
+ inflateEnd(&strm);
+ return 0;
+ }
+
+ do {
+ strm.avail_out = len;
+ strm.next_out = (Bytef *)buf + strm.total_out;
+
+ ret = inflate(&strm, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR) {
+ return 0;
+ }
+ } while (strm.avail_out == 0);
+
+ } while (ret != Z_STREAM_END);
+
+ inflateEnd(&strm);
+ return (size_t)strm.total_out;
+}
+
+#undef CHUNK
+
/**
* Returns true if the file with the specified name can be written.
* This implementation uses access(2), which makes the check according
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index fb941a61ae8..c275c8187e4 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -276,6 +276,9 @@ typedef struct Editing {
int cache_flag;
struct PrefetchJob *prefetch_job;
+
+ /* Must be initialized only by BKE_sequencer_cache_create() */
+ int64_t disk_cache_timestamp;
} Editing;
/* ************* Effect Variable Structs ********* */
@@ -682,6 +685,7 @@ enum {
SEQ_CACHE_VIEW_FINAL_OUT = (1 << 9),
SEQ_CACHE_PREFETCH_ENABLE = (1 << 10),
+ SEQ_CACHE_DISK_CACHE_ENABLE = (1 << 11),
};
#ifdef __cplusplus
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 4a3c5695827..d5ebe4f8778 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -862,7 +862,13 @@ typedef struct UserDef {
char render_display_type; /* eUserpref_RenderDisplayType */
char filebrowser_display_type; /* eUserpref_TempSpaceDisplayType */
- char _pad5[4];
+
+ char sequencer_disk_cache_dir[1024];
+ int sequencer_disk_cache_compression; /* eUserpref_DiskCacheCompression */
+ int sequencer_disk_cache_size_limit;
+ short sequencer_disk_cache_flag;
+
+ char _pad5[2];
struct WalkNavigation walk_navigation;
@@ -1286,6 +1292,12 @@ typedef enum eUserpref_EmulateMMBMod {
USER_EMU_MMB_MOD_OSKEY = 1,
} eUserpref_EmulateMMBMod;
+typedef enum eUserpref_DiskCacheCompression {
+ USER_SEQ_DISK_CACHE_COMPRESSION_NONE = 0,
+ USER_SEQ_DISK_CACHE_COMPRESSION_LOW = 1,
+ USER_SEQ_DISK_CACHE_COMPRESSION_HIGH = 2,
+} eUserpref_DiskCacheCompression;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 661f1007395..d4dd3365935 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -1951,7 +1951,7 @@ static void rna_def_editor(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0.0f, SEQ_CACHE_COST_MAX, 0.1f, 1);
RNA_def_property_float_sdna(prop, NULL, "recycle_max_cost");
RNA_def_property_ui_text(
- prop, "Recycle Up to Cost", "Only frames with cost lower than this value will be recycled");
+ prop, "Recycle Up To Cost", "Only frames with cost lower than this value will be recycled");
}
static void rna_def_filter_video(StructRNA *srna)
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 1557cd3d4b1..cada2cb13a9 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -192,6 +192,8 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
# include "BLF_api.h"
+# include "BLI_path_util.h"
+
# include "MEM_guardedalloc.h"
# include "MEM_CacheLimiterC-Api.h"
@@ -536,6 +538,19 @@ static void rna_Userdef_memcache_update(Main *UNUSED(bmain),
USERDEF_TAG_DIRTY;
}
+static void rna_Userdef_disk_cache_dir_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *UNUSED(ptr))
+{
+ if (U.sequencer_disk_cache_dir[0] != '\0') {
+ BLI_path_abs(U.sequencer_disk_cache_dir, BKE_main_blendfile_path_from_global());
+ BLI_add_slash(U.sequencer_disk_cache_dir);
+ BLI_path_make_safe(U.sequencer_disk_cache_dir);
+ }
+
+ USERDEF_TAG_DIRTY;
+}
+
static void rna_UserDef_weight_color_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Object *ob;
@@ -5126,6 +5141,25 @@ static void rna_def_userdef_system(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem seq_disk_cache_compression_levels[] = {
+ {USER_SEQ_DISK_CACHE_COMPRESSION_NONE,
+ "NONE",
+ 0,
+ "None",
+ "Requires fast storage, but uses minimum CPU resources"},
+ {USER_SEQ_DISK_CACHE_COMPRESSION_LOW,
+ "LOW",
+ 0,
+ "Low",
+ "Doesn't require fast storage and uses less CPU resources"},
+ {USER_SEQ_DISK_CACHE_COMPRESSION_HIGH,
+ "HIGH",
+ 0,
+ "High",
+ "Works on slower storage devices and uses most CPU resources"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "PreferencesSystem", NULL);
RNA_def_struct_sdna(srna, "UserDef");
RNA_def_struct_nested(brna, srna, "Preferences");
@@ -5168,6 +5202,31 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Memory Cache Limit", "Memory cache limit (in megabytes)");
RNA_def_property_update(prop, 0, "rna_Userdef_memcache_update");
+ /* Sequencer disk cache */
+
+ prop = RNA_def_property(srna, "use_sequencer_disk_cache", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(
+ prop, NULL, "sequencer_disk_cache_flag", SEQ_CACHE_DISK_CACHE_ENABLE);
+ RNA_def_property_ui_text(prop, "Use Disk Cache", "Store cached images to disk");
+
+ prop = RNA_def_property(srna, "sequencer_disk_cache_dir", PROP_STRING, PROP_DIRPATH);
+ RNA_def_property_string_sdna(prop, NULL, "sequencer_disk_cache_dir");
+ RNA_def_property_update(prop, 0, "rna_Userdef_disk_cache_dir_update");
+ RNA_def_property_ui_text(prop, "Disk Cache Directory", "Override default directory");
+
+ prop = RNA_def_property(srna, "sequencer_disk_cache_size_limit", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "sequencer_disk_cache_size_limit");
+ RNA_def_property_range(prop, 0, INT_MAX);
+ RNA_def_property_ui_text(prop, "Disk Cache Limit", "Disk cache limit (in gigabytes)");
+
+ prop = RNA_def_property(srna, "sequencer_disk_cache_compression", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, seq_disk_cache_compression_levels);
+ RNA_def_property_enum_sdna(prop, NULL, "sequencer_disk_cache_compression");
+ RNA_def_property_ui_text(
+ prop,
+ "Disk Cache Compression Level",
+ "Smaller compression will result in larger files, but less decoding overhead");
+
prop = RNA_def_property(srna, "scrollback", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "scrollback");
RNA_def_property_range(prop, 32, 32768);