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:
authorRichard Antalik <richardantalik@gmail.com>2020-03-19 02:05:18 +0300
committerRichard Antalik <richardantalik@gmail.com>2020-03-19 02:07:30 +0300
commit348d2fa09e0c01d62372f5999b62d06ac3b810f9 (patch)
tree36186498cb951601478819b8aceb01fbb7c9b879 /source/blender
parentc8b4b4c0fa3b1255a79b90393ee9f5ddb2ec35e9 (diff)
VSE: Disk cache
This patch implements dumping images from cache to HDD. The main goal of this system is to provide a means to achieve consistent playback speed mainly for strips that are not possible to preview in real time. How to use: Disk cache has own settings in user preferences for path to storage, size limit and compression level. To use disk cache, you need to check `Use Disk Cache` box, set `Disk Cache Directory`, `Disk Cache Limit` and save or open existing .blend file. By default sequencer output will be cached only. Manual setting is possible in cache panel. Uses: - Replacement or alternative for proxies. Disk cache will work with any strip type, supports float images as well. - Storage for strip thumbnails. - Less RAM needs to be allocated for preview cache How it works: Disk cache is extension of RAM cache. Every image, that is stored or deleted in RAM will be stored or deleted on HDD as well. Images can be compressed to save space and for use on slower drives. Compressed images are slower to write and read though. Images are stored in bulk of 100 rendered frames per one file. This is to overcome slow file access time for large amount of files. Drawback is, that if one frame needs to be redrawn, all 100 frames are deleted. Reviewed By: sergey Differential Revision: https://developer.blender.org/D5524
Diffstat (limited to 'source/blender')
-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
9 files changed, 934 insertions, 61 deletions
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);