Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions src/develop/pixelpipe_hb.c
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,81 @@ static void _dev_pixelpipe_synch(dt_dev_pixelpipe_t *pipe,
}
}

/** remove stale entries (deleted, disabled or de-synced consumers) from a
raster mask source's users table, so it doesn't keep
publishing/invalidating forever */
static void _iop_prune_stale_raster_users(dt_dev_pixelpipe_t *pipe, dt_iop_module_t *module)
{
GHashTable *users = module->raster_mask.source.users;
if(!module->dev || !users || g_hash_table_size(users) == 0)
return;

/* Phantom users (deleted or de-synced consumers) keep the source republishing
-- and invalidating downstream -- every run. Judge each consumer from its
node in THIS pipe, never from module->enabled/blend_params: those track the
darkroom UI and are stale in the export pipe, where the piece is authoritative
(dropped a live export consumer's mask, regression 0167-raster-mask). We only
compare the consumer pointer, so a freed module is never dereferenced. */
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init(&iter, users);
while(g_hash_table_iter_next(&iter, &key, &value))
{
dt_iop_module_t *sink = key;

// locate the consumer's node in this pipe (pointer-only match)
dt_dev_pixelpipe_iop_t *sink_piece = NULL;
for(GList *nodes = pipe->nodes; nodes; nodes = g_list_next(nodes))
{
dt_dev_pixelpipe_iop_t *p = nodes->data;
if(p->module == sink)
{
sink_piece = p;
break;
}
}
if(!sink_piece)
{
g_hash_table_iter_remove(&iter);
dt_print_pipe(DT_DEBUG_PIPE | DT_DEBUG_MASKS,
"prune stale raster user",
pipe,
module,
DT_DEVICE_NONE,
NULL,
NULL,
"dropped deleted consumer");
continue;
}

// a real consumer must still point back at us, have its node enabled in
// this pipe, and actually have its blending in raster-mask mode. A module
// that named us as raster source but is then disabled (or switched its mask
// to drawn/parametric) leaves a phantom entry that would otherwise keep us
// publishing -- and invalidating every downstream cacheline -- on every run.
const dt_develop_blend_params_t *bp = sink_piece->blendop_data;
const gboolean consumes = sink->raster_mask.sink.source == module && sink_piece->enabled &&
bp && (bp->mask_mode & DEVELOP_MASK_RASTER);
if(!consumes)
{
g_hash_table_iter_remove(&iter);
dt_print_pipe(DT_DEBUG_PIPE | DT_DEBUG_MASKS,
"prune stale raster user",
pipe,
module,
DT_DEVICE_NONE,
NULL,
NULL,
"dropped '%s%s' (%s)",
sink->op,
dt_iop_get_instance_id(sink),
sink->raster_mask.sink.source != module ? "de-synced"
: !sink_piece->enabled ? "disabled"
: "not in raster mode");
}
}
}

void dt_dev_pixelpipe_synch_all(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
{
dt_pthread_mutex_lock(&pipe->busy_mutex);
Expand Down Expand Up @@ -675,6 +750,12 @@ void dt_dev_pixelpipe_synch_all(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
_dev_pixelpipe_synch(pipe, dev, history);
history = g_list_next(history);
}

// history has been (re)applied, so real raster consumers have re-registered;
// drop any phantom users left behind by deleted or de-synced consumers
for(GList *nodes = pipe->nodes; nodes; nodes = g_list_next(nodes))
_iop_prune_stale_raster_users(pipe, ((dt_dev_pixelpipe_iop_t *)nodes->data)->module);

dt_print_pipe(DT_DEBUG_PARAMS,
"synch all modules done",
pipe, NULL, DT_DEVICE_NONE, NULL, NULL,
Expand All @@ -699,6 +780,12 @@ void dt_dev_pixelpipe_synch_top(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
dt_print_pipe(DT_DEBUG_PARAMS, "synch top history module missing!",
pipe, NULL, DT_DEVICE_NONE, NULL, NULL);
}

// clear any phantom raster users (deleted/de-synced consumers) so a source
// doesn't keep republishing its mask and invalidating downstream every run
for(GList *nodes = pipe->nodes; nodes; nodes = g_list_next(nodes))
_iop_prune_stale_raster_users(pipe, ((dt_dev_pixelpipe_iop_t *)nodes->data)->module);

dt_pthread_mutex_unlock(&pipe->busy_mutex);
}

Expand Down
Loading