From 9e8cf592717a267af4186ccc39ab76488c0aa1a2 Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Sun, 11 Sep 2022 13:05:41 +0200
Subject: [PATCH] shaders/icc: add 3DLUT caching API

It's a bit clumsy to mix this into pl_icc_params, but doing it here
quite elegantly avoids the need to have to plumb this through the
pl_renderer somehow, because the `pl_icc_params` are already exposed to
the user via `pl_render_params`.

Closes: https://code.videolan.org/videolan/libplacebo/-/issues/205
Closes: https://github.com/haasn/libplacebo/issues/139
---
 meson.build                          |  1 +
 src/include/libplacebo/shaders/icc.h | 22 ++++++++++++++++++++++
 src/shaders/icc.c                    | 22 +++++++++++++++++++++-
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/meson.build b/meson.build
index 7bbf6c963..093db7825 100644
--- a/meson.build
+++ b/meson.build
@@ -12,6 +12,7 @@ project('libplacebo', ['c', 'cpp'],
     5,
     # API version
     {
+      '222': 'add `pl_icc_params.cache_save/load`',
       '221': 'add `pl_source_frame.first_field`',
       '220': 'add deinterlacing-related fields to `pl_frame` and `pl_render_params`',
       '219': 'add pl_source_frame.duration, deprecating pl_queue_params.frame_duration',
diff --git a/src/include/libplacebo/shaders/icc.h b/src/include/libplacebo/shaders/icc.h
index 95714e825..3eab83c2c 100644
--- a/src/include/libplacebo/shaders/icc.h
+++ b/src/include/libplacebo/shaders/icc.h
@@ -48,6 +48,28 @@ struct pl_icc_params {
     // if unavailable.
     float max_luma;
 
+    // 3DLUT caching API. Providing these functions can help speed up ICC LUT
+    // generation by saving/loading profiles to/from disk. Both of these
+    // callbacks are optional.
+    void *cache_priv;
+    //
+    // This is called to inform users of new cache entries. The user may store
+    // this cache to disk or some other internal caching mechanism.
+    void (*cache_save)(void *priv, uint64_t sig, const uint8_t *cache, size_t size);
+    //
+    // This is called to query for existing cache entries. The user should look
+    // up this cache entry and write its contents to `cache`, ensuring that no
+    // more than `size` bytes are written, and return `true` on success.
+    bool (*cache_load)(void *priv, uint64_t sig, uint8_t *cache, size_t size);
+    //
+    // Note: The `signature` of a cache entry is NOT equal to the `signature`
+    // of the underlying `pl_icc_object` - it is split up into separate entries
+    // for `pl_icc_decode` and `pl_icc_encode`, and also includes a hashed
+    // representation of the encoded parameters.
+    //
+    // Note: These callbacks will only be called from within `pl_icc_decode` /
+    // `pl_icc_encode`, so `cache_priv` should exceed this lifetime.
+
     // Deprecated / removed options.
     bool use_display_contrast PL_DEPRECATED; // Always on
 };
diff --git a/src/shaders/icc.c b/src/shaders/icc.c
index 888ea020f..5b6b8c188 100644
--- a/src/shaders/icc.c
+++ b/src/shaders/icc.c
@@ -420,6 +420,15 @@ error:
     return NULL;
 }
 
+static inline bool cache_load(pl_icc_object icc, uint64_t sig,
+                              uint8_t *cache, size_t size)
+{
+    if (icc->params.cache_load)
+        return icc->params.cache_load(icc->params.cache_priv, sig, cache, size);
+
+    return false;
+}
+
 static void fill_lut(void *datap, const struct sh_lut_params *params, bool decode)
 {
     pl_icc_object icc = params->priv;
@@ -428,6 +437,13 @@ static void fill_lut(void *datap, const struct sh_lut_params *params, bool decod
     cmsHPROFILE srcp = decode ? p->profile : p->approx;
     cmsHPROFILE dstp = decode ? p->approx  : p->profile;
 
+    int s_r = params->width, s_g = params->height, s_b = params->depth;
+    size_t data_size = s_r * s_g * s_b * sizeof(uint16_t[4]);
+    if (cache_load(icc, params->signature, datap, data_size)) {
+        pl_info(log, "Using cached 3DLUT (0x%"PRIX64")", params->signature);
+        return;
+    }
+
     clock_t start = clock();
     cmsHTRANSFORM tf = cmsCreateTransformTHR(p->cms, srcp, TYPE_RGB_16,
                                              dstp, TYPE_RGBA_16,
@@ -440,7 +456,6 @@ static void fill_lut(void *datap, const struct sh_lut_params *params, bool decod
     clock_t after_transform = clock();
     pl_log_cpu_time(log, start, after_transform, "creating ICC transform");
 
-    int s_r = params->width, s_g = params->height, s_b = params->depth;
     uint16_t *tmp = pl_alloc(NULL, s_r * 3 * sizeof(tmp[0]));
     for (int b = 0; b < s_b; b++) {
         for (int g = 0; g < s_g; g++) {
@@ -460,6 +475,11 @@ static void fill_lut(void *datap, const struct sh_lut_params *params, bool decod
     pl_log_cpu_time(log, after_transform, clock(), "generating ICC 3DLUT");
     cmsDeleteTransform(tf);
     pl_free(tmp);
+
+    if (icc->params.cache_save) {
+        icc->params.cache_save(icc->params.cache_priv, params->signature,
+                               datap, data_size);
+    }
 }
 
 static void fill_decode(void *datap, const struct sh_lut_params *params)
-- 
GitLab