Commit 3207144b authored by Niklas Haas's avatar Niklas Haas
Browse files

renderer: correctly invalidate mixer cache when render params change

Annoying but necessary. Otherwise this just creates unintuitive
behaviour, especially when displaying the same frame over and over again
(which is the default pl_queue semenatics for single frame streams).
parent cbd9f0e6
......@@ -194,12 +194,12 @@ struct pl_render_params {
bool allow_delayed_peak_detect;
// Normally, when the size of the `pl_render_target` used with
// `pl_render_image_mix` changes, the internal cache of mixed frames must
// be discarded in order to re-render all required frames at the new output
// size. Setting this option to `true` will skip the cache invalidation and
// instead re-use the existing frames (with bilinear scaling to the new
// size), which comes at a quality loss shortly after a resize, but should
// make it much more smooth.
// `pl_render_image_mix` changes, or the render parameters are updated, the
// internal cache of mixed frames must be discarded in order to re-render
// all required frames. Setting this option to `true` will skip the cache
// invalidation and instead re-use the existing frames (with bilinear
// scaling to the new size if necessary), which comes at a quality loss
// shortly after a resize, but should make it much more smooth.
bool preserve_mixing_cache;
// --- Performance tuning / debugging options
......
......@@ -143,8 +143,16 @@ static inline pl_str pl_str_getline(pl_str str, pl_str *out_rest)
// ignored. When successful, this allocates a new array to store the output.
bool pl_str_decode_hex(void *alloc, pl_str hex, pl_str *out);
// Return a 64-bit hash of a string's contents
uint64_t pl_str_hash(pl_str str);
// Compute a fast 64-bit hash
uint64_t pl_mem_hash(const void *mem, size_t size);
static inline void pl_hash_merge(uint64_t *accum, uint64_t hash) {
*accum ^= hash + 0x9e3779b9 + (*accum << 6) + (*accum >> 2);
}
static inline uint64_t pl_str_hash(pl_str str)
{
return pl_mem_hash(str.buf, str.len);
}
static inline bool pl_str_equals(pl_str str1, pl_str str2)
{
......
......@@ -23,6 +23,7 @@
struct cached_frame {
uint64_t signature;
uint64_t params_hash; // for detecting `pl_render_params` changes
struct pl_color_space color;
struct pl_icc_profile profile;
const struct pl_tex *tex;
......@@ -2236,6 +2237,64 @@ error:
return false;
}
static uint64_t render_params_hash(const struct pl_render_params *params_orig)
{
struct pl_render_params params = *params_orig;
uint64_t hash = 0;
#define HASH_PTR(ptr) \
do { \
if (ptr) { \
pl_hash_merge(&hash, pl_mem_hash(ptr, sizeof(*ptr))); \
ptr = NULL; \
} \
} while (0)
#define HASH_FILTER(scaler) \
do { \
if (scaler) { \
struct pl_filter_config filter = *scaler; \
HASH_PTR(filter.kernel); \
HASH_PTR(filter.window); \
pl_hash_merge(&hash, pl_mem_hash(&filter, sizeof(filter))); \
scaler = NULL; \
} \
} while (0)
HASH_FILTER(params.upscaler);
HASH_FILTER(params.downscaler);
HASH_FILTER(params.frame_mixer);
HASH_PTR(params.deband_params);
HASH_PTR(params.sigmoid_params);
HASH_PTR(params.color_adjustment);
HASH_PTR(params.peak_detect_params);
HASH_PTR(params.color_map_params);
HASH_PTR(params.dither_params);
HASH_PTR(params.cone_params);
HASH_PTR(params.blend_params);
HASH_PTR(params.hooks);
#ifdef PL_HAVE_LCMS
HASH_PTR(params.icc_params);
HASH_PTR(params.lut3d_params);
#endif
// Hash all hooks
for (int i = 0; i < params.num_hooks; i++)
pl_hash_merge(&hash, pl_mem_hash(&params.hooks[i], sizeof(params.hooks[i])));
params.hooks = NULL;
// Hash the LUT by only looking at the signature
if (params.lut) {
pl_hash_merge(&hash, params.lut->signature);
params.lut = NULL;
}
pl_hash_merge(&hash, pl_mem_hash(&params, sizeof(params)));
return hash;
}
#define MAX_MIX_FRAMES 16
bool pl_render_image_mix(struct pl_renderer *rr, const struct pl_frame_mix *images,
......@@ -2243,6 +2302,7 @@ bool pl_render_image_mix(struct pl_renderer *rr, const struct pl_frame_mix *imag
const struct pl_render_params *params)
{
params = PL_DEF(params, &pl_render_default_params);
uint64_t params_hash = render_params_hash(params);
require(images->num_frames >= 1);
for (int i = 0; i < images->num_frames - 1; i++)
......@@ -2362,11 +2422,12 @@ bool pl_render_image_mix(struct pl_renderer *rr, const struct pl_frame_mix *imag
}
// Check to see if we can blindly reuse this cache entry. This is the
// case either if the size is compatible, or if the user doesn't care.
bool can_reuse = !!f->tex;
if (f->tex && !params->preserve_mixing_cache) {
// case if either the params are compatible, or the user doesn't care
bool can_reuse = f->tex;
if (can_reuse && !params->preserve_mixing_cache) {
can_reuse = f->tex->params.w == out_w &&
f->tex->params.h == out_h;
f->tex->params.h == out_h &&
f->params_hash == params_hash;
}
if (!can_reuse) {
......@@ -2416,6 +2477,8 @@ bool pl_render_image_mix(struct pl_renderer *rr, const struct pl_frame_mix *imag
rr->disable_mixing = true;
goto fallback;
}
f->params_hash = params_hash;
}
pl_assert(fidx < MAX_MIX_FRAMES);
......
......@@ -48,9 +48,9 @@
v2 = ROTL(v2, 32); \
} while (0)
uint64_t pl_str_hash(pl_str str)
uint64_t pl_mem_hash(const void *mem, size_t size)
{
if (!str.len)
if (!size)
return 0x8533321381b8254bULL;
uint64_t v0 = 0x736f6d6570736575ULL;
......@@ -61,16 +61,17 @@ uint64_t pl_str_hash(pl_str str)
uint64_t k1 = 0x68f7f03510e5285cULL;
uint64_t m;
int i;
const uint8_t *end = str.buf + str.len - (str.len % sizeof(uint64_t));
const int left = str.len & 7;
uint64_t b = ((uint64_t) str.len) << 56;
const uint8_t *buf = mem;
const uint8_t *end = buf + size - (size % sizeof(uint64_t));
const int left = size & 7;
uint64_t b = ((uint64_t) size) << 56;
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
for (; str.buf != end; str.buf += 8) {
m = U8TO64_LE(str.buf);
for (; buf != end; buf += 8) {
m = U8TO64_LE(buf);
v3 ^= m;
for (i = 0; i < cROUNDS; ++i)
......@@ -80,13 +81,13 @@ uint64_t pl_str_hash(pl_str str)
}
switch (left) {
case 7: b |= ((uint64_t) str.buf[6]) << 48; // fall through
case 6: b |= ((uint64_t) str.buf[5]) << 40; // fall through
case 5: b |= ((uint64_t) str.buf[4]) << 32; // fall through
case 4: b |= ((uint64_t) str.buf[3]) << 24; // fall through
case 3: b |= ((uint64_t) str.buf[2]) << 16; // fall through
case 2: b |= ((uint64_t) str.buf[1]) << 8; // fall through
case 1: b |= ((uint64_t) str.buf[0]); break;
case 7: b |= ((uint64_t) buf[6]) << 48; // fall through
case 6: b |= ((uint64_t) buf[5]) << 40; // fall through
case 5: b |= ((uint64_t) buf[4]) << 32; // fall through
case 4: b |= ((uint64_t) buf[3]) << 24; // fall through
case 3: b |= ((uint64_t) buf[2]) << 16; // fall through
case 2: b |= ((uint64_t) buf[1]) << 8; // fall through
case 1: b |= ((uint64_t) buf[0]); break;
case 0: break;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment