Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
dev_pixelpipe.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Aurélien PIERRE.
4 Copyright (C) 2026 Guillaume Stutin.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "common/debug.h"
21#include "common/darktable.h"
22#include "common/dtpthread.h"
23#include "develop/imageop.h"
24#include "develop/pixelpipe.h"
26#include "develop/blend.h"
28#include "control/control.h"
29#include "control/signal.h"
30#include <stdint.h>
31#include <stdlib.h>
32
33// Keep a preview-like virtual pipe in sync with history without running pixels.
36 const uint32_t history_end, GList *start_node,
37 const char *debug_label);
38// dt_dev_pixelpipe_propagate_formats() is the authoritative forward buffer-format pass; it is
39// declared in dev_pixelpipe.h. It must run before any dt_pixelpipe_get_global_hash() because the
40// auto-disable it performs feeds the cumulative global hash (see its definition).
41static void _dt_dev_pixelpipe_cache_wait_ready_callback(gpointer instance, const guint64 hash,
42 gpointer user_data);
43
50
64
66 = { .lock = { PTHREAD_MUTEX_INITIALIZER }, .pending = NULL, .connected = FALSE,
67 .wait_cursor_active = FALSE,
68 .next_request_id = 1, .queued_requests = 0, .served_requests = 0,
69 .cancelled_requests = 0, .immediate_hits = 0, .misses = 0 };
70
71static gboolean _cache_wait_cursor_progress(gpointer user_data)
72{
74 return G_SOURCE_REMOVE;
75
77 return G_SOURCE_REMOVE;
78}
79
80static gboolean _cache_wait_cursor_restore(gpointer user_data)
81{
83 return G_SOURCE_REMOVE;
84
87 return G_SOURCE_REMOVE;
88}
89
91{
92 for(GList *iter = _cache_wait_manager.pending; iter; iter = g_list_next(iter))
93 {
94 dt_dev_pixelpipe_cache_wait_record_t *record = iter->data;
95 if(!IS_NULL_PTR(record) && record->wait == wait)
96 {
97 _cache_wait_manager.pending = g_list_delete_link(_cache_wait_manager.pending, iter);
98 return record;
99 }
100 }
101
102 return NULL;
103}
104
106{
107 const char *context = !IS_NULL_PTR(reason) ? reason : "unspecified";
108 const int64_t now_us = g_get_monotonic_time();
109
110 // This is a diagnostic snapshot only. Skip it rather than stalling GUI teardown
111 // behind a cache-ready callback that is already updating the same wait list.
113 {
114 dt_print(DT_DEBUG_PIPECACHE, "[cache-wait] dump reason=%s skipped=lock-busy\n", context);
115 return;
116 }
117
118 const guint pending_count = g_list_length(_cache_wait_manager.pending);
120 "[cache-wait] dump reason=%s pending=%u queued=%" PRIu64 " served=%" PRIu64
121 " cancelled=%" PRIu64 " immediate=%" PRIu64 " misses=%" PRIu64 "\n",
122 context, pending_count, _cache_wait_manager.queued_requests,
125
126 for(GList *iter = _cache_wait_manager.pending; iter; iter = g_list_next(iter))
127 {
128 dt_dev_pixelpipe_cache_wait_record_t *record = iter->data;
129 dt_dev_pixelpipe_cache_wait_t *wait = !IS_NULL_PTR(record) ? record->wait : NULL;
130 if(IS_NULL_PTR(wait) || IS_NULL_PTR(record)) continue;
131
132 const int64_t age_ms = MAX((now_us - record->queued_at_us) / 1000, 0);
134 "[cache-wait] pending id=%" PRIu64 " owner=%s hash=%" PRIu64
135 " target=%s connected=%d age_ms=%" PRId64 "\n",
136 wait->request_id,
137 !IS_NULL_PTR(wait->owner_tag) ? wait->owner_tag : "(unknown)",
138 wait->hash,
139 !IS_NULL_PTR(wait->module) ? wait->module->op : "backbuf",
140 wait->connected,
141 age_ms);
142 }
144}
145
147 const dt_iop_module_t *module)
148{
149 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(module)) return FALSE;
150 if(pipe->type != DT_DEV_PIXELPIPE_PREVIEW) return FALSE;
151 if(dt_dev_pixelpipe_get_realtime(pipe)) return FALSE;
152 if(!pipe->gui_observable_source) return FALSE;
153
154 return !strcmp(module->op, "initialscale") || !strcmp(module->op, "colorout");
155}
156
158 const dt_iop_module_t *module)
159{
160 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(module)) return FALSE;
161 if(pipe->type != DT_DEV_PIXELPIPE_PREVIEW) return FALSE;
162 if(dt_dev_pixelpipe_get_realtime(pipe)) return FALSE;
163 if(!pipe->gui_observable_source) return FALSE;
164
165 return !strcmp(module->op, "gamma");
166}
167
168static gchar *_get_debug_pipe_name(const dt_dev_pixelpipe_t *pipe, const dt_develop_t *dev)
169{
170 if(!IS_NULL_PTR(dev) && !IS_NULL_PTR(pipe) && dev->virtual_pipe == pipe)
171 return g_strdup("virtual-preview");
172
173 return g_strdup(dt_pixelpipe_get_pipe_name(!IS_NULL_PTR(pipe) ? pipe->type : DT_DEV_PIXELPIPE_NONE));
174}
175
177{
178 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
179 {
181 if(!IS_NULL_PTR(piece) && !strcmp(piece->module->op, "detailmask")) return nodes;
182 }
183
184 return NULL;
185}
186
188{
189 dt_dev_pixelpipe_iop_t *detailmask_piece = NULL;
191
192 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
193 {
195 if(IS_NULL_PTR(piece)) continue;
196
197 if(!strcmp(piece->module->op, "detailmask")) detailmask_piece = piece;
198 if(piece->detail_mask)
199 {
201 break;
202 }
203 }
204
205 if(!IS_NULL_PTR(detailmask_piece))
206 {
207 const gboolean enabled = (pipe->want_detail_mask == DT_DEV_DETAIL_MASK_ENABLED)
208 && (detailmask_piece->dsc_in.channels == 4)
209 && (detailmask_piece->dsc_in.datatype == TYPE_FLOAT)
210 && (detailmask_piece->dsc_in.cst == IOP_CS_RGB);
211 detailmask_piece->enabled = enabled;
212 detailmask_piece->process_tiling_ready = !enabled;
213 if(!IS_NULL_PTR(detailmask_piece->data)) *((int *)detailmask_piece->data) = enabled ? 1 : 0;
214 }
215}
216
223
242{
243 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(pipe->dev) || IS_NULL_PTR(pipe->dev->gui_module)) return FALSE;
244 dt_develop_t *dev = pipe->dev;
245 dt_iop_module_t *focus = dev->gui_module;
246
247 const gboolean transient = dt_dev_transient_params_active(dev, focus);
248 const gboolean realtime = dt_dev_pixelpipe_get_realtime(pipe);
249 if(!transient && !realtime) return FALSE;
250
251 const uint32_t history_end = dt_dev_get_history_end_ext(dev);
252 if(history_end == 0) return FALSE;
253
254 // Enable/blend state comes from the focused module's history item (transient edits are params-only).
256 if(IS_NULL_PTR(hist) || IS_NULL_PTR(hist->module) || IS_NULL_PTR(hist->params)) return FALSE;
257
259 if(IS_NULL_PTR(piece)) return FALSE;
260
261 // Resolve params: thread-safe transient snapshot when published, else the history snapshot. A copy is
262 // taken so the slot lock is not held across commit_params(); never the live GUI module->params.
263 dt_iop_params_t *params = hist->params;
264 void *tbuf = NULL;
265 if(transient && focus->params_size > 0)
266 {
267 tbuf = g_malloc0(focus->params_size);
268 if(!IS_NULL_PTR(tbuf)
269 && dt_dev_transient_params_get(dev, focus, tbuf, (size_t)focus->params_size, NULL, 0, NULL))
270 params = (dt_iop_params_t *)tbuf;
271 else
272 dt_free(tbuf);
273 }
274
275 const gboolean previous_want_detail_mask = (pipe->want_detail_mask != DT_DEV_DETAIL_MASK_NONE);
276 piece->enabled = hist->enabled;
277 piece->detail_mask = !IS_NULL_PTR(hist->blend_params) && hist->blend_params->details != 0.0f;
278 dt_iop_commit_params(focus, params, hist->blend_params, pipe, piece);
279 dt_free(tbuf);
281 if(previous_want_detail_mask != (pipe->want_detail_mask != DT_DEV_DETAIL_MASK_NONE)) return FALSE;
282
283 // The global hash is cumulative and skips disabled nodes, so the format contract (which may
284 // disable a node on an incompatible input) must be settled first.
287
288 // Only advance the synch_top fence when the focused module is the newest history item (the realtime
289 // drawlayer case, where each heartbeat appended a top item). For a transient edit of a non-top module
290 // no history item changed, so the fence must stay where the last real history sync left it.
291 GList *last_item = g_list_nth(dev->history, history_end - 1);
292 if(last_item && (dt_dev_history_item_t *)last_item->data == hist)
293 {
294 pipe->last_history_hash = hist->hash;
295 pipe->last_history_item = hist;
296 }
297 return TRUE;
298}
299
332{
333 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(pipe->nodes)) return;
334
335 // The scopes/global histogram are not refreshed while a realtime stroke is in progress (the preview
336 // pipe is paused), so do not force any module to publish a host copy of its output just to feed them.
337 // This drops the per-frame RAM copy of `colorout` (and the histogram input/module caches) during
338 // realtime drawing, where it is pure overhead.
339 const gboolean realtime = dt_dev_pixelpipe_get_realtime(pipe);
340
341 gboolean current_output_must_cache_host = TRUE;
342
343 for(GList *pieces = g_list_last(pipe->nodes); pieces; pieces = g_list_previous(pieces))
344 {
345 dt_dev_pixelpipe_iop_t *piece = pieces->data;
346 dt_iop_module_t *module = !IS_NULL_PTR(piece) ? piece->module : NULL;
347 if(IS_NULL_PTR(piece) || IS_NULL_PTR(module) || !piece->enabled) continue;
348
349 gboolean supports_opencl = FALSE;
350#ifdef HAVE_OPENCL
351 supports_opencl = dt_opencl_is_inited() && piece->process_cl_ready && module->process_cl;
352#endif
353
354 gchar *string = g_strdup_printf("/plugins/%s/cache", module->op);
355 if(!dt_conf_key_exists(string) || !dt_conf_key_not_empty(string))
357
358 const gboolean authored_cache = piece->cache_output_on_ram;
359 const gboolean user_requested_cache = dt_conf_get_bool(string);
360 dt_free(string);
361
362 const gboolean color_picker_on = dt_iop_color_picker_force_cache(pipe, module);
363 const gboolean global_hist_output_on
364 = !realtime && _module_requires_global_histogram_output_cache(pipe, module);
365 const gboolean global_hist_input_on
366 = !realtime && _module_requires_global_histogram_input_cache(pipe, module);
367 const gboolean module_hist_on
368 = !realtime
369 && (pipe->type == DT_DEV_PIXELPIPE_PREVIEW
370 && pipe->gui_observable_source
372 && (piece->request_histogram & DT_REQUEST_ON));
373 const gboolean active_in_gui
375 && pipe->dev->gui_module == module;
376
377 const gboolean has_autoset = pipe->autoset && !IS_NULL_PTR(module->autoset);
378
379 const gboolean previous_output_must_cache_host
380 = !supports_opencl || active_in_gui || module_hist_on || global_hist_input_on || has_autoset;
381
383 = authored_cache || user_requested_cache || color_picker_on
384 || global_hist_output_on
385 || current_output_must_cache_host;
386
387 current_output_must_cache_host = previous_output_must_cache_host;
388 }
389}
390
398
404
406{
407 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
409 // Virtual pipe mirrors preview history for GUI coordinate transforms.
411}
412
419
425
427{
428 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
430 // Virtual pipe mirrors preview history for GUI coordinate transforms.
432}
433
440
442{
443 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
445 // Keep the virtual pipe aligned with preview ROI/zoom changes for GUI transforms.
447}
448
450{
451 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
452 /* Zoom/pan updates must run through the normal validity/ROI flow.
453 * If a module left the main pipe in realtime mode, force it back to
454 * standard mode here so zoom changes cannot get visually stuck on a
455 * best-effort backbuffer policy. */
459}
460
467
469{
470 if (IS_NULL_PTR(dev) || !dev->gui_attached) return;
471 /* Entering a zoom/pan change always exits realtime rendering policy.
472 * Realtime is stroke-scoped and should never control darkroom navigation. */
474 // Slightly different logic: killswitch ASAP,
475 // then redraw UI ASAP for feedback,
476 // finally flag the pipe as dirty for later recompute.
477 // Remember GUI responsiveness is paramount, since a laggy UI
478 // will make user repeat their order for lack of feedback,
479 // meaning relaunching a pipe recompute, meaning working more
480 // for the same contract.
483 gtk_widget_queue_draw(dt_ui_center(darktable.gui->ui));
486}
487
489{
490 return (dev // don't segfault
491 && dev->gui_attached // don't run on background/export pipes
492 && dev->gui_module // don't segfault
493 && dev->gui_module != current_module
494 // current_module is not the active one (capturing edit mode)
495 && dev->gui_module->operation_tags_filter() & current_module->operation_tags())
496 // current_module does operation(s) that active module doesn't want
498 // cache bypass is our hint that the active module is in "editing" mode
499}
500
501
503 const int width_in, const int height_in,
504 int *width, int *height)
505{
506 dt_iop_roi_t roi_in = (dt_iop_roi_t){ 0, 0, width_in, height_in, 1.0 };
507 dt_iop_roi_t roi_out = roi_in;
508 gchar *pipe_name = NULL;
510 pipe_name = _get_debug_pipe_name(pipe, pipe->dev);
511
512 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
513 {
515 dt_iop_module_t *module = piece->module;
516
517 piece->buf_in = roi_in;
518
519 // If in GUI and using a module that needs a full, undistorterted image,
520 // we need to shutdown temporarily any module distorting the image.
522 piece->enabled = FALSE;
523
524 // If module is disabled, modify_roi_out() is a no-op
525 if(piece->enabled)
526 module->modify_roi_out(module, pipe, piece, &roi_out, &roi_in);
527 else
528 roi_out = roi_in;
529
530 // Forward ROI planning answers "what output rectangle does this module
531 // produce from the previous one ?". Logging the tuple here makes each
532 // module-local geometry change visible on `-d pipe`.
533 if(piece->enabled && (darktable.unmuted & DT_DEBUG_PIPE))
535 "[roi-out] pipe=%-15s module=%-18s enabled=%d in =(x=%5d y=%5d w=%5d h=%5d scale=%2.2f)"
536 " out=(x=%5d y=%5d w=%5d h=%5d scale=%2.2f)\n",
537 pipe_name, module->op, piece->enabled,
538 roi_in.x, roi_in.y, roi_in.width, roi_in.height, roi_in.scale,
539 roi_out.x, roi_out.y, roi_out.width, roi_out.height, roi_out.scale);
540
541 piece->buf_out = roi_out;
542 roi_in = roi_out;
543 }
544
545 if(pipe_name) dt_free(pipe_name);
546 *width = roi_out.width;
547 *height = roi_out.height;
548}
549
551{
552 // while module->modify_roi_out describes how the current module will change the size of
553 // the output buffer depending on its parameters (pretty intuitive),
554 // module->modify_roi_in describes "how much material" the current module needs from the previous one,
555 // because some modules (lens correction) need a padding on their input.
556 // The tricky part is therefore that the effect of the current module->modify_roi_in() needs to be repercuted
557 // upstream in the pipeline for proper pipeline cache invalidation, so we need to browse the pipeline
558 // backwards.
559
560 // The virtual pipe is expected to be ready before calling this.
561 // This function no longer supports NULL pipes or ad-hoc temp nodes.
562
563 dt_iop_roi_t roi_out_temp = roi_out;
564 dt_iop_roi_t roi_in;
565 gchar *pipe_name = NULL;
567 pipe_name = _get_debug_pipe_name(pipe, pipe->dev);
568 for(GList *nodes = g_list_last(pipe->nodes); nodes; nodes = g_list_previous(nodes))
569 {
571 dt_iop_module_t *module = piece->module;
572
573 piece->roi_out = roi_out_temp;
574
575 // If in GUI and using a module that needs a full, undistorterted image,
576 // we need to shutdown temporarily any module distorting the image.
578 piece->enabled = FALSE;
579
580 // If module is disabled, modify_roi_in() is a no-op
581 if(piece->enabled)
582 module->modify_roi_in(module, pipe, piece, &roi_out_temp, &roi_in);
583 else
584 roi_in = roi_out_temp;
585
586 // Backward ROI planning answers "how much input rectangle does this
587 // module need from upstream to deliver the requested downstream output ?".
588 // Logging that request before and after modify_roi_in() makes ROI growth
589 // and padding traceable module-by-module on `-d pipe`.
590 if(piece->enabled && (darktable.unmuted & DT_DEBUG_PIPE))
592 "[roi-in ] pipe=%-15s module=%-18s enabled=%d out=(x=%5d y=%5d w=%5d h=%5d scale=%2.2f)"
593 " in=(x=%5d y=%5d w=%5d h=%5d scale=%2.2f)\n",
594 pipe_name, module->op, piece->enabled,
595 roi_out_temp.x, roi_out_temp.y, roi_out_temp.width, roi_out_temp.height, roi_out_temp.scale,
596 roi_in.x, roi_in.y, roi_in.width, roi_in.height, roi_in.scale);
597
598 piece->roi_in = roi_in;
599 roi_out_temp = roi_in;
600 }
601
602 if(pipe_name) dt_free(pipe_name);
603
604 /* ROI planning runs backwards, but rawprepare seals the effective Bayer/X-Trans phase only once
605 * the real input crop is known. Forward that authored RAW descriptor now so the downstream RAW
606 * modules process with the same CFA layout that rawprepare just computed for this run. */
607 dt_iop_buffer_dsc_t upstream_dsc = pipe->dev->image_storage.dsc;
608 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
609 {
611
612 if(piece->dsc_in.cst == IOP_CS_RAW && piece->dsc_in.channels == 1)
613 {
614 piece->dsc_in.filters = upstream_dsc.filters;
615 memcpy(piece->dsc_in.xtrans, upstream_dsc.xtrans, sizeof(piece->dsc_in.xtrans));
616 }
617
618 if(piece->dsc_out.cst == IOP_CS_RAW && piece->dsc_out.channels == 1
619 && (!piece->enabled || strcmp(piece->module->op, "rawprepare")))
620 {
621 piece->dsc_out.filters = piece->dsc_in.filters;
622 memcpy(piece->dsc_out.xtrans, piece->dsc_in.xtrans, sizeof(piece->dsc_out.xtrans));
623 }
624
625 upstream_dsc = piece->dsc_out;
626 }
627
628}
629
631{
632 // Start with a hash that is unique, image-wise.
633 return dt_hash(5381, (const char *)&pipe->dev->image_storage.filename, DT_MAX_FILENAME_LEN);
634}
635
637 const dt_iop_roi_t roi_out, const int pos)
638{
639 // to be called at runtime, not at pipe init.
640
641 // Only at the first step of pipe, we don't have a module because we init the base buffer.
642 if(!IS_NULL_PTR(piece))
643 return piece->global_hash;
644 else
645 {
646 // This is used for the first step of the pipe, before modules, when initing base buffer
647 // We need to take care of the ROI manually
648 uint64_t hash = _default_pipe_hash(pipe);
649 hash = dt_hash(hash, (const char *)&roi_out, sizeof(dt_iop_roi_t));
650 return dt_hash(hash, (const char *)&pos, sizeof(int));
651 }
652}
653
655 const dt_iop_module_t *module)
656{
657 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(module)) return NULL;
658
659 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node))
660 {
661 dt_dev_pixelpipe_iop_t *const piece = node->data;
662 if(piece && piece->enabled && piece->module == module)
663 return piece;
664 }
665
666 return NULL;
667}
668
670 const dt_dev_pixelpipe_iop_t *piece)
671{
672 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(piece)) return NULL;
673
674 GList *node = g_list_find(pipe->nodes, (gpointer)piece);
675 if(IS_NULL_PTR(node)) return NULL;
676
677 for(node = g_list_previous(node); node; node = g_list_previous(node))
678 {
679 dt_dev_pixelpipe_iop_t *const previous = node->data;
680 if(previous && previous->enabled)
681 return previous;
682 }
683
684 return NULL;
685}
686
688 void **data, dt_pixel_cache_entry_t **cache_entry,
691 gpointer restart_data)
692{
693 if(!IS_NULL_PTR(data)) *data = NULL;
694 if(!IS_NULL_PTR(cache_entry)) *cache_entry = NULL;
695 if(IS_NULL_PTR(pipe)) return FALSE;
696
697 // Module-output cache requests are not satisfiable when the target piece
698 // itself bypasses cache retention. Re-queueing those from GUI redraws would
699 // keep transient edit modes in a self-feeding recompute loop.
700 // Upstream targets (for example color-picker input sampling) stay allowed even if a
701 // downstream piece enabled bypass mode.
702 if(!IS_NULL_PTR(piece)
703 && (dt_dev_pixelpipe_get_realtime(pipe) || pipe->no_cache || piece->bypass_cache))
704 return FALSE;
705
706 const uint64_t hash = !IS_NULL_PTR(piece) ? piece->global_hash : dt_dev_pixelpipe_get_hash(pipe);
707
708 // For the final backbuffer (piece == NULL), GUI consumers display the last *published* frame,
709 // identified by pipe->backbuf.hash. The pipeline plans the next frame ahead of publishing it, so
710 // pipe->hash (the planned final hash) runs ahead of the backbuffer while a recompute is in flight.
711 // This is the normal steady state of realtime drawing: every heartbeat plans a new frame. Peeking
712 // the planned hash would then miss the perfectly valid published frame, the darkroom main-surface
713 // lock would fail, and the view would drop to the paused preview pipe (which lacks the in-progress
714 // stroke) — the flicker between "drawing applied" and "original". So look up the published backbuf
715 // hash for display, and keep the planned hash only to drive recompute on a genuine miss below.
716 uint64_t display_hash = hash;
717 if(IS_NULL_PTR(piece))
718 {
719 const uint64_t backbuf_hash = dt_dev_backbuf_get_hash(&pipe->backbuf);
720 if(backbuf_hash != DT_PIXELPIPE_CACHE_HASH_INVALID) display_hash = backbuf_hash;
721 }
722
723 void *buffer = NULL;
724 dt_pixel_cache_entry_t *entry = NULL;
725 // GUI consumers run on the CPU and never own the OpenCL device lock, so we pass preferred_devid = -1:
726 // dt_dev_pixelpipe_cache_peek() then returns host-resident cachelines directly but refuses to
727 // materialize device-only ones from the GPU (which would enqueue hidden GPU work from the GUI thread,
728 // racing whichever pipeline thread currently owns the device — the clReleaseEvent crash). A device-only
729 // entry is reported as a miss, so the request below waits for the pipeline to publish a host copy
730 // (modules whose output the GUI samples, e.g. initialscale, already cache to RAM).
731 if(display_hash != DT_PIXELPIPE_CACHE_HASH_INVALID
732 && dt_dev_pixelpipe_cache_peek(darktable.pixelpipe_cache, display_hash, &buffer, &entry, -1, NULL)
733 && !IS_NULL_PTR(buffer) && !IS_NULL_PTR(entry))
734 {
735 // These counters are only diagnostic; cache consumers should not wait for
736 // queue lifecycle updates before reopening an already available cacheline.
738 {
741 }
742 dt_dev_pixelpipe_cache_wait_cleanup(wait, "peek-gui-immediate-hit");
743 if(!IS_NULL_PTR(data)) *data = buffer;
744 if(!IS_NULL_PTR(cache_entry)) *cache_entry = entry;
745 return TRUE;
746 }
747
748 // A missed GUI peek already requests cache publication below. The miss counter
749 // is diagnostic, so it must not block redraw/picker paths under contention.
751 {
754 }
755
756 gboolean request_cacheline = TRUE;
757 if(!IS_NULL_PTR(wait) && !IS_NULL_PTR(restart) && hash != DT_PIXELPIPE_CACHE_HASH_INVALID)
758 {
759 const gboolean changed_target = !wait->connected
760 || wait->pipe != pipe
761 || wait->module != (!IS_NULL_PTR(piece) ? piece->module : NULL)
762 || wait->hash != hash
763 || wait->restart != restart;
764 if(changed_target)
765 {
766 gboolean activate_wait_cursor = FALSE;
767 dt_dev_pixelpipe_cache_wait_cleanup(wait, "peek-gui-target-changed");
768 wait->pipe = pipe;
769 wait->module = !IS_NULL_PTR(piece) ? piece->module : NULL;
770 wait->hash = hash;
771 wait->restart = restart;
772 wait->user_data = restart_data;
773 if(IS_NULL_PTR(wait->owner_tag))
774 wait->owner_tag = !IS_NULL_PTR(piece) && !IS_NULL_PTR(piece->module) ? piece->module->op : "gui-backbuf";
775 if(IS_NULL_PTR(wait->owner_object))
776 wait->owner_object = restart_data;
777 wait->connected = TRUE;
778
782 dt_dev_pixelpipe_cache_wait_record_t *record = calloc(1, sizeof(*record));
783 record->wait = wait;
784 record->request_id = wait->request_id;
785 record->queued_at_us = g_get_monotonic_time();
786 _cache_wait_manager.pending = g_list_prepend(_cache_wait_manager.pending, record);
788 {
792 }
794 {
796 activate_wait_cursor = TRUE;
797 }
799 if(activate_wait_cursor)
800 g_main_context_invoke(NULL, _cache_wait_cursor_progress, NULL);
801
803 "[cache-wait] queued id=%" PRIu64 " owner=%s hash=%" PRIu64 " target=%s\n",
804 wait->request_id,
805 !IS_NULL_PTR(wait->owner_tag) ? wait->owner_tag : "(unknown)",
806 wait->hash,
807 !IS_NULL_PTR(wait->module) ? wait->module->op : "backbuf");
808 }
809 else
810 {
811 /* The GUI already waits for this exact cacheline. Re-emitting CACHE_REQUEST from
812 * each expose keeps retrying an unsatisfied target forever when the pipeline cannot
813 * publish it, for example after an OpenCL memory pre-check failure on very large images. */
814 request_cacheline = FALSE;
815 }
816 }
817
818 if(request_cacheline)
819 {
823 !IS_NULL_PTR(piece) ? piece->module : NULL);
825
826 dt_print(DT_DEBUG_DEV, "[pixelpipe/gui] request host cache pipe=%s target=%s hash=%" PRIu64 "\n",
828 !IS_NULL_PTR(piece) && !IS_NULL_PTR(piece->module) ? piece->module->op : "backbuf", hash);
829 }
830
831 return FALSE;
832}
833
850static void _dt_dev_pixelpipe_cache_wait_ready_callback(gpointer instance, const guint64 hash,
851 gpointer user_data)
852{
853 GList *to_restart = NULL;
854 gboolean restore_wait_cursor = FALSE;
855
857 for(GList *iter = _cache_wait_manager.pending; iter; )
858 {
859 GList *next = g_list_next(iter);
860 dt_dev_pixelpipe_cache_wait_record_t *record = iter->data;
861 dt_dev_pixelpipe_cache_wait_t *wait = !IS_NULL_PTR(record) ? record->wait : NULL;
862 if(!IS_NULL_PTR(wait) && wait->connected && wait->hash == hash)
863 {
864 _cache_wait_manager.pending = g_list_delete_link(_cache_wait_manager.pending, iter);
866 wait->connected = FALSE;
867 to_restart = g_list_prepend(to_restart, wait);
868 dt_free(record);
869 }
870 iter = next;
871 }
872
874 {
878 }
880 {
882 restore_wait_cursor = TRUE;
883 }
885 if(restore_wait_cursor)
886 g_main_context_invoke(NULL, _cache_wait_cursor_restore, NULL);
887
888 for(GList *iter = to_restart; iter; iter = g_list_next(iter))
889 {
890 dt_dev_pixelpipe_cache_wait_t *wait = iter->data;
892 gpointer restart_data = wait->user_data;
893
895 "[cache-wait] served id=%" PRIu64 " owner=%s hash=%" PRIu64 " target=%s\n",
896 wait->request_id,
897 !IS_NULL_PTR(wait->owner_tag) ? wait->owner_tag : "(unknown)",
898 hash,
899 !IS_NULL_PTR(wait->module) ? wait->module->op : "backbuf");
900
901 wait->pipe = NULL;
902 wait->module = NULL;
904 wait->restart = NULL;
905 wait->user_data = NULL;
906 wait->request_id = 0;
907
908 if(!IS_NULL_PTR(restart)) restart(restart_data);
909 }
910 g_list_free(to_restart);
911}
912
914{
915 if(IS_NULL_PTR(wait) || !wait->connected) return;
916 gboolean restore_wait_cursor = FALSE;
917 int64_t queued_at_us = 0;
918 const uint64_t request_id = wait->request_id;
919 const uint64_t hash = wait->hash;
920 const char *owner_tag = wait->owner_tag;
921 const dt_iop_module_t *module = wait->module;
922 const char *cancel_reason = !IS_NULL_PTR(reason) ? reason : "unspecified";
923
926 if(!IS_NULL_PTR(record))
927 {
928 queued_at_us = record->queued_at_us;
929 dt_free(record);
930 }
933 {
937 }
939 {
941 restore_wait_cursor = TRUE;
942 }
944 if(restore_wait_cursor)
945 g_main_context_invoke(NULL, _cache_wait_cursor_restore, NULL);
946
947 const int64_t age_ms = queued_at_us > 0 ? MAX((g_get_monotonic_time() - queued_at_us) / 1000, 0) : -1;
949 "[cache-wait] cancelled id=%" PRIu64 " owner=%s hash=%" PRIu64
950 " target=%s age_ms=%" PRId64 " reason=%s\n",
951 request_id,
952 !IS_NULL_PTR(owner_tag) ? owner_tag : "(unknown)",
953 hash,
954 !IS_NULL_PTR(module) ? module->op : "backbuf",
955 age_ms,
956 cancel_reason);
957
958 wait->pipe = NULL;
959 wait->module = NULL;
961 wait->restart = NULL;
962 wait->user_data = NULL;
963 wait->request_id = 0;
964 wait->connected = FALSE;
965}
966
968 const char *owner_tag,
969 gpointer owner_object)
970{
971 if(IS_NULL_PTR(wait)) return;
972 wait->owner_tag = owner_tag;
973 wait->owner_object = owner_object;
974}
975
976/* Establish the *input* side of a piece's buffer contract from the upstream descriptor by
977 * asking the module's input_format(). This does NOT decide compatibility or finalize the
978 * output: the single authoritative pass dt_dev_pixelpipe_propagate_formats() owns the mismatch/auto-disable
979 * decision so it is taken once, on the fully-committed and consistently-threaded chain, never
980 * on the stale partial state a synch_top / realtime edit can leave behind (issue #733).
981 *
982 * It is split out from that pass because commit_params() of a few modules (exposure, highlights,
983 * invert, temperature) reads piece->dsc_in (filters, processed_maximum), so the commit loop must
984 * set dsc_in before committing. */
985// Short colorspace name for the [dsc] format-propagation debug log (-d pipe). Knowing the cst of
986// each node's input/output is essential to spot when the pixelpipe's automatic RGB<->Lab conversion
987// should fire: a Lab-domain module whose dsc_in.cst is not "lab" will silently consume RGB.
988static const char *_dsc_cst_name(const int cst)
989{
990 switch(cst)
991 {
992 case IOP_CS_RAW: return "raw";
993 case IOP_CS_LAB: return "lab";
994 case IOP_CS_RGB: return "rgb";
995 case IOP_CS_RGB_DISPLAY: return "display-rgb";
996 case IOP_CS_LCH: return "lch";
997 case IOP_CS_HSL: return "hsl";
998 case IOP_CS_JZCZHZ: return "jzczhz";
999 case IOP_CS_NONE: return "none";
1000 default: return "?";
1001 }
1002}
1003
1005 const dt_iop_buffer_dsc_t *upstream_dsc)
1006{
1007 piece->dsc_in = *upstream_dsc;
1008 piece->dsc_out = *upstream_dsc;
1011
1012 /* Disabled modules are strict pass-through stages. Their advertised contracts must
1013 * not rewrite the upstream descriptor, otherwise a disabled RGB-only module can make
1014 * downstream RAW stages such as demosaic believe the stream was already converted. */
1015 if(!piece->enabled) return;
1016
1017 piece->module->input_format(piece->module, pipe, piece, &piece->dsc_in);
1019 piece->dsc_out = piece->dsc_in;
1021}
1022
1024 dt_iop_params_t *params, dt_develop_blend_params_t *blend_params,
1025 dt_iop_buffer_dsc_t *upstream_dsc)
1026{
1027 const dt_iop_buffer_dsc_t actual_input_dsc = *upstream_dsc;
1028
1029 // Set dsc_in before committing (commit_params() of some modules reads it). Compatibility and
1030 // the final output descriptor are settled later by dt_dev_pixelpipe_propagate_formats(), so this loop
1031 // never auto-disables a module on a possibly-stale upstream descriptor.
1032 _prepare_piece_input_contract(pipe, piece, upstream_dsc);
1033
1034 // This should run even if the module is disabled because
1035 // some modules with no history self-enable based on runtime context
1036 dt_iop_commit_params(piece->module, params, blend_params, pipe, piece);
1037
1038 if(piece->enabled)
1039 {
1040 piece->module->output_format(piece->module, pipe, piece, &piece->dsc_out);
1042 }
1043 else
1044 {
1045 piece->dsc_in = actual_input_dsc;
1046 piece->dsc_out = actual_input_dsc;
1049 }
1050
1051 *upstream_dsc = piece->dsc_out;
1052}
1053
1054/* Direction-independent forward format pass.
1055 *
1056 * The buffer-format contract (channels / datatype / colorspace / CFA presence flowing from one
1057 * module to the next) is conceptually independent of ROI planning, which runs in both
1058 * directions (end->start for a target size, start->end for the native size). Deriving the
1059 * contract inside the history-sync commit loop made it sensitive to partial syncs
1060 * (synch_top / realtime top edits): a downstream module could read a stale upstream descriptor
1061 * and get wrongly disabled for an "unexpected input buffer format" (issue #733). Worse, the
1062 * auto-disable is one-way, so a later pass could not undo a false positive.
1063 *
1064 * This single forward pass re-threads the whole chain from the input image descriptor after
1065 * params have been committed, and is the *only* place that disables a module for a genuinely
1066 * incompatible input. Disabling here is safe because the chain is consistent and fully threaded
1067 * from pipe->dev->image_storage.dsc. Only the first stage (basebuffer) reads the input image
1068 * type; every later node derives its contract solely from its upstream piece. The lone
1069 * ROI-dependent refinement, rawprepare's CFA phase shift, is finalized later in modify_roi_*().
1070 *
1071 * It is deliberately NON-destructive for compatible enabled modules: it verifies the input
1072 * contract and re-threads dsc_in, but keeps the dsc_out that commit_params() produced. dsc_out
1073 * carries runtime fields (processed_maximum, rawprepare black/white, temperature coeffs, CFA
1074 * phase) that input/output_format cannot reconstruct, so recomputing them here would wipe the
1075 * tonal scaling and render images black/white. */
1077{
1078 if(IS_NULL_PTR(pipe)) return;
1079
1080 dt_iop_buffer_dsc_t upstream_dsc = pipe->dev->image_storage.dsc;
1081 gchar *pipe_name = (darktable.unmuted & DT_DEBUG_PIPE) ? _get_debug_pipe_name(pipe, NULL) : NULL;
1082
1083 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
1084 {
1086 if(IS_NULL_PTR(piece) || IS_NULL_PTR(piece->module)) continue;
1087
1088 const dt_iop_buffer_dsc_t actual_input_dsc = upstream_dsc;
1089
1090 if(!piece->enabled)
1091 {
1092 // Disabled modules are strict pass-through stages.
1093 piece->dsc_in = actual_input_dsc;
1094 piece->dsc_out = actual_input_dsc;
1097 }
1098 else
1099 {
1100 // Ask the module what input it expects, into a scratch descriptor. We must NOT recompute
1101 // piece->dsc_out from input/output_format here: commit_params() already produced dsc_out
1102 // with runtime fields that those methods cannot reconstruct (processed_maximum, rawprepare
1103 // black/white, temperature coeffs, CFA phase). Recomputing would reset processed_maximum to
1104 // the input image's value and blow up downstream tone handling (black/white renders).
1105 dt_iop_buffer_dsc_t declared_in = actual_input_dsc;
1106 dt_iop_buffer_dsc_update_bpp(&declared_in);
1107 piece->module->input_format(piece->module, pipe, piece, &declared_in);
1108 dt_iop_buffer_dsc_update_bpp(&declared_in);
1109
1110 // A module whose declared input does not match what the previous stage actually publishes
1111 // cannot run: disable it (in the pipe only; history is untouched) and turn it into a
1112 // pass-through so the contract keeps flowing to the next stage.
1113 const gboolean input_mismatch
1114 = (declared_in.bpp != actual_input_dsc.bpp
1115 || declared_in.channels != actual_input_dsc.channels
1116 || declared_in.filters != actual_input_dsc.filters);
1117
1118 if(input_mismatch)
1119 {
1120 dt_control_log(_("disabled module `%s`: unexpected input buffer format"),
1121 piece->module->name());
1123 "[pixelpipe] disabling module %s because input format expects %" G_GSIZE_FORMAT
1124 " B/px, %u channels, filters %u but upstream publishes %" G_GSIZE_FORMAT
1125 " B/px, %u channels, filters %u\n",
1126 piece->module->op, declared_in.bpp, declared_in.channels, declared_in.filters,
1127 actual_input_dsc.bpp, actual_input_dsc.channels, actual_input_dsc.filters);
1128
1129 piece->enabled = FALSE;
1134 piece->dsc_in = actual_input_dsc;
1135 piece->dsc_out = actual_input_dsc;
1138 }
1139 else
1140 {
1141 // Input is compatible: publish the module's DECLARED input descriptor. `declared_in` was
1142 // seeded from the upstream descriptor and then run through input_format(), so it already
1143 // carries the upstream runtime fields (processed_maximum, rawprepare, temperature, CFA
1144 // phase) AND the colorspace/channel layout the module actually consumes.
1145 //
1146 // Critically, we must NOT fall back to `actual_input_dsc` here: that would reset dsc_in.cst
1147 // to the upstream colorspace and make it equal to the buffer's cst, so the pixelpipe's
1148 // automatic colorspace conversion (pixelpipe_cpu.c / pixelpipe_gpu.c) would believe no
1149 // conversion is needed. Lab-domain modules (old color balance, color checker, atrous, ...)
1150 // inserted after an RGB stage would then receive RGB data interpreted as Lab and render
1151 // garbled/solid colors. The full-resync path keeps dsc_in from input_format() for the same
1152 // reason; this incremental (synch_top) path must match it.
1153 piece->dsc_in = declared_in;
1155 }
1156 }
1157
1158 if(pipe_name)
1160 "[dsc] pipe=%s module=%s enabled=%d in=(cst=%s ch=%i bpp=%" G_GSIZE_FORMAT
1161 " filters=%u) out=(cst=%s ch=%i bpp=%" G_GSIZE_FORMAT " filters=%u)\n",
1162 pipe_name, piece->module->op, piece->enabled,
1163 _dsc_cst_name(piece->dsc_in.cst), piece->dsc_in.channels, piece->dsc_in.bpp, piece->dsc_in.filters,
1164 _dsc_cst_name(piece->dsc_out.cst), piece->dsc_out.channels, piece->dsc_out.bpp, piece->dsc_out.filters);
1165
1166 upstream_dsc = piece->dsc_out;
1167 }
1168
1169 dt_free(pipe_name);
1170}
1171
1172static void _sync_pipe_nodes_from_history(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev, const uint32_t history_end,
1173 const char *debug_label)
1174{
1175 dt_iop_buffer_dsc_t upstream_dsc = pipe->dev->image_storage.dsc;
1176 const gboolean previous_want_detail_mask = (pipe->want_detail_mask != DT_DEV_DETAIL_MASK_NONE);
1177
1178 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
1179 {
1181 if(IS_NULL_PTR(piece)) continue;
1182
1187 piece->enabled = piece->module->default_enabled;
1188 piece->detail_mask = FALSE;
1189
1190 dt_iop_params_t *params = piece->module->default_params;
1191 dt_develop_blend_params_t *blend_params = piece->module->default_blendop_params;
1192 gboolean found_history = FALSE;
1193
1194 for(GList *history = g_list_nth(dev->history, history_end - 1);
1195 history;
1196 history = g_list_previous(history))
1197 {
1198 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1199 if(piece->module == hist->module)
1200 {
1201 piece->enabled = hist->enabled;
1202 params = hist->params;
1203 blend_params = hist->blend_params;
1204 found_history = TRUE;
1205 break;
1206 }
1207 }
1208
1209 piece->detail_mask = blend_params && blend_params->details != 0.0f;
1210 if(!strcmp(piece->module->op, "detailmask"))
1212 _commit_piece_contract(pipe, piece, params, blend_params, &upstream_dsc);
1213
1214 if(!found_history)
1215 dt_print(DT_DEBUG_PARAMS, "[pixelpipe] info: committed default params for %s (%s) in pipe %s\n",
1216 piece->module->op, piece->module->multi_name, debug_label);
1217 }
1218
1220 if(previous_want_detail_mask != (pipe->want_detail_mask != DT_DEV_DETAIL_MASK_NONE))
1221 {
1222 GList *detailmask_node = _find_detailmask_node(pipe);
1223 if(detailmask_node)
1224 _sync_pipe_nodes_from_history_from_node(pipe, history_end, detailmask_node, debug_label);
1225 }
1226}
1227
1229 const uint32_t history_end, GList *start_node,
1230 const char *debug_label)
1231{
1232 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(start_node)) return;
1233
1234 dt_iop_buffer_dsc_t upstream_dsc = pipe->dev->image_storage.dsc;
1235 const gboolean previous_want_detail_mask = (pipe->want_detail_mask != DT_DEV_DETAIL_MASK_NONE);
1236 for(GList *node = g_list_first(pipe->nodes); node && node != start_node; node = g_list_next(node))
1237 {
1239 if(IS_NULL_PTR(piece)) continue;
1240
1241 upstream_dsc = piece->dsc_out;
1242 }
1243
1244 for(GList *nodes = start_node; nodes; nodes = g_list_next(nodes))
1245 {
1247 if(IS_NULL_PTR(piece)) continue;
1248
1253
1254 piece->enabled = piece->module->default_enabled;
1255 piece->detail_mask = FALSE;
1256
1257 dt_iop_params_t *params = piece->module->default_params;
1258 dt_develop_blend_params_t *blend_params = piece->module->default_blendop_params;
1259 gboolean found_history = FALSE;
1260
1261 for(GList *history = g_list_nth(pipe->dev->history, history_end - 1);
1262 history;
1263 history = g_list_previous(history))
1264 {
1265 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1266 if(piece->module == hist->module)
1267 {
1268 piece->enabled = hist->enabled;
1269 params = hist->params;
1270 blend_params = hist->blend_params;
1271 found_history = TRUE;
1272 break;
1273 }
1274 }
1275
1276 piece->detail_mask = blend_params && blend_params->details != 0.0f;
1277 if(!strcmp(piece->module->op, "detailmask"))
1279 _commit_piece_contract(pipe, piece, params, blend_params, &upstream_dsc);
1280
1281 if(!found_history)
1282 dt_print(DT_DEBUG_PARAMS, "[pixelpipe] info: committed default params for %s (%s) in pipe %s\n",
1283 piece->module->op, piece->module->multi_name, debug_label);
1284 }
1285
1287 if(previous_want_detail_mask != (pipe->want_detail_mask != DT_DEV_DETAIL_MASK_NONE))
1288 {
1289 GList *detailmask_node = _find_detailmask_node(pipe);
1290 if(detailmask_node && detailmask_node != start_node)
1291 _sync_pipe_nodes_from_history_from_node(pipe, history_end, detailmask_node, debug_label);
1292 }
1293}
1294
1296{
1297 /* Traverse the pipeline node by node and compute the cumulative (global) hash of each module.
1298 * This hash takes into account the hashes of the previous modules and the size of the current ROI.
1299 * It is used to map pipeline cache states to current parameters.
1300 * It represents the state of internal modules params as well as their position in the pipe and their output size.
1301 * It is to be called at pipe init, not at runtime.
1302 */
1303
1304 // bernstein hash (djb2)
1305 uint64_t hash = _default_pipe_hash(pipe);
1306 gboolean passthrough_preview = FALSE;
1307
1308 // Bypassing cache contaminates downstream modules, starting at the module requesting it.
1309 // Usecase : crop, clip, ashift, etc. that need the uncropped image ;
1310 // mask displays ; overexposed/clipping alerts and all other transient previews.
1311 gboolean bypass_cache = FALSE;
1312
1313 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node))
1314 {
1316 if(!piece->enabled) continue;
1317
1318 // Combine with the previous bypass states
1319 bypass_cache |= piece->module->bypass_cache;
1320 piece->bypass_cache = bypass_cache;
1321
1322 // Combine with the previous modules hashes
1323 uint64_t local_hash = piece->hash;
1324
1325 // Some GUI previews author their final display inside the active module,
1326 // then runtime forwards that exact buffer through later pass-through stages.
1327 // Keep the planned hash contract aligned with the published cacheline so the
1328 // GUI does not keep requesting a downstream output hash that will never exist.
1329 if(passthrough_preview
1330 && !(piece->module->operation_tags() & IOP_TAG_DISTORT)
1331 && (piece->dsc_in.bpp == piece->dsc_out.bpp)
1332 && !memcmp(&piece->roi_in, &piece->roi_out, sizeof(dt_iop_roi_t)))
1333 {
1334 piece->global_mask_hash = hash;
1335 piece->global_hash = hash;
1336 continue;
1337 }
1338
1339 // Panning and zooming change the ROI. Some GUI modes (crop in editing mode) too.
1340 // dt_dev_get_roi_in() should have run before
1341 local_hash = dt_hash(local_hash, (const char *)&piece->roi_in, sizeof(dt_iop_roi_t));
1342 local_hash = dt_hash(local_hash, (const char *)&piece->roi_out, sizeof(dt_iop_roi_t));
1343
1344 local_hash = dt_hash(local_hash, (const char *)&piece->dsc_in, sizeof(dt_iop_buffer_dsc_t));
1345 local_hash = dt_hash(local_hash, (const char *)&piece->dsc_out, sizeof(dt_iop_buffer_dsc_t));
1346
1347/*
1348 fprintf(stdout, "start->end : %-17s | ROI in: %4ix%-4i @%2.4f | ROI out: %4ix%-4i @%2.4f\n", piece->module->op,
1349 piece->buf_in.width, piece->buf_in.height, piece->buf_in.scale, piece->buf_out.width,
1350 piece->buf_out.height, piece->buf_out.scale);
1351 fprintf(stdout, "end->start : %-17s | ROI in: %4ix%-4i @%2.4f | ROI out: %4ix%-4i @%2.4f\n", piece->module->op,
1352 piece->roi_in.width, piece->roi_in.height, piece->planned_roi_in.scale,
1353 piece->roi_out.width, piece->roi_out.height, piece->roi_out.scale);
1354*/
1355 // Mask preview display doesn't re-commit params, so we need to keep that of it here
1356 // Too much GUI stuff interleaved with pipeline stuff...
1357 // Mask display applies only to main preview in darkroom.
1358 if(pipe->type == DT_DEV_PIXELPIPE_FULL)
1359 {
1360 local_hash = dt_hash(local_hash, (const char *)&piece->module->request_mask_display, sizeof(int));
1361
1362 /* Mask-preview appearance is global GUI state, not module history. Hash
1363 * its revision at the module producing the active preview so upstream
1364 * cache entries remain reusable while this module and gamma are rerun. */
1365 if(pipe->dev->gui_attached
1366 && piece->module == pipe->dev->gui_module
1367 && piece->module->request_mask_display != DT_DEV_PIXELPIPE_DISPLAY_NONE)
1368 {
1369 const int revision = dt_atomic_get_int(&pipe->dev->mask_preview_settings_revision);
1370 local_hash = dt_hash(local_hash, (const char *)&revision, sizeof(revision));
1371 }
1372 }
1373 else
1374 {
1375 const int zero = 0;
1376 local_hash = dt_hash(local_hash, (const char *)&zero, sizeof(int));
1377 }
1378
1379 // Keep track of distortion bypass in GUI. That may affect upstream modules in the stack,
1380 // while bypass_cache only affects downstream ones.
1381 // In theory, distortion bypass should already affect planned ROI in/out, but it depends whether
1382 // internal params are committed. Anyway, make it more reliable.
1383 int bypass_distort = dt_dev_pixelpipe_activemodule_disables_currentmodule(pipe->dev, piece->module);
1384 local_hash = dt_hash(local_hash, (const char *)&bypass_distort, sizeof(int));
1385
1386 // If the cache bypass is on, the corresponding cache lines will be freed immediately after use,
1387 // we need to track that. It somewhat overlaps module->request_mask_display, but...
1388 local_hash = dt_hash(local_hash, (const char *)&piece->bypass_cache, sizeof(gboolean));
1389
1390 // Update global hash for this stage
1391 hash = dt_hash(hash, (const char *)&local_hash, sizeof(uint64_t));
1392
1394 {
1395 gchar *type = _get_debug_pipe_name(pipe, pipe->dev);
1396 dt_print(DT_DEBUG_PIPE, "[pixelpipe] global hash for %20s (%s) in pipe %s with hash %lu\n",
1397 piece->module->op, piece->module->multi_name, type, (long unsigned int)hash);
1398 dt_free(type);
1399 }
1400 // In case of drawn masks, we would need to account only for the distortions of previous modules.
1401 // Aka conditional to: if((piece->module->operation_tags() & IOP_TAG_DISTORT) == IOP_TAG_DISTORT)
1402 // But in case of parametric masks, they depend on previous modules parameters.
1403 // So, all in all, (parametric | drawn | raster) masking depends on everything :
1404 // - if masking on output, internal params + blendop params + all previous modules internal params + ROI size,
1405 // - if masking on input, blendop params + all previous modules internal params + ROI size
1406 // So we use all that ot once :
1407 piece->global_mask_hash = dt_hash(hash, (const char *)&piece->blendop_hash, sizeof(uint64_t));
1408
1409 // Finally, the output of the module also depends on the mask:
1410 hash = dt_hash(hash, (const char *)&piece->global_mask_hash, sizeof(uint64_t));
1411 piece->global_hash = hash;
1412
1413 if(pipe->type == DT_DEV_PIXELPIPE_FULL
1414 && pipe->dev->gui_attached
1415 && (piece->module == pipe->dev->gui_module)
1416 && (piece->module->request_mask_display != DT_DEV_PIXELPIPE_DISPLAY_NONE))
1417 {
1418 passthrough_preview = TRUE;
1419 }
1420 }
1421
1422 // The pipe hash is the hash of its last module.
1423 dt_dev_pixelpipe_set_hash(pipe, hash);
1424 pipe->bypass_cache = bypass_cache;
1425}
1426
1437void dt_dev_pixelpipe_synch_all_real(dt_dev_pixelpipe_t *pipe, const char *caller_func)
1438{
1439 gchar *type = _get_debug_pipe_name(pipe, pipe->dev);
1440 dt_print(DT_DEBUG_DEV, "[pixelpipe] synch all modules with history for pipe %s called from %s\n", type, caller_func);
1441
1442 const uint32_t history_end = dt_dev_get_history_end_ext(pipe->dev);
1443 _sync_pipe_nodes_from_history(pipe, pipe->dev, history_end, type);
1444
1445 // Keep track of the last history item to have been synced
1446 GList *last_item = g_list_nth(pipe->dev->history, history_end - 1);
1447 if(last_item)
1448 {
1449 dt_dev_history_item_t *last_hist = (dt_dev_history_item_t *)last_item->data;
1450 pipe->last_history_hash = last_hist->hash;
1451 pipe->last_history_item = last_hist;
1452 }
1453 else
1454 {
1456 pipe->last_history_item = NULL;
1457 }
1458
1459 dt_free(type);
1460}
1461
1463{
1464 gchar *type = _get_debug_pipe_name(pipe, pipe->dev);
1465
1466 dt_print(DT_DEBUG_DEV, "[pixelpipe] synch top modules with history for pipe %s\n", type);
1467
1468 const uint32_t history_end = dt_dev_get_history_end_ext(pipe->dev);
1469 GList *last_item = g_list_nth(pipe->dev->history, history_end - 1);
1470 if(last_item)
1471 {
1472 GList *first_item = NULL;
1473 for(GList *history = last_item; history; history = g_list_previous(history))
1474 {
1475 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1476 first_item = history;
1477
1478 if(hist->hash == pipe->last_history_hash || hist == pipe->last_history_item)
1479 break;
1480 }
1481
1482 GList *fence_item = g_list_nth(pipe->dev->history, history_end);
1483 for(GList *history = first_item; history && history != fence_item; history = g_list_next(history))
1484 {
1485 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1486 if(!hist || !hist->module) continue;
1487 dt_print(DT_DEBUG_PARAMS, "[pixelpipe] synch top history module `%s` (%s) for pipe %s\n",
1488 hist->module->op, hist->module->multi_name, type);
1489 for(GList *nodes = g_list_last(pipe->nodes); nodes; nodes = g_list_previous(nodes))
1490 {
1492 if(IS_NULL_PTR(piece) || piece->module != hist->module) continue;
1493
1494 _sync_pipe_nodes_from_history_from_node(pipe, history_end, nodes, type);
1495 break;
1496 }
1497 }
1498
1499 dt_dev_history_item_t *last_hist = (dt_dev_history_item_t *)last_item->data;
1500 pipe->last_history_hash = last_hist->hash;
1501 pipe->last_history_item = last_hist;
1502 }
1503 else
1504 {
1505 dt_print(DT_DEBUG_DEV, "[pixelpipe] synch top history module missing error for pipe %s\n", type);
1507 pipe->last_history_item = NULL;
1508 }
1509
1510 dt_free(type);
1511}
1512
1513// Modules without history need to be resynced unconditionnally with their internal params
1514// because some of them are self-enabled/disabled from commit_params() methods
1516{
1517 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
1518 {
1520 if(IS_NULL_PTR(piece) || IS_NULL_PTR(piece->module)) continue;
1521 dt_iop_module_t *module = piece->module;
1522 if(module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
1523 dt_iop_commit_params(module, module->default_params, module->default_blendop_params, pipe, piece);
1524 }
1525}
1526
1528{
1529 dt_times_t start;
1530 dt_get_times(&start);
1531
1542
1543 gchar *type = _get_debug_pipe_name(pipe, pipe->dev);
1544 char *status_str = g_strdup_printf("%s%s%s%s%s%s%s",
1545 (status & DT_DEV_PIPE_UNCHANGED) ? "UNCHANGED " : "",
1546 (status & DT_DEV_PIPE_REMOVE) ? "REMOVE " : "",
1547 (status & DT_DEV_PIPE_TOP_CHANGED) ? "TOP_CHANGED " : "",
1548 (status & DT_DEV_PIPE_SYNCH) ? "SYNCH " : "",
1549 (status & DT_DEV_PIPE_ZOOMED) ? "ZOOMED " : "",
1550 (status & DT_DEV_PIPE_CACHE_REQUEST) ? "CACHE_REQUEST " : "",
1551 (status & DT_DEV_PIPE_REENTRY) ? "REENTRY " : "");
1552
1553 dt_print(DT_DEBUG_DEV, "[dt_dev_pixelpipe_change] pipeline state changing for pipe %s, flag %s\n",
1554 type, status_str);
1555
1556 dt_free(status_str);
1557
1558 // mask display off as a starting point
1560 pipe->bypass_blendif = 0;
1561
1562 /* Zoom/pan only replans ROI and hashes, it does not replay history sync.
1563 * Rebuild the aggregate detail-mask demand from the already synchronized
1564 * pieces in that case, otherwise the pipe forgets that the hidden
1565 * `detailmask` stage is needed before the next processing run starts. */
1568 else
1570
1572
1573 // The realtime in-place path settles the format contract and the global hash itself (it must,
1574 // since that hash is cumulative over enabled nodes); the other paths defer it to the
1575 // unconditional pass below.
1576 gboolean formats_propagated = FALSE;
1577
1578 // case DT_DEV_PIPE_UNCHANGED: case DT_DEV_PIPE_ZOOMED:
1579 if(status & DT_DEV_PIPE_REMOVE)
1580 {
1581 // modules have been added in between or removed. need to rebuild the whole pipeline.
1582 if(pipe->nodes) dt_dev_pixelpipe_cleanup_nodes(pipe);
1586 }
1587 else if(status & DT_DEV_PIPE_SYNCH)
1588 {
1589 // pipeline topology remains intact, only change all params.
1592 }
1593 else if(status & DT_DEV_PIPE_TOP_CHANGED)
1594 {
1595 // only top history item(s) changed, or a focused module published transient/realtime params
1596 if(_sync_focused_in_place(pipe))
1597 {
1598 formats_propagated = TRUE;
1599 }
1600 else
1601 {
1604 }
1605 }
1606 else // DT_DEV_PIPE_ZOOMED DT_DEV_PIPE_CACHE_REQUEST DT_DEV_PIPE_REENTRY
1607 {
1608 // Finalscale will need to self-enable/disable depending on zoom level
1610 }
1613
1614 // Re-establish the buffer-format contract of the whole chain once, after whichever sync path
1615 // (full, top-only or realtime-in-place) committed params. This is the single authoritative,
1616 // direction-independent place where per-node dsc_in/dsc_out are settled and incompatible
1617 // modules are disabled (issue #733), so the OpenCL cache policy and ROI planning below see a
1618 // consistent contract. The realtime path already did this before computing its global hash.
1619 if(!formats_propagated)
1621
1623
1624 // Update theoritical final scale based on distorting modules
1625 // This also writes piece->buf_in/out for each pipe->nodes piece,
1626 // so it's not nearly a matter of getting processed_width/height
1628 &pipe->processed_height);
1629
1630 dt_show_times_f(&start, "[dev_pixelpipe] pipeline resync with history", "for pipe %s", type);
1631 dt_free(type);
1632}
1633
1635{
1636 // Virtual pipe exists only for GUI geometry (ROI/mask transforms) and never processes pixels.
1637 if(IS_NULL_PTR(dev) || !dev->gui_attached || IS_NULL_PTR(dev->virtual_pipe)) return;
1638 if(!dev->roi.raw_inited || dev->image_storage.id <= 0) return;
1639
1640 // Ensure its input image metadata matches the current dev state.
1641 if(dev->virtual_pipe->imgid != dev->image_storage.id
1642 || dev->virtual_pipe->iwidth != dev->roi.raw_width
1643 || dev->virtual_pipe->iheight != dev->roi.raw_height
1644 || dev->virtual_pipe->dev->image_storage.id != dev->image_storage.id)
1645 {
1647 dev->roi.raw_width, dev->roi.raw_height, 1.0f, DT_MIPMAP_FULL);
1648 }
1649
1650 // Mirror the preview-pipe change flags and commit immediately.
1653}
1654
1659
1667
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_atomic_set_int(dt_atomic_int *var, int value)
int dt_atomic_get_int(dt_atomic_int *var)
int dt_atomic_exch_int(dt_atomic_int *var, int value)
atomic_int dt_atomic_int
Definition atomic.h:66
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
@ IOP_CS_RAW
@ IOP_CS_LCH
@ IOP_CS_JZCZHZ
@ IOP_CS_RGB
@ IOP_CS_HSL
@ IOP_CS_LAB
@ IOP_CS_NONE
gboolean dt_iop_color_picker_force_cache(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module)
int type
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
int dt_conf_key_exists(const char *key)
gboolean dt_conf_key_not_empty(const char *name)
void dt_control_log(const char *msg,...)
Definition control.c:761
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:861
void dt_control_change_cursor_by_name_and_flush(const char *curs_str)
Apply a named cursor immediatelly and flush display updates for immediate feedback.
Definition control.c:326
void dt_control_commit_cursor()
Definition control.c:334
void dt_control_navigation_redraw()
request redraw of the navigation widget. This redraws the wiget of the navigation module.
Definition control.c:866
darktable_t darktable
Definition darktable.c:181
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...)
Definition darktable.c:1594
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_PIPE
Definition darktable.h:741
@ DT_DEBUG_PIPECACHE
Definition darktable.h:720
@ DT_DEBUG_VERBOSE
Definition darktable.h:743
@ DT_DEBUG_PARAMS
Definition darktable.h:736
@ DT_DEBUG_DEV
Definition darktable.h:717
#define dt_free(ptr)
Definition darktable.h:456
static void dt_get_times(dt_times_t *t)
Definition darktable.h:921
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition darktable.h:1043
#define DT_MAX_FILENAME_LEN
Definition darktable.h:1052
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
gboolean dt_dev_transient_params_get(dt_develop_t *dev, const dt_iop_module_t *module, void *out_params, const size_t out_params_size, void *out_blend, const size_t out_blend_size, gboolean *out_has_blend)
dt_dev_history_item_t * dt_dev_history_get_last_item_by_module(GList *history_list, dt_iop_module_t *module, int history_end)
Find the last history item referencing a module up to history_end.
gboolean dt_dev_transient_params_active(dt_develop_t *dev, const dt_iop_module_t *module)
int32_t dt_dev_get_history_end_ext(struct dt_develop_t *dev)
Get the current history end index (GUI perspective).
Definition develop.c:1659
void dt_iop_params_t
Definition dev_history.h:41
static void _sync_pipe_nodes_from_history_from_node(dt_dev_pixelpipe_t *pipe, const uint32_t history_end, GList *start_node, const char *debug_label)
void dt_dev_pixelpipe_propagate_formats(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_update_zoom_main_real(dt_develop_t *dev)
static gboolean _cache_wait_cursor_restore(gpointer user_data)
static void _prepare_piece_input_contract(dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, const dt_iop_buffer_dsc_t *upstream_dsc)
void dt_dev_pixelpipe_update_history_all_real(dt_develop_t *dev)
const dt_dev_pixelpipe_iop_t * dt_dev_pixelpipe_get_module_piece(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module)
void dt_dev_pixelpipe_update_history_main_real(dt_develop_t *dev)
void dt_dev_pixelpipe_synch_top(dt_dev_pixelpipe_t *pipe)
static void _refresh_pipe_detail_mask_state(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_cache_wait_cleanup(dt_dev_pixelpipe_cache_wait_t *wait, const char *reason)
Cancel one pending GUI cache wait request and clear its runtime state.
uint64_t dt_dev_pixelpipe_node_hash(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t roi_out, const int pos)
const dt_dev_pixelpipe_iop_t * dt_dev_pixelpipe_get_prev_enabled_piece(const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
static dt_dev_pixelpipe_cache_wait_record_t * _cache_wait_manager_remove_wait_locked(dt_dev_pixelpipe_cache_wait_t *wait)
void dt_pixelpipe_get_global_hash(dt_dev_pixelpipe_t *pipe)
gboolean dt_dev_pixelpipe_is_backbufer_valid(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_reset_all(dt_develop_t *dev)
static void _commit_piece_contract(dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_params_t *params, dt_develop_blend_params_t *blend_params, dt_iop_buffer_dsc_t *upstream_dsc)
void dt_dev_pixelpipe_get_roi_out(dt_dev_pixelpipe_t *pipe, const int width_in, const int height_in, int *width, int *height)
static void _sync_pipe_nodes_from_history(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev, const uint32_t history_end, const char *debug_label)
void dt_dev_pixelpipe_change(dt_dev_pixelpipe_t *pipe)
static const char * _dsc_cst_name(const int cst)
void dt_dev_pixelpipe_cache_wait_dump_pending(const char *reason)
Dump pending GUI cache wait requests for lifecycle debugging.
static gchar * _get_debug_pipe_name(const dt_dev_pixelpipe_t *pipe, const dt_develop_t *dev)
static void _change_pipe(dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_change_t flag)
void dt_dev_pixelpipe_change_zoom_main(dt_develop_t *dev)
void dt_dev_pixelpipe_synch_all_real(dt_dev_pixelpipe_t *pipe, const char *caller_func)
Find the last history item matching each pipeline node (module), in the order of pipeline execution....
void dt_dev_pixelpipe_sync_no_history(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_sync_virtual(dt_develop_t *dev, dt_dev_pixelpipe_change_t flag)
static GList * _find_detailmask_node(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_rebuild_all_real(dt_develop_t *dev)
static void _sync_virtual_pipe(dt_develop_t *dev, dt_dev_pixelpipe_change_t flag)
static gboolean _sync_focused_in_place(dt_dev_pixelpipe_t *pipe)
Re-commit the focused module's piece in place from transient (or realtime) params.
void dt_dev_pixelpipe_cache_wait_set_owner(dt_dev_pixelpipe_cache_wait_t *wait, const char *owner_tag, gpointer owner_object)
Attach debug ownership metadata to one cache wait request.
void dt_dev_pixelpipe_get_roi_in(dt_dev_pixelpipe_t *pipe, const struct dt_iop_roi_t roi_out)
static void _dt_dev_pixelpipe_cache_wait_ready_callback(gpointer instance, const guint64 hash, gpointer user_data)
Serve queued GUI cache waiters matching one published cacheline hash.
static void _seal_opencl_cache_policy(dt_dev_pixelpipe_t *pipe)
Seal host-cache retention policy on synchronized pieces before processing starts.
void dt_dev_pixelpipe_update_history_preview_real(dt_develop_t *dev)
void dt_dev_pixelpipe_update_zoom_preview_real(dt_develop_t *dev)
gboolean dt_dev_pixelpipe_is_pipeline_valid(dt_dev_pixelpipe_t *pipe)
static uint64_t _default_pipe_hash(dt_dev_pixelpipe_t *pipe)
static gboolean _cache_wait_cursor_progress(gpointer user_data)
void dt_dev_pixelpipe_resync_history_all_real(dt_develop_t *dev)
static dt_dev_pixelpipe_cache_wait_manager_t _cache_wait_manager
gboolean dt_dev_pixelpipe_activemodule_disables_currentmodule(struct dt_develop_t *dev, struct dt_iop_module_t *current_module)
static gboolean _module_requires_global_histogram_output_cache(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module)
void dt_dev_pixelpipe_resync_history_main_real(dt_develop_t *dev)
void dt_dev_pixelpipe_resync_history_preview_real(dt_develop_t *dev)
static gboolean _module_requires_global_histogram_input_cache(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module)
gboolean dt_dev_pixelpipe_cache_peek_gui(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, void **data, dt_pixel_cache_entry_t **cache_entry, dt_dev_pixelpipe_cache_wait_t *wait, dt_dev_pixelpipe_cache_ready_callback_t restart, gpointer restart_data)
#define dt_dev_pixelpipe_rebuild_all(dev)
void(* dt_dev_pixelpipe_cache_ready_callback_t)(gpointer user_data)
#define dt_dev_pixelpipe_resync_history_preview(dev)
#define dt_dev_pixelpipe_resync_history_main(dev)
#define dt_dev_pixelpipe_update_history_main(dev)
#define dt_dev_pixelpipe_update_zoom_main(dev)
#define dt_dev_pixelpipe_update_history_preview(dev)
void dt_dev_update_mouse_effect_radius(dt_develop_t *dev)
Convert absolute output-image coordinates to input image space by calling dt_dev_coordinates_image_ab...
Definition develop.c:1869
static uint64_t dt_dev_get_history_hash(const dt_develop_t *dev)
Definition develop.h:504
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:117
@ DT_DEV_DETAIL_MASK_ENABLED
Definition develop.h:145
@ DT_DEV_DETAIL_MASK_NONE
Definition develop.h:144
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_mutex_trylock(dt_pthread_mutex_t *mutex) TRY_ACQUIRE(0
#define dt_pthread_rwlock_unlock
Definition dtpthread.h:392
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
#define dt_pthread_rwlock_rdlock
Definition dtpthread.h:393
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
Definition format.c:28
@ TYPE_FLOAT
Definition format.h:46
GtkWidget * dt_ui_center(dt_ui_t *ui)
get the center drawable widget
const char flag
Definition image.h:252
void dt_iop_commit_params(dt_iop_module_t *module, dt_iop_params_t *params, dt_develop_blend_params_t *blendop_params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition imageop.c:1913
gboolean dt_iop_get_cache_bypass(dt_iop_module_t *module)
Definition imageop.c:2910
@ IOP_FLAGS_NO_HISTORY_STACK
Definition imageop.h:174
@ IOP_CS_RGB_DISPLAY
Definition imageop.h:210
@ IOP_TAG_DISTORT
Definition imageop.h:151
@ DT_MIPMAP_FULL
int dt_opencl_is_inited(void)
Definition opencl.c:2730
@ DT_REQUEST_ON
Definition pixelpipe.h:48
@ DT_REQUEST_ONLY_IN_GUI
Definition pixelpipe.h:49
@ DT_DEV_PIXELPIPE_NONE
Definition pixelpipe.h:37
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:40
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:39
void dt_dev_pixelpipe_cache_flush(dt_dev_pixelpipe_cache_t *cache, const int id)
Remove cache lines matching id. Entries locked in read/write or having reference count greater than 0...
gboolean dt_dev_pixelpipe_cache_peek(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, void **data, dt_pixel_cache_entry_t **entry, const int preferred_devid, void **cl_mem_output)
Non-owning lookup of an existing cache line.
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
void dt_dev_pixelpipe_set_input(dt_dev_pixelpipe_t *pipe, int32_t imgid, int width, int height, float iscale, dt_mipmap_size_t size)
gboolean dt_dev_pixelpipe_get_realtime(const dt_dev_pixelpipe_t *pipe)
char * dt_pixelpipe_get_pipe_name(dt_dev_pixelpipe_type_t pipe_type)
void dt_dev_pixelpipe_create_nodes(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_set_realtime(dt_dev_pixelpipe_t *pipe, gboolean state)
void dt_dev_pixelpipe_cleanup_nodes(dt_dev_pixelpipe_t *pipe)
static void dt_dev_pixelpipe_set_hash(dt_dev_pixelpipe_t *pipe, const uint64_t hash)
static uint64_t dt_dev_backbuf_get_history_hash(const dt_backbuf_t *backbuf)
static uint64_t dt_dev_pixelpipe_get_hash(const dt_dev_pixelpipe_t *pipe)
static void dt_dev_pixelpipe_or_changed(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_change_t flags)
static void dt_dev_pixelpipe_set_cache_request(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_cache_request_t request, const struct dt_iop_module_t *module)
dt_dev_pixelpipe_change_t
@ DT_DEV_PIPE_REENTRY
@ DT_DEV_PIPE_ZOOMED
@ DT_DEV_PIPE_CACHE_REQUEST
@ DT_DEV_PIPE_SYNCH
@ DT_DEV_PIPE_TOP_CHANGED
@ DT_DEV_PIPE_REMOVE
@ DT_DEV_PIPE_UNCHANGED
static uint64_t dt_dev_pixelpipe_get_history_hash(const dt_dev_pixelpipe_t *pipe)
static void dt_dev_pixelpipe_set_history_hash(dt_dev_pixelpipe_t *pipe, const uint64_t history_hash)
#define dt_dev_pixelpipe_synch_all(pipe)
static uint64_t dt_dev_backbuf_get_hash(const dt_backbuf_t *backbuf)
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_BACKBUF
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:368
@ DT_SIGNAL_CACHELINE_READY
This signal is raised when one cacheline write lock is released. 1 : uint64_t cacheline hash no retur...
Definition signal.h:185
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:357
unsigned __int64 uint64_t
Definition strptime.c:75
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:790
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_control_signal_t * signals
Definition darktable.h:774
int32_t unmuted
Definition darktable.h:760
struct dt_control_t * control
Definition darktable.h:773
dt_iop_params_t * params
Definition dev_history.h:52
struct dt_develop_blend_params_t * blend_params
Definition dev_history.h:55
struct dt_iop_module_t *gboolean enabled
Definition dev_history.h:50
dt_dev_pixelpipe_cache_wait_t * wait
struct dt_dev_pixelpipe_t * pipe
const struct dt_iop_module_t *uint64_t hash
dt_dev_pixelpipe_cache_ready_callback_t restart
dt_iop_buffer_dsc_t dsc_out
dt_dev_request_flags_t request_histogram
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
gpointer last_history_item
gboolean gui_observable_source
uint64_t last_history_hash
dt_backbuf_t backbuf
dt_atomic_int shutdown
dt_dev_pixelpipe_type_t type
dt_atomic_int changed
struct dt_develop_t * dev
int32_t gui_attached
Definition develop.h:162
dt_image_t image_storage
Definition develop.h:259
int32_t raw_height
Definition develop.h:228
struct dt_iop_module_t * gui_module
Definition develop.h:165
dt_pthread_rwlock_t history_mutex
Definition develop.h:263
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:247
GList * history
Definition develop.h:275
int32_t raw_width
Definition develop.h:228
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
dt_atomic_int mask_preview_settings_revision
Revision of the global mask-preview appearance.
Definition develop.h:176
struct dt_develop_t::@17 roi
gboolean raw_inited
Definition develop.h:239
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
int32_t reset
Definition gtk.h:172
dt_ui_t * ui
Definition gtk.h:164
dt_iop_buffer_dsc_t dsc
Definition image.h:337
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
int32_t id
Definition image.h:319
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
uint8_t xtrans[6][6]
Definition format.h:70
dt_iop_buffer_type_t datatype
Definition format.h:56
GModule *dt_dev_operation_t op
Definition imageop.h:256
int32_t params_size
Definition imageop.h:309
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
#define MAX(a, b)
Definition thinplate.c:29