Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
pixelpipe_hb.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2016 johannes hanika.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2011 Bruce Guenter.
6 Copyright (C) 2011 Robert Bieber.
7 Copyright (C) 2011 Rostyslav Pidgornyi.
8 Copyright (C) 2011-2017, 2019 Ulrich Pegelow.
9 Copyright (C) 2012, 2021 Aldric Renaudin.
10 Copyright (C) 2012 Richard Wonka.
11 Copyright (C) 2012-2019 Tobias Ellinghaus.
12 Copyright (C) 2013-2016 Roman Lebedev.
13 Copyright (C) 2013 Simon Spannagel.
14 Copyright (C) 2014, 2016 Pedro Côrte-Real.
15 Copyright (C) 2016 Matthieu Moy.
16 Copyright (C) 2017, 2019 luzpaz.
17 Copyright (C) 2018, 2020-2026 Aurélien PIERRE.
18 Copyright (C) 2018-2019 Edgardo Hoszowski.
19 Copyright (C) 2018-2022 Pascal Obry.
20 Copyright (C) 2019 Andreas Schneider.
21 Copyright (C) 2019-2022 Dan Torop.
22 Copyright (C) 2019-2022 Hanno Schwalm.
23 Copyright (C) 2019 Heiko Bauke.
24 Copyright (C) 2020 Chris Elston.
25 Copyright (C) 2020 Diederik Ter Rahe.
26 Copyright (C) 2020 GrahamByrnes.
27 Copyright (C) 2020-2021 Harold le Clément de Saint-Marcq.
28 Copyright (C) 2020-2021 Hubert Kowalski.
29 Copyright (C) 2020-2021 Ralf Brown.
30 Copyright (C) 2021 Sakari Kapanen.
31 Copyright (C) 2022 Martin Bařinka.
32 Copyright (C) 2022 Philipp Lutz.
33 Copyright (C) 2023-2024 Alynx Zhou.
34 Copyright (C) 2023 lologor.
35 Copyright (C) 2023 Luca Zulberti.
36 Copyright (C) 2024 Alban Gruin.
37 Copyright (C) 2024 tatu.
38 Copyright (C) 2025-2026 Guillaume Stutin.
39 Copyright (C) 2025 Miguel Moquillon.
40
41 darktable is free software: you can redistribute it and/or modify
42 it under the terms of the GNU General Public License as published by
43 the Free Software Foundation, either version 3 of the License, or
44 (at your option) any later version.
45
46 darktable is distributed in the hope that it will be useful,
47 but WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49 GNU General Public License for more details.
50
51 You should have received a copy of the GNU General Public License
52 along with darktable. If not, see <http://www.gnu.org/licenses/>.
53*/
56#include "system/capabilities.h"
59#include "common/hash.h"
60#include "common/histogram.h"
62#include "system/atomic.h"
63#include "common/opencl.h"
64#include "develop/iop_order.h"
65#include "control/signal.h"
66#include "develop/blend.h"
68#include "develop/dev_history.h"
69#include "pixel/format.h"
71#include "common/sentry.h"
72#include "common/telemetry.h"
73#include "develop/pixelpipe.h"
75#include "develop/supervisor.h"
79#include "develop/tiling.h"
80#include "develop/masks.h"
81
82#include <assert.h>
83#include <inttypes.h>
84#include <math.h>
85#include <stdint.h>
86#include <stdlib.h>
87#include <string.h>
88#include <strings.h>
89#include <unistd.h>
90
93
94static void _trace_cache_owner(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module,
95 const char *phase, const char *slot, const uint64_t requested_hash,
96 const void *buffer, const dt_pixel_cache_entry_t *entry,
97 const gboolean verbose)
98{
99 if(!(dt_get_debug_flags() & DT_DEBUG_PIPECACHE)) return;
100 if(!(dt_get_debug_flags() & DT_DEBUG_VERBOSE)) return;
101
103 "[pixelpipe_owner] pipe=%s module=%s phase=%s slot=%s req=%" PRIu64
104 " entry=%" PRIu64 "/%" PRIu64 " refs=%i auto=%i data=%p buf=%p name=%s\n",
105 pipe ? dt_pixelpipe_get_pipe_name(pipe->type) : "-",
106 module ? module->op : "base",
107 phase ? phase : "-",
108 slot ? slot : "-",
109 requested_hash,
110 entry ? entry->hash : DT_PIXELPIPE_CACHE_HASH_INVALID,
111 entry ? entry->serial : 0,
112 entry ? dt_atomic_get_int((dt_atomic_int *)&entry->refcount) : -1,
113 entry ? entry->auto_destroy : -1,
114 entry ? entry->data : NULL,
115 buffer,
116 (entry && entry->name) ? entry->name : "-");
117}
118
119
120static void _trace_buffer_content(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module,
121 const char *phase, const void *buffer,
122 const dt_iop_buffer_dsc_t *format, const dt_iop_roi_t *roi)
123{
124 if(!(dt_get_debug_flags() & DT_DEBUG_PIPECACHE)) return;
125 if(!(dt_get_debug_flags() & DT_DEBUG_VERBOSE)) return;
126 if(IS_NULL_PTR(buffer) || IS_NULL_PTR(format) || IS_NULL_PTR(roi)) return;
127 if(roi->width <= 0 || roi->height <= 0) return;
128
129 const size_t pixels = (size_t)roi->width * (size_t)roi->height;
130 const unsigned int channels = format->channels;
131
132 if(format->datatype == TYPE_FLOAT && channels >= 1)
133 {
134 const float *in = (const float *)buffer;
135 float minv[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
136 float maxv[4] = { -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX };
137 size_t nonfinite = 0;
138 size_t near_black = 0;
139
140 for(size_t k = 0; k < pixels; k++, in += channels)
141 {
142 gboolean finite = TRUE;
143 for(unsigned int c = 0; c < MIN(channels, 4U); c++)
144 {
145 if(!isfinite(in[c]))
146 {
147 finite = FALSE;
148 continue;
149 }
150 minv[c] = fminf(minv[c], in[c]);
151 maxv[c] = fmaxf(maxv[c], in[c]);
152 }
153
154 if(!finite)
155 {
156 nonfinite++;
157 continue;
158 }
159
160 const float energy = fabsf(in[0]) + ((channels > 1) ? fabsf(in[1]) : 0.0f)
161 + ((channels > 2) ? fabsf(in[2]) : 0.0f);
162 if(energy < 1e-6f) near_black++;
163 }
164
165 const uint64_t content_hash = dt_hash(5381, (const char *)buffer, pixels * channels * sizeof(float));
166
168 "[pixelpipe_stats] pipe=%s module=%s phase=%s type=float ch=%u roi=%dx%d ptr=%p "
169 "content_hash=%" PRIu64 " "
170 "rgb_min=(%g,%g,%g) rgb_max=(%g,%g,%g) a_min=%g a_max=%g near_black=%" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT " nonfinite=%" G_GSIZE_FORMAT "\n",
171 dt_pixelpipe_get_pipe_name(pipe->type), module->op, phase ? phase : "-",
172 channels, roi->width, roi->height, buffer, content_hash,
173 minv[0], (channels > 1) ? minv[1] : 0.0f, (channels > 2) ? minv[2] : 0.0f,
174 maxv[0], (channels > 1) ? maxv[1] : 0.0f, (channels > 2) ? maxv[2] : 0.0f,
175 (channels > 3) ? minv[3] : 0.0f, (channels > 3) ? maxv[3] : 0.0f,
176 near_black, pixels, nonfinite);
177 }
178 else if(format->datatype == TYPE_UINT8 && channels >= 1)
179 {
180 const uint8_t *in = (const uint8_t *)buffer;
181 int minv[4] = { 255, 255, 255, 255 };
182 int maxv[4] = { 0, 0, 0, 0 };
183 size_t near_black = 0;
184
185 for(size_t k = 0; k < pixels; k++, in += channels)
186 {
187 for(unsigned int c = 0; c < MIN(channels, 4U); c++)
188 {
189 minv[c] = MIN(minv[c], in[c]);
190 maxv[c] = MAX(maxv[c], in[c]);
191 }
192
193 const int energy = in[0] + ((channels > 1) ? in[1] : 0) + ((channels > 2) ? in[2] : 0);
194 if(energy == 0) near_black++;
195 }
196
197 const uint64_t content_hash = dt_hash(5381, (const char *)buffer, pixels * channels * sizeof(uint8_t));
198
200 "[pixelpipe_stats] pipe=%s module=%s phase=%s type=u8 ch=%u roi=%dx%d ptr=%p "
201 "content_hash=%" PRIu64 " "
202 "rgb_min=(%d,%d,%d) rgb_max=(%d,%d,%d) a_min=%d a_max=%d near_black=%" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT "\n",
203 dt_pixelpipe_get_pipe_name(pipe->type), module->op, phase ? phase : "-",
204 channels, roi->width, roi->height, buffer, content_hash,
205 minv[0], (channels > 1) ? minv[1] : 0, (channels > 2) ? minv[2] : 0,
206 maxv[0], (channels > 1) ? maxv[1] : 0, (channels > 2) ? maxv[2] : 0,
207 (channels > 3) ? minv[3] : 0, (channels > 3) ? maxv[3] : 0,
208 near_black, pixels);
209 }
210}
211
213 dt_iop_module_t *module, const uint64_t input_hash,
214 const void *input, dt_pixel_cache_entry_t *input_entry,
215 const uint64_t output_hash, void **output,
216 void **cl_mem_output, dt_pixel_cache_entry_t *output_entry)
217{
218 _trace_cache_owner(pipe, module, "shutdown-drop", "input", input_hash, input, input_entry, FALSE);
219 _trace_cache_owner(pipe, module, "shutdown-drop", "output", output_hash,
220 output ? *output : NULL, output_entry, FALSE);
221
223
224 if(!IS_NULL_PTR(input_entry))
225 {
228 }
229
230 if(!IS_NULL_PTR(output_entry))
231 {
233
234 if(dt_dev_pixelpipe_cache_remove(TRUE, output_entry))
236 }
237
238 if(output) *output = NULL;
239
240 if(!IS_NULL_PTR(*cl_mem_output))
241 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_output, NULL, NULL, FALSE);
242
243 return 1;
244}
245
246static inline gboolean _is_focused_realtime_gui_module(const dt_dev_pixelpipe_t *pipe,
247 const dt_develop_t *dev,
248 const dt_iop_module_t *module)
249{
250 return (pipe->type == DT_DEV_PIXELPIPE_FULL || pipe->type == DT_DEV_PIXELPIPE_PREVIEW)
251 && pipe->realtime && dev && module && dev->gui_module == module;
252}
253
254
256 const dt_pixel_cache_entry_t *cache_entry)
257{
258 return (pipe->realtime
259 || (cache_entry && IS_NULL_PTR(dt_pixel_cache_entry_get_data((dt_pixel_cache_entry_t *)cache_entry)))
260 || !_bypass_cache(pipe, NULL));
261}
262
264{
265 char *r = NULL;
266
267 switch(pipe_type)
268 {
270 r = _("preview");
271 break;
273 r = _("full");
274 break;
276 r = _("thumbnail");
277 break;
279 r = _("export");
280 break;
281 default:
282 r = _("invalid");
283 }
284 return r;
285}
286
287
288inline static void _uint8_to_float(const uint8_t *const input, float *const output,
289 const size_t width, const size_t height, const size_t chan)
290{
291 __OMP_FOR_SIMD__(aligned(input, output: 64) )
292 for(size_t k = 0; k < height * width; k++)
293 {
294 const size_t index = k * chan;
295 // Warning: we take BGRa and put it back into RGBa
296 output[index + 0] = (float)input[index + 2] / 255.f;
297 output[index + 1] = (float)input[index + 1] / 255.f;
298 output[index + 2] = (float)input[index + 0] / 255.f;
299 output[index + 3] = 0.f;
300 }
301}
302
303static const char *_debug_cst_to_string(const int cst)
304{
305 switch(cst)
306 {
307 case IOP_CS_RAW:
308 return "raw";
309 case IOP_CS_LAB:
310 return "lab";
311 case IOP_CS_RGB:
312 return "rgb";
314 return "display-rgb";
315 case IOP_CS_LCH:
316 return "lch";
317 case IOP_CS_HSL:
318 return "hsl";
319 case IOP_CS_JZCZHZ:
320 return "jzczhz";
321 case IOP_CS_NONE:
322 return "none";
323 default:
324 return "unknown";
325 }
326}
327
329{
330 switch(type)
331 {
332 case TYPE_FLOAT:
333 return "float";
334 case TYPE_UINT16:
335 return "uint16";
336 case TYPE_UINT8:
337 return "uint8";
338 case TYPE_UNKNOWN:
339 default:
340 return "unknown";
341 }
342}
343
345 const gboolean is_cl,
346 const dt_iop_buffer_dsc_t *in_dsc, const dt_iop_buffer_dsc_t *out_dsc,
347 const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out,
348 const size_t in_bpp, const size_t out_bpp,
349 const int cst_before, const int cst_after)
350{
351 if(!(dt_get_debug_flags() & DT_DEBUG_PIPE)) return;
352 if(!(dt_get_debug_flags() & DT_DEBUG_VERBOSE)) return;
353 const char *module_name = module ? module->op : "base";
354 const char *pipe_name = dt_pixelpipe_get_pipe_name(pipe->type);
355 const char *stage_name = stage ? stage : "process";
356
357 if(!IS_NULL_PTR(in_dsc) && !IS_NULL_PTR(out_dsc))
358 {
360 "[pixelpipe] %s %s %s %s: in cst=%s->%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d | "
361 "out cst=%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d\n",
362 pipe_name, module_name, is_cl ? "cl" : "cpu", stage_name,
363 _debug_cst_to_string(cst_before), _debug_cst_to_string(cst_after),
364 in_dsc->channels, _debug_type_to_string(in_dsc->datatype), in_bpp,
365 roi_in ? roi_in->width : 0, roi_in ? roi_in->height : 0,
366 _debug_cst_to_string(out_dsc->cst), out_dsc->channels, _debug_type_to_string(out_dsc->datatype),
367 out_bpp, roi_out ? roi_out->width : 0, roi_out ? roi_out->height : 0);
368 }
369 else if(!IS_NULL_PTR(out_dsc))
370 {
372 "[pixelpipe] %s %s %s %s: out cst=%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d\n",
373 pipe_name, module_name, is_cl ? "cl" : "cpu", stage_name,
374 _debug_cst_to_string(out_dsc->cst), out_dsc->channels, _debug_type_to_string(out_dsc->datatype),
375 out_bpp, roi_out ? roi_out->width : 0, roi_out ? roi_out->height : 0);
376 }
377}
378
379
380int dt_dev_pixelpipe_init_export(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev, int levels, gboolean store_masks)
381{
382 const int res = dt_dev_pixelpipe_init_cached(pipe);
385 pipe->levels = levels;
386 pipe->store_all_raster_masks = store_masks;
387 pipe->dev = dev;
388 return res;
389}
390
392{
393 const int res = dt_dev_pixelpipe_init_cached(pipe);
395 pipe->no_cache = TRUE;
396 pipe->dev = dev;
397 return res;
398}
399
401{
402 const int res = dt_dev_pixelpipe_init_cached(pipe);
404 pipe->no_cache = TRUE;
405 pipe->dev = dev;
406 return res;
407}
408
410{
411 // Init with the size of MIPMAP_F
412 const int res = dt_dev_pixelpipe_init_cached(pipe);
415
416 // Needed for caching
418 pipe->dev = dev;
419 return res;
420}
421
433{
434 if(IS_NULL_PTR(pipe)) return;
435
436 pipe->mask_rasterization_step = MAX(1, step);
437}
438
440{
441 const int res = dt_dev_pixelpipe_init_cached(pipe);
443
444 // Needed for caching
446 pipe->dev = dev;
447 return res;
448}
449
451{
452 // Set everything to 0 = NULL = FALSE
453 memset(pipe, 0, sizeof(dt_dev_pixelpipe_t));
454
455 // Set only the stuff that doesn't take 0 as default
457 pipe->devid = -1;
458 pipe->last_devid = -1;
459
460 /* Pixel accuracy, the safe default: only the darkroom ever raises this, from the GUI thread.
461 * It is set HERE, in the one function every pipe init calls, and not in each of them: the
462 * per-type version of this missed dt_dev_pixelpipe_init_dummy(), which gui/dtgtk/focus.h uses
463 * for the focus-ring overlay, and left it at the memset's 0. Zero does not divide by anything
464 * -- `use_sparse' is `step > 1' and the interpolation loop is `for(k = 1; k < step; k++)', so
465 * both are simply skipped -- but it makes _is_within_pxl_threshold() test `< 0', which is
466 * never true, so the outline subdivides until `tmax - tmin < 0.0001' instead of stopping at
467 * one pixel: thousands of samples per segment, on an overlay that wanted the cheap path. */
468 pipe->mask_rasterization_step = 1;
472 dt_dev_set_backbuf(&pipe->backbuf, 0, 0, 0, -1, -1);
475 pipe->raster_mask_hashes = g_array_new(FALSE, FALSE, sizeof(uint64_t));
476
478 pipe->iscale = 1.0f;
482
484 dt_pthread_mutex_init(&(pipe->busy_mutex), NULL);
485
488
490 return 1;
491}
492
494{
495 if(IS_NULL_PTR(pipe)) return;
497}
498
500{
501 return pipe ? dt_atomic_get_int((dt_atomic_int *)&pipe->realtime) : FALSE;
502}
503
504void dt_dev_pixelpipe_set_input(dt_dev_pixelpipe_t *pipe, int32_t imgid, int width, int height,
506{
507 pipe->iwidth = width;
508 pipe->iheight = height;
509 pipe->iscale = iscale;
510 pipe->imgid = imgid;
511 pipe->dev->image_storage = pipe->dev->image_storage;
512 pipe->size = size;
513
514 // Nodes may already exist if this pipe was set up before its input size was known: export
515 // pipes call dt_dev_pixelpipe_create_nodes() before dt_dev_pixelpipe_set_input() (see
516 // common/imageio.c), and create_nodes() bakes pipe->iwidth/iheight into each piece at
517 // node-creation time. Without this, every piece stays stuck with whatever (zero) size the
518 // pipe had at that point, so any module reading piece->iwidth/iheight directly (toneequal,
519 // soften, ashift) silently gets 0 during export while darkroom pipes (whose input is set
520 // before their nodes are created) see the real size -- e.g. toneequal's blending radius
521 // collapses to 0 on export only, disabling its local smoothing there regardless of the
522 // module's actual settings.
523 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
524 {
526 if(IS_NULL_PTR(piece)) continue;
527 piece->iwidth = width;
528 piece->iheight = height;
529 }
530}
531
533 const gchar *icc_filename, dt_iop_color_intent_t icc_intent)
534{
535 pipe->icc_type = icc_type;
536 dt_free(pipe->icc_filename);
537 pipe->icc_filename = g_strdup(icc_filename ? icc_filename : "");
538 pipe->icc_intent = icc_intent;
539}
540
542{
543 /* Device-side cache payloads are only an acceleration layer. Drop the cl_mem
544 * objects this pipe produced on the device it last ran on -- but only that
545 * device, so we never touch cache entries another, still-running pipe holds
546 * on a different (or the same) OpenCL device. */
548
549 // blocks while busy and sets shutdown bit:
551 // The pipe held a reference on the history item it last synced to (see the field's comment
552 // in pixelpipe_hb.h); drop it now that no resync can run on this pipe any more. Exchanged,
553 // not read-then-cleared, for the same reason every other write to that slot is.
555 // so now it's safe to clean up cache:
556 const uint64_t old_backbuf_hash = dt_dev_backbuf_get_hash(&pipe->backbuf);
557 if(old_backbuf_hash != DT_PIXELPIPE_CACHE_HASH_INVALID)
558 {
559 /* Backbuffer ownership belongs to the pipeline, not its GUI consumers. Once the pipe itself is
560 * torn down, always release that keepalive ref and invalidate the published backbuffer metadata. */
561 dt_dev_pixelpipe_cache_unref_hash(old_backbuf_hash);
562
563 if(pipe->no_cache)
564 {
565 dt_pixel_cache_entry_t *old_backbuf_entry
566 = dt_dev_pixelpipe_cache_get_entry(old_backbuf_hash);
567 if(old_backbuf_entry)
568 {
571 }
572 }
573 }
578 dt_free(pipe->icc_filename);
579
580 /* Image-derived input profile, if this pipe built one. input_profile_info may point at
581 * it or at a shared memo entry, so free through the owning pointer only. */
583 pipe->input_profile_info = NULL;
584
586
588
589 // Every hash in this array corresponds to one cache reference acquired
590 // either while reopening current provider masks or while publishing a new
591 // one. Release the exact same number of references before destroying it.
592 for(guint k = 0; k < pipe->raster_mask_hashes->len; k++)
593 {
594 const uint64_t hash = g_array_index(pipe->raster_mask_hashes, uint64_t, k);
596 }
597 g_array_free(pipe->raster_mask_hashes, TRUE);
598 pipe->raster_mask_hashes = NULL;
599
600 if(pipe->forms)
601 {
602 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_form_unref);
603 pipe->forms = NULL;
604 }
605}
606
607
609{
611 {
612 pipe->reentry = TRUE;
613 pipe->reentry_hash = hash;
614 dt_print(DT_DEBUG_DEV, "[dev_pixelpipe] re-entry flag set for %" PRIu64 "\n", hash);
615 return TRUE;
616 }
617
618 return FALSE;
619}
620
621
623{
624 if(pipe->reentry_hash == hash)
625 {
626 pipe->reentry = FALSE;
628 dt_print(DT_DEBUG_DEV, "[dev_pixelpipe] re-entry flag unset for %" PRIu64 "\n", hash);
629 return TRUE;
630 }
631
632 return FALSE;
633}
634
636{
637 return pipe->reentry;
638}
639
646
648{
649 // destroy all nodes
650 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
651 {
653 if(IS_NULL_PTR(piece)) continue;
654 // printf("cleanup module `%s'\n", piece->module->name());
655 if(dt_supervisor_active() && piece->module)
657 dt_supervisor_node_key(pipe->type, piece->module->op, piece->module->multi_priority),
659 piece->module->op, piece->module->multi_priority, piece->module->iop_order,
660 pipe->type, pipe->imgid);
661 if(piece->module) dt_iop_cleanup_pipe(piece->module, pipe, piece);
662
663 /* Clear the node before releasing it. A freed node whose fields still read as a live one is
664 * exactly what makes this class of bug land far from its cause: a walker that outlives the
665 * teardown finds a plausible module pointer and a plausible private-data pointer, and the
666 * crash surfaces inside whichever module's commit_params() dereferences them. Zeroed, the
667 * same node is rejected by _iop_piece_is_committable() and reported by name.
668 * This does not make use-after-free safe -- the allocator may hand the bytes out again --
669 * it removes the window where the node is stale but still self-consistent. */
670 piece->module = NULL;
671 piece->data = NULL;
672 piece->data_size = 0;
673 piece->blendop_data = NULL;
674 piece->enabled = 0;
675
676 dt_free(piece);
677 }
678 g_list_free(pipe->nodes);
679 pipe->nodes = NULL;
680 // and iop order
681 g_list_free_full(pipe->iop_order_list, dt_free_gpointer);
682 pipe->iop_order_list = NULL;
684}
685
687{
688 // check that the pipe was actually properly cleaned up after the last run
689 g_assert(IS_NULL_PTR(pipe->nodes));
690 g_assert(IS_NULL_PTR(pipe->iop_order_list));
692
693 // for all modules in dev (iterated in iop_order, so each is the predecessor of the next):
694 dt_iop_module_t *prev_module = NULL;
695 for(GList *modules = g_list_first(pipe->dev->iop); modules; modules = g_list_next(modules))
696 {
697 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
699 if(IS_NULL_PTR(piece)) continue;
700 piece->enabled = module->enabled;
701 piece->request_histogram = DT_REQUEST_ONLY_IN_GUI;
702 piece->histogram_params.bins_count = 256;
703 piece->iwidth = pipe->iwidth;
704 piece->iheight = pipe->iheight;
705 piece->module = module;
707 piece->blendop_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
708 piece->global_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
709 piece->global_mask_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
711 piece->cache_output_on_ram = TRUE;
712
713 // dsc_mask is static, single channel float image
714 piece->dsc_mask.channels = 1;
715 piece->dsc_mask.datatype = TYPE_FLOAT;
716 dt_iop_buffer_dsc_update_bpp(&piece->dsc_mask);
717
718 dt_iop_init_pipe(piece->module, pipe, piece);
719
720 pipe->nodes = g_list_append(pipe->nodes, piece);
721
722 // Topology node: created once here, immutable until the nodes are torn down.
723 // param_hash is bound later, at history synchronization (dt_supervisor_node UPDATE).
725 {
726 const uint64_t pred = prev_module
727 ? dt_supervisor_node_key(pipe->type, prev_module->op, prev_module->multi_priority)
730 dt_supervisor_node_key(pipe->type, module->op, module->multi_priority),
731 DT_PIXELPIPE_CACHE_HASH_INVALID, pred, module->op, module->multi_priority,
732 module->iop_order, pipe->type, pipe->imgid);
733 }
734 prev_module = module;
735 }
736}
737
739 const dt_dev_pixelpipe_iop_t *const piece,
740 const dt_iop_buffer_dsc_t *const output_dsc)
741{
746
747 const dt_iop_colorspace_type_t blend_cst = dt_develop_blend_colorspace(piece, output_dsc->cst);
749 if(piece->dsc_in.cst != blend_cst
752 if(output_dsc->cst != blend_cst
753 && !(dt_iop_colorspace_is_rgb(output_dsc->cst) && dt_iop_colorspace_is_rgb(blend_cst)))
755
756 return transforms;
757}
758
759#define KILL_SWITCH_ABORT \
760 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
761 { \
762 if(!IS_NULL_PTR(cl_mem_output)) \
763 { \
764 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_output, NULL, NULL, FALSE); \
765 } \
766 return 1; \
767 }
768
769// Once we have a cache, stopping computation before full completion
770// has good chances of leaving it corrupted. So we invalidate it.
771#define KILL_SWITCH_AND_FLUSH_CACHE \
772 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
773 { \
774 return _abort_module_shutdown_cleanup(pipe, piece, module, input_hash, input, input_entry, hash, &output, \
775 &cl_mem_output, output_entry); \
776 }
777
778static void _print_perf_debug(dt_dev_pixelpipe_t *pipe, const dt_pixelpipe_flow_t pixelpipe_flow,
780 const gboolean recycled_output_cacheline, dt_times_t *start)
781{
782 char histogram_log[32] = "";
783 if(!(pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_NONE))
784 {
785 snprintf(histogram_log, sizeof(histogram_log), ", collected histogram on %s",
786 (pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_ON_GPU
787 ? "GPU"
788 : pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_ON_CPU ? "CPU" : ""));
789 }
790
791 const char *blend_log = pixelpipe_flow & PIXELPIPE_FLOW_BLENDED_ON_GPU
792 ? "blended on GPU"
793 : pixelpipe_flow & PIXELPIPE_FLOW_BLENDED_ON_CPU ? "blended on CPU" : "no blending";
794
795 gchar *module_label = dt_history_item_get_name(module);
797 start, "[dev_pixelpipe]", "processed `%s' on %s%s%s%s, %s [%s]", module_label,
798 pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_GPU
799 ? "GPU"
800 : pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_CPU ? "CPU" : "",
801 pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_WITH_TILING ? " with tiling" : "",
802 recycled_output_cacheline ? ", recycled cacheline" : "",
803 (!(pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_NONE) && (piece->request_histogram & DT_REQUEST_ON))
804 ? histogram_log
805 : "",
806 blend_log,
808 dt_free(module_label);
809}
810
811
812static void _print_nan_debug(dt_dev_pixelpipe_t *pipe, void *cl_mem_output, void *output,
813 const dt_iop_roi_t *roi_out, dt_iop_buffer_dsc_t *out_format,
814 dt_iop_module_t *module)
815{
816 if(!(dt_get_debug_flags() & DT_DEBUG_NAN)) return;
817 if(!(dt_get_debug_flags() & DT_DEBUG_VERBOSE)) return;
818 if((dt_get_debug_flags() & DT_DEBUG_NAN) && strcmp(module->op, "gamma") != 0)
819 {
820 gchar *module_label = dt_history_item_get_name(module);
821
822 if(out_format->datatype == TYPE_FLOAT && out_format->channels == 4)
823 {
824 int hasinf = 0, hasnan = 0;
825 dt_aligned_pixel_t min = { FLT_MAX };
826 dt_aligned_pixel_t max = { FLT_MIN };
827
828 for(int k = 0; k < 4 * roi_out->width * roi_out->height; k++)
829 {
830 if((k & 3) < 3)
831 {
832 float f = ((float *)(output))[k];
833 if(isnan(f))
834 hasnan = 1;
835 else if(!isfinite(f))
836 hasinf = 1;
837 else
838 {
839 min[k & 3] = fmin(f, min[k & 3]);
840 max[k & 3] = fmax(f, max[k & 3]);
841 }
842 }
843 }
844 if(hasnan)
845 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs NaNs! [%s]\n", module_label,
847 if(hasinf)
848 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs non-finite floats! [%s]\n", module_label,
850 fprintf(stderr, "[dev_pixelpipe] module `%s' min: (%f; %f; %f) max: (%f; %f; %f) [%s]\n", module_label,
851 min[0], min[1], min[2], max[0], max[1], max[2], dt_pixelpipe_get_pipe_name(pipe->type));
852 }
853 else if(out_format->datatype == TYPE_FLOAT && out_format->channels == 1)
854 {
855 int hasinf = 0, hasnan = 0;
856 float min = FLT_MAX;
857 float max = FLT_MIN;
858
859 for(int k = 0; k < roi_out->width * roi_out->height; k++)
860 {
861 float f = ((float *)(output))[k];
862 if(isnan(f))
863 hasnan = 1;
864 else if(!isfinite(f))
865 hasinf = 1;
866 else
867 {
868 min = fmin(f, min);
869 max = fmax(f, max);
870 }
871 }
872 if(hasnan)
873 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs NaNs! [%s]\n", module_label,
875 if(hasinf)
876 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs non-finite floats! [%s]\n", module_label,
878 fprintf(stderr, "[dev_pixelpipe] module `%s' min: (%f) max: (%f) [%s]\n", module_label, min, max,
880 }
881
882 dt_free(module_label);
883 }
884}
885
887 uint64_t *out_hash, const dt_dev_pixelpipe_iop_t **out_piece,
888 GList *pieces, int pos)
889{
890 // The pipeline is executed recursively, from the end. For each module n, starting from the end,
891 // if output is cached, take it, else if input is cached, take it, process it and output,
892 // else recurse to the previous module n-1 to get a an input.
893 void *input = NULL;
894 void *output = NULL;
895 void *cl_mem_output = NULL;
896
898
899 if(IS_NULL_PTR(pieces))
900 {
902 *out_piece = NULL;
903 return 0;
904 }
905
906 dt_iop_module_t *module = NULL;
908
910
911 // skip this module?
912 if(!piece->enabled)
913 return dt_dev_pixelpipe_process_rec(pipe, out_hash, out_piece, g_list_previous(pieces), pos - 1);
914
915 module = piece->module;
916
917 if(pipe->dev->gui_attached) pipe->dev->progress.total++;
918
920
921 const uint64_t hash = dt_dev_pixelpipe_node_hash(pipe, piece, piece->roi_out, pos);
922
923 // 1) Fast-track:
924 // If we have a cache entry for this hash, return it straight away,
925 // don't recurse through pipeline and don't process, unless this module still
926 // needs GUI-side sampling from its host input or the gamma display histogram
927 // needs the upstream cache entry.
928 dt_pixel_cache_entry_t *existing_cache = NULL;
929 void *existing_output = NULL;
930 /* Atomically look up the entry and increment its refcount so the caller receives a
931 * fully-owned reference. peek() + separate ref_count_entry() has a TOCTOU window:
932 * peek releases its tryrdlock immediately, so the entry can reach refcount 0 and be
933 * evicted between peek() and ref_count_entry(). */
934 const gboolean exact_output_cache_hit
935 = !_bypass_cache(pipe, piece)
937 &existing_output, &existing_cache)
938 && !IS_NULL_PTR(existing_output);
939
940 if(exact_output_cache_hit)
941 {
942 _trace_cache_owner(pipe, module, "exact-hit-direct", "output", hash, NULL, existing_cache, FALSE);
943 *out_hash = hash;
944 *out_piece = piece;
945 return 0;
946 }
947 else if(existing_cache)
948 {
949 /* ref_entry_by_hash succeeded but data was NULL (device-only entry); undo the ref. */
951 }
952
953 // 3) now recurse through the pipeline.
955 const dt_dev_pixelpipe_iop_t *previous_piece = NULL;
956 if(dt_dev_pixelpipe_process_rec(pipe, &input_hash, &previous_piece, g_list_previous(pieces), pos - 1))
957 {
958 /* Child recursion failed before this module acquired any output cache entry.
959 * Dropping `hash` here underflows cached exact-hit outputs during shutdown. */
961 "[pipeline] module=%s child recursion failed input_hash=%" PRIu64 " output_hash=%" PRIu64 "\n",
962 module->op, input_hash, hash);
963 return 1;
964 }
965
967
968 // Child recursion just published or exact-hit returned this hash with one ref already reserved for
969 // this immediate consumer. Reopen the live cache entry directly instead of going through exact-hit
970 // lookup, because exact-hit intentionally rejects auto-destroy entries while the parent recursion
971 // still needs to consume transient outputs in the same run.
972 dt_pixel_cache_entry_t *input_entry
974 if(!IS_NULL_PTR(previous_piece))
975 input_entry = dt_dev_pixelpipe_cache_get_entry(input_hash);
976 if(IS_NULL_PTR(input_entry) && !(module->flags() & IOP_FLAGS_TAKE_NO_INPUT))
977 {
979 "[pipeline] module=%s input cache entry missing input_hash=%" PRIu64 " output_hash=%" PRIu64
980 " prev_module=%s prev_hash=%" PRIu64 "\n",
981 module->op, input_hash, hash,
982 !IS_NULL_PTR(previous_piece) ? previous_piece->module->op : "",
983 !IS_NULL_PTR(previous_piece) ? previous_piece->global_hash : -1);
984 return 1;
985 }
986 input = input_entry ? dt_pixel_cache_entry_get_data(input_entry) : NULL;
987 _trace_cache_owner(pipe, module, "acquire", "input", input_hash, input, input_entry, FALSE);
988 if(input_entry)
989 _trace_buffer_content(pipe, module, "input-acquire", input, &piece->dsc_in, &piece->roi_in);
990 const size_t bufsize = (size_t)piece->dsc_out.bpp * piece->roi_out.width * piece->roi_out.height;
991 // Note: IS_NULL_PTR(input) is valid if we are on a GPU-only path, aka previous module ran on GPU
992 // without leaving its output on a RAM cache copy, and current module will also run on GPU.
993 // In this case, we rely on cl_mem_input for best performance (avoid memcpy between RAM and GPU).
994 // Should the GPU path fail at process time, we will init input and flush cl_mem_input into it.
995 // In any case, this avoids carrying a possibly-uninited input buffer, without knowing if it has
996 // data on it (or having to blindly copy back from vRAM to RAM).
997
998 // 3c) actually process this module BUT treat all bypasses first.
999 // special case: user requests to see channel data in the parametric mask of a module, or the blending
1000 // mask. In that case we skip all modules manipulating pixel content and only process image distorting
1001 // modules. Finally "gamma" is responsible for displaying channel/mask data accordingly.
1002 if(pipe->dev->gui_attached
1004 && !(module->operation_tags() & IOP_TAG_DISTORT)
1005 && (piece->dsc_in.bpp == piece->dsc_out.bpp)
1006 && !memcmp(&piece->roi_in, &piece->roi_out, sizeof(struct dt_iop_roi_t)))
1007 {
1008 /* Mask/channel passthrough keeps the exact child buffer alive for the next
1009 * downstream module. This stage does not publish a new cacheline, so it
1010 * must forward the child hash and actual previous piece contract exactly as
1011 * they came from recursion, including the single reserved ref for the next
1012 * real consumer. */
1013 *out_hash = input_hash;
1014 *out_piece = previous_piece;
1015 return 0;
1016 }
1017
1018 if(pipe->dev->gui_attached)
1019 {
1020 gchar *module_label = dt_history_item_get_name(module);
1021 dt_pipeline_busy_printf(_("Processing module `%s` for pipeline %s (%ix%i px @ %0.f%%)..."),
1022 module_label, dt_pixelpipe_get_pipe_name(pipe->type),
1023 piece->roi_out.width, piece->roi_out.height,
1024 piece->roi_out.scale * 100.f);
1025 dt_free(module_label);
1026 }
1027
1028 dt_pixel_cache_entry_t *output_entry = NULL;
1029 gchar *type = dt_pixelpipe_get_pipe_name(pipe->type);
1030
1032
1033 gchar *name = g_strdup_printf("module %s (%s) for pipe %s", module->op, module->multi_name, type);
1034 // Cache bypass makes intermediate module outputs disposable, but the final output is the
1035 // backbuffer consumed by Cairo. It must keep a host payload even when an edit mode (crop,
1036 // clipping, ashift...) bypasses the downstream cache. Otherwise the GUI requests that host
1037 // cacheline after every GPU render, while each cache-request pass publishes another device-only
1038 // backbuffer and feeds an infinite preview recompute loop.
1039 const gboolean keep_final_output = (hash == dt_dev_pixelpipe_get_hash(pipe));
1040 gboolean cache_ram_output
1041 = piece->cache_output_on_ram && (!_bypass_cache(pipe, piece) || keep_final_output);
1042
1043 /* `piece->cache_entry` is only valid as a writable-reuse hint for transient outputs that will
1044 * be fully overwritten later. As soon as we keep the current output as a published cacheline in
1045 * RAM, rekey reuse must stop for that piece so later runs cannot overwrite a long-term state in
1046 * place just because the pipe is running in realtime. */
1047 const gboolean allow_rekey_reuse = !(dt_get_debug_flags() & DT_DEBUG_NOCACHE_REUSE) && !cache_ram_output;
1048 const dt_dev_pixelpipe_cache_writable_status_t acquire_status
1049 = dt_dev_pixelpipe_cache_get_writable(hash, bufsize, name, pipe->type,
1050 cache_ram_output, allow_rekey_reuse,
1051 &piece->cache_entry,
1052 &output, &output_entry);
1053 dt_free(name);
1054 if(acquire_status == DT_DEV_PIXELPIPE_CACHE_WRITABLE_EXACT_HIT)
1055 {
1056 /* Another pipe already owns a cacheline for this exact hash. If that cacheline is
1057 * still write-locked, this is not a processing error: it only means the concurrent
1058 * publisher has not finished exposing the exact-hit payload yet. Wait for that
1059 * publication to complete instead of aborting the whole recursion. */
1060 dt_pixel_cache_entry_t *exact_entry
1062 if(IS_NULL_PTR(exact_entry))
1063 {
1065 "[pipeline] module=%s exact-hit entry missing output_hash=%" PRIu64 "\n",
1066 module->op, hash);
1067 if(input_entry)
1069 return 1;
1070 }
1071
1077
1079 "[pipeline] module=%s writable-exact-hit output_hash=%" PRIu64 " has_host_data=%d"
1080 " cache_ram_output=%d devid=%d\n",
1081 module->op, hash, !IS_NULL_PTR(dt_pixel_cache_entry_get_data(exact_entry)), cache_ram_output,
1082 pipe->devid);
1083
1084 /* This entry may predate the current run's host-caching requirement: it was created device-only
1085 * (OpenCL, no RAM copy) back when nothing needed a host copy of it, and an exact-hash hit here
1086 * short-circuits without ever re-running the module, so `cache_ram_output` newly turning TRUE
1087 * (e.g. a color picker or histogram just started sampling this piece) would otherwise never take
1088 * effect on an already-cached entry. Materialize the host copy now from the device payload we
1089 * already own the OpenCL context for, instead of silently leaving GUI host-only consumers
1090 * (dt_dev_pixelpipe_cache_peek_gui, preferred_devid = -1) missing this hash forever. */
1091 if(cache_ram_output && IS_NULL_PTR(dt_pixel_cache_entry_get_data(exact_entry)))
1092 {
1093 void *restored = NULL;
1094 const gboolean restored_ok
1096 &restored);
1098 "[pipeline] module=%s exact-hit was device-only, host materialize %s output_hash=%" PRIu64 "\n",
1099 module->op, restored_ok ? "succeeded" : "failed", hash);
1100 }
1101
1102 _trace_cache_owner(pipe, module, "exact-hit-wait", "output", hash,
1103 dt_pixel_cache_entry_get_data(exact_entry), exact_entry, FALSE);
1104
1105 if(input_entry)
1107 *out_hash = hash;
1108 *out_piece = piece;
1109 return 0;
1110 }
1111 if(IS_NULL_PTR(output_entry))
1112 {
1114 "[pipeline] module=%s writable output acquisition failed output_hash=%" PRIu64
1115 " acquire_status=%d\n",
1116 module->op, hash, acquire_status);
1117 if(input_entry)
1119 return 1;
1120 }
1121 const gboolean new_entry = (acquire_status == DT_DEV_PIXELPIPE_CACHE_WRITABLE_CREATED);
1122 _trace_cache_owner(pipe, module, (acquire_status == DT_DEV_PIXELPIPE_CACHE_WRITABLE_REKEYED) ? "acquire-rekeyed"
1123 : (new_entry ? "acquire-new" : "acquire-existing"),
1124 "output", hash, output, output_entry, FALSE);
1125
1126 /* get tiling requirement of module */
1128 tiling.factor_cl = tiling.maxbuf_cl = -1; // set sentinel value to detect whether callback set sizes
1129 module->tiling_callback(module, pipe, piece, &tiling);
1130 if (tiling.factor_cl < 0) tiling.factor_cl = tiling.factor; // default to CPU size if callback didn't set GPU
1131 if (tiling.maxbuf_cl < 0) tiling.maxbuf_cl = tiling.maxbuf;
1132
1133 /* does this module involve blending? */
1134 if(piece->blendop_data && ((dt_develop_blend_params_t *)piece->blendop_data)->mask_mode != DEVELOP_MASK_DISABLED)
1135 {
1136 /* get specific memory requirement for blending */
1137 dt_develop_tiling_t tiling_blendop = { 0 };
1138 tiling_callback_blendop(module, pipe, piece, &tiling_blendop);
1139
1140 /* aggregate in structure tiling */
1141 tiling.factor = fmax(tiling.factor, tiling_blendop.factor);
1142 tiling.factor_cl = fmax(tiling.factor_cl, tiling_blendop.factor);
1143 tiling.maxbuf = fmax(tiling.maxbuf, tiling_blendop.maxbuf);
1144 tiling.maxbuf_cl = fmax(tiling.maxbuf_cl, tiling_blendop.maxbuf);
1145 tiling.overhead = fmax(tiling.overhead, tiling_blendop.overhead);
1146 }
1147
1148 /* remark: we do not do tiling for blendop step, neither in opencl nor on cpu. if overall tiling
1149 requirements (maximum of module and blendop) require tiling for opencl path, then following blend
1150 step is anyhow done on cpu. we assume that blending itself will never require tiling in cpu path,
1151 because memory requirements will still be low enough. */
1152
1153 assert(tiling.factor > 0.0f);
1154 assert(tiling.factor_cl > 0.0f);
1155
1156 // Actual pixel processing for this module
1157 int error = 0;
1158 dt_times_t start;
1159 dt_get_times(&start);
1160
1161 const char *prev_module = dt_pixelpipe_cache_set_current_module(module ? module->op : NULL);
1162
1163#ifdef HAVE_OPENCL
1164 error = pixelpipe_process_on_GPU(pipe, piece, previous_piece, &tiling, &pixelpipe_flow,
1165 &cache_ram_output,
1166 input_entry, output_entry);
1167#else
1168 error = pixelpipe_process_on_CPU(pipe, piece, previous_piece, &tiling, &pixelpipe_flow,
1169 &cache_ram_output,
1170 input_entry, output_entry);
1171#endif
1172
1174 output = dt_pixel_cache_entry_get_data(output_entry);
1175
1176 _print_perf_debug(pipe, pixelpipe_flow, piece, module,
1177 (acquire_status != DT_DEV_PIXELPIPE_CACHE_WRITABLE_CREATED), &start);
1178
1179 if(pipe->dev->gui_attached) pipe->dev->progress.completed++;
1180
1181 if(error)
1182 {
1184 "[pipeline] module=%s backend processing failed input_hash=%" PRIu64 " output_hash=%" PRIu64
1185 " input_cst=%d output_cst=%d roi_in=%dx%d roi_out=%dx%d\n",
1186 module->op, input_hash, hash, piece->dsc_in.cst, piece->dsc_out.cst,
1187 piece->roi_in.width, piece->roi_in.height, piece->roi_out.width, piece->roi_out.height);
1188 _trace_cache_owner(pipe, module, "error-cleanup", "input", input_hash, input, input_entry, FALSE);
1189 _trace_cache_owner(pipe, module, "error-cleanup", "output", hash, output, output_entry, FALSE);
1190 // Ensure we always release locks and cache references on error, otherwise cache eviction/GC will stall.
1193 if(input_entry)
1194 {
1197 }
1198
1199 // No point in keeping garbled output
1201 if(dt_dev_pixelpipe_cache_remove(TRUE, output_entry))
1203 return 1;
1204 }
1205
1206 // Publish the module output descriptor authored for this stage. The cache entry keeps the
1207 // descriptor of the pixels this module actually published, not the stale descriptor of its input.
1208 _trace_cache_owner(pipe, module, "publish", "output", hash, output, output_entry, FALSE);
1209 _trace_buffer_content(pipe, module, "publish", output, &piece->dsc_out, &piece->roi_out);
1210
1211 if(!IS_NULL_PTR(output_entry))
1212 {
1213 piece->cache_entry = *output_entry;
1214 }
1215 else
1217
1218 if(!IS_NULL_PTR(output_entry) && !IS_NULL_PTR(output) && !(module->flags() & IOP_FLAGS_CPU_WRITES_OPENCL)
1219 && ((pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_CPU)
1220 || (pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_WITH_TILING)))
1221 {
1222 dt_dev_pixelpipe_gpu_flush_host_pinned_images(pipe, output, output_entry, "module output host rewrite");
1223 /* CPU/tiling processing rewrote the whole host buffer for this output. If the cache entry was rekeyed
1224 * from an older GPU stage, any cached device-only images still hanging off the same entry now point to
1225 * obsolete pixels. Drop them here so later mixed GPU/CPU modules cannot resurrect stale device payloads. */
1227 }
1228
1229 // Flag to throw away intermediate outputs as soon as the next module consumes them.
1230 // `cache_output` only means the backend had to keep a host-authoritative payload for this
1231 // stage (for example because the next module may need RAM or because the current stage ran
1232 // through an OpenCL cache path). In no-cache/bypass pipelines, that does not make the
1233 // published cacheline long-lived: once the downstream module takes its input ref, this
1234 // stage is transient and must disappear on release. Only the final published output needs
1235 // to survive long enough for dt_dev_pixelpipe_process() to promote it to the backbuffer,
1236 // otherwise thumbnail/export callers only see a missing exact-hit and fall back to invalid
1237 // placeholder pixels.
1238 if(_bypass_cache(pipe, piece) && !keep_final_output)
1240
1241 if(pipe->dev->gui_attached)
1243
1244 // From here on we only publish/inspect the finished output. Keep the writable lock strictly
1245 // around cacheline allocation and backend processing, then release it at one visible point
1246 // before the generic tail cleanup shared by darkroom and headless paths.
1247 if(dt_supervisor_active() && !IS_NULL_PTR(output_entry))
1249 dt_supervisor_node_key(pipe->type, module->op, module->multi_priority),
1250 piece->hash, input_hash, module->op, module->multi_priority,
1251 module->iop_order, pipe->type, pipe->imgid, piece->roi_out.width,
1252 piece->roi_out.height, pipe->devid,
1253 dt_pixel_cache_entry_get_size(output_entry), NULL);
1254 // Stamp the producing node identity so the CACHELINE_READY raised on the write-lock
1255 // release below carries it: GUI cache-wait consumers can then be served by producer
1256 // node even when the exact output hash drifted between their request and this publish
1257 // (doc/pipeline-cache.md §8). Set on every publish so a rekey-reused entry never keeps
1258 // a stale producer.
1259 if(!IS_NULL_PTR(output_entry))
1260 output_entry->producer_node_key
1261 = dt_supervisor_node_key(pipe->type, module->op, module->multi_priority);
1263
1265
1266 // Decrease reference count on input and flush it if it was flagged for auto destroy previously
1267 _trace_cache_owner(pipe, module, "release", "input", input_hash, input, input_entry, FALSE);
1268 if(input_entry)
1269 {
1272 }
1273
1274 // Print min/max/Nan in debug mode only
1275 if((dt_get_debug_flags() & DT_DEBUG_NAN) && strcmp(module->op, "gamma") != 0 && !IS_NULL_PTR(output))
1276 {
1278 _print_nan_debug(pipe, cl_mem_output, output, &piece->roi_out, &piece->dsc_out, module);
1280 }
1281
1283
1284 *out_hash = hash;
1285 *out_piece = piece;
1286 return 0;
1287}
1288
1290{
1291 GList *nodes = g_list_last(pipe->nodes);
1293 while(strcmp(piece->module->op, op))
1294 {
1295 piece->enabled = 0;
1296 piece = NULL;
1297 nodes = g_list_previous(nodes);
1298 if(!nodes) break;
1299 piece = (dt_dev_pixelpipe_iop_t *)nodes->data;
1300 }
1301}
1302
1304{
1305 GList *nodes = pipe->nodes;
1307 while(strcmp(piece->module->op, op))
1308 {
1309 piece->enabled = 0;
1310 piece = NULL;
1311 nodes = g_list_next(nodes);
1312 if(!nodes) break;
1313 piece = (dt_dev_pixelpipe_iop_t *)nodes->data;
1314 }
1315}
1316
1317#define KILL_SWITCH_PIPE \
1318 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
1319 { \
1320 if(pipe->devid >= 0) \
1321 { \
1322 dt_opencl_release_device(pipe->devid); \
1323 pipe->devid = -1; \
1324 } \
1325 if(pipe->forms) \
1326 { \
1327 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_form_unref); \
1328 pipe->forms = NULL; \
1329 } \
1330 dt_pthread_mutex_unlock(dt_pipeline_threadsafe_mutex()); \
1331 return 1; \
1332 }
1333
1334
1336{
1337 switch(error)
1338 {
1339 case 1:
1340 dt_print(DT_DEBUG_OPENCL, "[opencl] Opencl errors; disabling opencl for %s pipeline!\n", dt_pixelpipe_get_pipe_name(pipe->type));
1341 dt_pipeline_message(_("Ansel discovered problems with your OpenCL setup; disabling OpenCL for %s pipeline!"), dt_pixelpipe_get_pipe_name(pipe->type));
1342 break;
1343 case 2:
1345 "[opencl] Too many opencl errors; disabling opencl for this session!\n");
1346 dt_pipeline_message(_("Ansel discovered problems with your OpenCL setup; disabling OpenCL for this session!"));
1347 break;
1348 default:
1349 break;
1350 }
1351}
1352
1354{
1355 const uint64_t requested_hash = dt_dev_pixelpipe_get_hash(pipe);
1356 const uint64_t entry_hash = entry->hash;
1357
1358 _trace_cache_owner(pipe, NULL, "backbuf-update", "backbuf", requested_hash,
1359 entry ? entry->data : NULL, entry, FALSE);
1360
1361 if(requested_hash == DT_PIXELPIPE_CACHE_HASH_INVALID
1362 || entry_hash == DT_PIXELPIPE_CACHE_HASH_INVALID
1363 || entry_hash != requested_hash)
1364 {
1368 return;
1369 }
1370
1371 // Keep exactly one cache reference to the last valid output ("backbuf") for display.
1372 // This prevents the cache entry from being evicted while still in use by the GUI,
1373 // without leaking references on repeated cache hits.
1374 const gboolean hash_changed = (dt_dev_backbuf_get_hash(&pipe->backbuf) != entry_hash);
1375 if(hash_changed)
1376 {
1379 }
1380
1381 /* The backbuf advertises the ROI THIS RUN ASKED FOR, paired with whatever cacheline it
1382 * resolved. Those two are supposed to describe the same image, and every consumer trusts that
1383 * they do: the display paths compute a cairo stride from the width and walk the cacheline with
1384 * it. When they disagree the picture is drawn with the wrong row length -- the diagonal
1385 * striping of a stride error -- and nothing downstream can detect it, because a cacheline is
1386 * just bytes and carries no shape of its own.
1387 *
1388 * They can disagree. Cache entries are reused in place by rekeying rather than reallocated
1389 * (caches/pixelpipe_cache.c), so an entry produced at one ROI can be handed back under a key
1390 * planned at another.
1391 *
1392 * The invariant worth checking is that the cacheline is BIG ENOUGH for the advertised
1393 * dimensions -- not that it is exactly that size. Buffers come from an aligned allocator and
1394 * from a reuse pool, so a cacheline is routinely larger than width x height x bpp: measured on
1395 * an ordinary darkroom entry, the display buffers run about 0.2 % over and a thumbnail's can
1396 * be several times over. That slack is why `bpp' below, a division by the requested pixel
1397 * count, is not a pixel size and should not be read as one -- nothing does; the field exists
1398 * for a consumer that no longer has one.
1399 *
1400 * Too SMALL is the dangerous direction, and the only one a consumer cannot survive: every
1401 * display path computes a cairo stride from the width and walks that many rows, so a short
1402 * cacheline is an out-of-bounds read, and a wrong width is the diagonal striping of a stride
1403 * error. Report it; do not try to repair it here, where neither the true shape of the entry nor
1404 * the reason it was selected is known. */
1405 int bpp = 0;
1406 if(roi.width > 0 && roi.height > 0)
1407 {
1408 const size_t entry_size = dt_pixel_cache_entry_get_size(entry);
1409 const size_t pixels = (size_t)roi.width * (size_t)roi.height;
1410 bpp = (int)(entry_size / pixels);
1411
1412 /* 4 bytes per pixel: this is the final, display-encoded backbuffer. */
1413 if(entry_size < pixels * 4)
1415 // %llu, not %zu: MinGW's printf does not implement the C99 length modifier.
1416 "[pixelpipe] BACKBUF TOO SMALL on pipe %s: roi %dx%d needs %llu bytes, cacheline holds "
1417 "%llu; hash %" PRIu64 "\n",
1419 (unsigned long long)(pixels * 4), (unsigned long long)entry_size, (uint64_t)entry_hash);
1420
1421 /* And the shape itself. `roi' is what this run ASKED the pipe for; the pixels in the
1422 * cacheline are whatever the last node actually produced, which is its roi_out. Those are
1423 * supposed to be the same rectangle, and the backbuffer advertises the former while handing
1424 * consumers the latter -- so if they differ, every display path computes its cairo stride
1425 * from a width the data does not have, and draws the diagonal striping of a stride error.
1426 *
1427 * The cache cannot answer this on its own: an entry records how many BYTES it holds and not
1428 * what shape they are in, so a cacheline reused at another size is indistinguishable from a
1429 * correct one by size alone -- large-but-wrong passes every check a consumer can make. The
1430 * last node's planned output is the only place the true shape survives. */
1431 const GList *const last = g_list_last(pipe->nodes);
1432 if(!IS_NULL_PTR(last))
1433 {
1434 const dt_dev_pixelpipe_iop_t *const tail = (const dt_dev_pixelpipe_iop_t *)last->data;
1435 if(tail->roi_out.width != roi.width || tail->roi_out.height != roi.height)
1437 "[pixelpipe] BACKBUF SHAPE MISMATCH on pipe %s: advertising %dx%d but %s produced "
1438 "%dx%d; hash %" PRIu64 "\n",
1439 dt_pixelpipe_get_pipe_name(pipe->type), roi.width, roi.height, tail->module->op,
1440 tail->roi_out.width, tail->roi_out.height, (uint64_t)entry_hash);
1441 }
1442 }
1443
1444 // Always refresh backbuf geometry/state, even when the cache key is unchanged.
1445 // Realtime drawing can update pixels in-place in the same cacheline, so width/height/history
1446 // must stay synchronized independently from key changes.
1447 dt_dev_set_backbuf(&pipe->backbuf, roi.width, roi.height, bpp, entry_hash,
1449
1452 roi.width, roi.height, bpp, pipe->type,
1453 pipe->devid >= 0 ? pipe->devid : pipe->last_devid);
1454}
1455
1456static GList *_get_requested_piece_node(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module, int *pos)
1457{
1458 if(pos) *pos = 0;
1459 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(module)) return NULL;
1460
1461 int current_pos = 1;
1462 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node), current_pos++)
1463 {
1464 dt_dev_pixelpipe_iop_t *const piece = node->data;
1465 if(piece && piece->enabled && piece->module == module)
1466 {
1467 if(pos) *pos = current_pos;
1468 return node;
1469 }
1470 }
1471
1472 return NULL;
1473}
1474
1476{
1477 /* `pipe->devid` is only valid while the current run owns the OpenCL device lock.
1478 * Reset it before any cache probe so callers never reuse a stale device id from a
1479 * previous pipeline pass. */
1480 pipe->devid = -1;
1481
1482 /* Record what image / pipeline is being processed, for crash reports. */
1483 if(pipe->dev)
1484 {
1485 const char *pl = "other";
1486 switch(pipe->type)
1487 {
1488 case DT_DEV_PIXELPIPE_FULL: pl = "darkroom"; break;
1489 case DT_DEV_PIXELPIPE_PREVIEW: pl = "darkroom-preview"; break;
1490 case DT_DEV_PIXELPIPE_EXPORT: pl = "export"; break;
1491 case DT_DEV_PIXELPIPE_THUMBNAIL: pl = "thumbnail"; break;
1492 default: break;
1493 }
1496 }
1497
1499 {
1500 fprintf(stderr, "[memory] before pixelpipe process\n");
1502 }
1503
1505
1506 if(pipe->dev->gui_attached)
1507 {
1508 pipe->dev->color_picker.pending_module = NULL;
1509 pipe->dev->color_picker.pending_pipe = NULL;
1511 }
1512
1513 // Get the roi_out hash of all nodes.
1514 // Get the previous output size of the module, for cache invalidation.
1515 dt_dev_pixelpipe_get_roi_in(pipe, roi);
1517 const guint pos = g_list_length(pipe->dev->iop);
1518
1519 const guint previous_raster_refs = pipe->raster_mask_hashes->len;
1520
1527 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node))
1528 {
1529 const dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)node->data;
1530 if(!piece->enabled) continue;
1531
1532 GHashTableIter mask_iter;
1533 gpointer mask_key = NULL;
1534 gpointer mask_name = NULL;
1535 g_hash_table_iter_init(&mask_iter, piece->module->raster_mask.source.masks);
1536 while(g_hash_table_iter_next(&mask_iter, &mask_key, &mask_name))
1537 {
1538 const int mask_id = GPOINTER_TO_INT(mask_key);
1539 if(!pipe->store_all_raster_masks
1540 && !dt_iop_is_raster_mask_used(piece->module, mask_id))
1541 continue;
1542
1543 const uint64_t mask_hash = dt_dev_pixelpipe_raster_mask_hash(piece, mask_id);
1545 mask_hash, NULL, NULL))
1546 g_array_append_val(pipe->raster_mask_hashes, mask_hash);
1547 }
1548 }
1549
1550 for(guint k = 0; k < previous_raster_refs; k++)
1551 {
1552 const uint64_t hash = g_array_index(pipe->raster_mask_hashes, uint64_t, k);
1554 }
1555 if(previous_raster_refs > 0)
1556 g_array_remove_range(pipe->raster_mask_hashes, 0, previous_raster_refs);
1557
1559 const dt_iop_module_t *const requested_module = dt_dev_pixelpipe_get_cache_request_module(pipe);
1561
1562 GList *requested_pieces = g_list_last(pipe->nodes);
1563 int requested_pos = (int)pos;
1564 dt_dev_pixelpipe_iop_t *requested_piece = NULL;
1565 gboolean requested_backbuf = TRUE;
1566
1567 if(cache_request == DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE && !IS_NULL_PTR(requested_module))
1568 {
1569 requested_pieces = _get_requested_piece_node(pipe, requested_module, &requested_pos);
1570 if(requested_pieces)
1571 {
1572 requested_piece = requested_pieces->data;
1573 requested_backbuf = FALSE;
1574 }
1575 else
1576 {
1577 dt_print(DT_DEBUG_DEV, "[pixelpipe/gui] requested module cache target disappeared pipe=%s module=%s\n",
1578 dt_pixelpipe_get_pipe_name(pipe->type), requested_module->op);
1579 }
1580 }
1581
1582 void *buf = NULL;
1583
1584 /* GUI cache requests can target either the final backbuffer or one module output in the middle of the
1585 current synchronized graph. Exact-hit checks must therefore look at the requested target instead of
1586 always assuming the run goes to the pipe end. */
1587 const uint64_t requested_hash = requested_backbuf ? dt_dev_pixelpipe_get_hash(pipe)
1588 : requested_piece ? requested_piece->global_hash
1590 dt_pixel_cache_entry_t *entry = NULL;
1591 if(!_bypass_cache(pipe, requested_piece)
1592 && requested_hash != DT_PIXELPIPE_CACHE_HASH_INVALID
1593 && dt_dev_pixelpipe_cache_peek(requested_hash, &buf, &entry,
1594 pipe->devid, NULL)
1595 && !IS_NULL_PTR(buf))
1596 {
1597 if(requested_backbuf)
1598 _update_backbuf_cache_reference(pipe, roi, entry);
1599
1600 /* A GUI consumer explicitly requested this target (color picker, histogram, autoset) and is
1601 * blocked on it in the cache-wait manager. The output is already host-cached, so the run stops
1602 * here without ever taking a write lock — hence no DT_SIGNAL_CACHELINE_READY would fire and the
1603 * waiter would hang forever, even though its buffer is right there (doc/pipeline-cache.md §8).
1604 * This is the "no node update, nothing" deadlock: it bites when the hash the GUI predicted
1605 * (H_gui) drifted from the one now cached (H_pipe), so the waiter never sees its exact hash and
1606 * the pipe never republishes. Wake it here, carrying the producing node key so the manager's
1607 * producer-node match serves the waiter even under that drift; its restart then re-reads the
1608 * module's current hash and hits. Gated on an actual pending request so ordinary cache-hit
1609 * renders add no signal traffic. */
1610 if(cache_request != DT_DEV_PIXELPIPE_CACHE_REQUEST_NONE)
1611 {
1612 const uint64_t ready_node_key
1613 = (!requested_backbuf && !IS_NULL_PTR(requested_piece) && !IS_NULL_PTR(requested_piece->module))
1614 ? dt_supervisor_node_key(pipe->type, requested_piece->module->op,
1615 requested_piece->module->multi_priority)
1618 ready_node_key);
1619 }
1620 return 0;
1621 }
1622
1623 dt_print(DT_DEBUG_DEV, "[pixelpipe] Started %s pipeline recompute at %i×%i px\n",
1624 dt_pixelpipe_get_pipe_name(pipe->type), roi.width, roi.height);
1625
1626 // get a snapshot of the mask list: shared by reference (dt_masks_cow_touch on the GUI side
1627 // clones instead of mutating in place whenever refcount > 1, so this snapshot stays frozen
1628 // even if dev->forms is edited mid-run), not deep-copied on every single pipeline recompute.
1630
1631 // go through the list of modules from the end:
1632 GList *pieces = g_list_last(pipe->nodes);
1633
1634 // Because it's possible here that we export at full resolution,
1635 // and our memory planning doesn't account for several concurrent pipelines
1636 // at full size, we allow only one pipeline at a time to run.
1637 // This is because wavelets decompositions and such use 6 copies,
1638 // so the RAM usage can go out of control here.
1640
1642 {
1643 const guint node_count = g_list_length(pipe->nodes);
1644 uint64_t *invalidated_hashes = g_new(uint64_t, node_count);
1645 size_t invalidated_count = 0;
1646 gboolean reached_provider = FALSE;
1647
1659 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node))
1660 {
1662 if(!piece->enabled) continue;
1663
1664 reached_provider |= (piece->global_hash == pipe->reentry_hash);
1665 if(reached_provider)
1666 invalidated_hashes[invalidated_count++] = piece->global_hash;
1667 }
1668
1669 const int retained = dt_dev_pixelpipe_cache_invalidate_hashes(
1670 invalidated_hashes, invalidated_count);
1672 "[raster masks] invalidated %" G_GSIZE_FORMAT " cache states at retry from provider=%" PRIu64
1673 " retained=%d pipe=%s\n",
1674 invalidated_count, pipe->reentry_hash, retained,
1676 dt_free(invalidated_hashes);
1677 }
1678
1679 pipe->opencl_enabled = dt_opencl_update_settings(); // update enabled flag and profile from preferences
1681 : -1; // try to get/lock opencl resource
1682
1683 if(pipe->devid > -1)
1684 {
1686 pipe->last_devid = pipe->devid;
1687 }
1688 dt_print(DT_DEBUG_OPENCL, "[pixelpipe_process] [%s] using device %d\n", dt_pixelpipe_get_pipe_name(pipe->type),
1689 pipe->devid);
1690
1692
1693 gboolean keep_running = TRUE;
1694 int runs = 0;
1695 int opencl_error = 0;
1696 int err = 0;
1697
1698 while(keep_running && runs < 3)
1699 {
1700 ++runs;
1701
1702 /* Mask preview is authored while the current run advances through blend.c.
1703 * Reset it for each retry so a stale state from a previous pass cannot leak
1704 * into the next recursion before the active module reaches its own blend. */
1706
1707#ifdef HAVE_OPENCL
1709#endif
1710
1712
1713 dt_times_t start;
1714 dt_get_times(&start);
1715 uint64_t final_hash = -1;
1716 const dt_dev_pixelpipe_iop_t *final_piece = NULL;
1717 err = dt_dev_pixelpipe_process_rec(pipe, &final_hash, &final_piece,
1718 requested_backbuf ? pieces : requested_pieces,
1719 requested_backbuf ? pos : requested_pos);
1720 (void)final_piece;
1721 gchar *msg = g_strdup_printf("[pixelpipe] %s internal pixel pipeline processing", dt_pixelpipe_get_pipe_name(pipe->type));
1722 dt_show_times(&start, msg);
1723 dt_free(msg);
1724
1725 // get status summary of opencl queue by checking the eventlist
1726 const int oclerr = (pipe->devid > -1) ? dt_opencl_events_flush(pipe->devid, TRUE) != 0 : 0;
1727
1728 // Check if we had opencl errors ....
1729 // remark: opencl errors can come in two ways: pipe->opencl_error is TRUE (and err is TRUE) OR oclerr is
1730 // TRUE
1731 keep_running = (oclerr || (err && pipe->opencl_error));
1732 if(keep_running)
1733 {
1734 // Report it and be told what it means: 1 = retry this run on CPU, 2 = OpenCL is off
1735 // for the rest of the session. The count and the threshold are the OpenCL module's.
1736 opencl_error = dt_opencl_report_pipe_error();
1737
1738 // Disable OpenCL for this pipe
1740 pipe->opencl_enabled = 0;
1741 pipe->opencl_error = 0;
1742 pipe->devid = -1;
1743
1744 _print_opencl_errors(opencl_error, pipe);
1745 }
1746 else if(!dt_dev_pixelpipe_has_shutdown(pipe))
1747 {
1748 // No opencl errors, no killswitch triggered: we should have a valid output buffer now.
1749 dt_pixel_cache_entry_t *final_entry = NULL;
1750 void *final_buf = NULL;
1751 if(!requested_backbuf)
1752 {
1754 }
1756 &final_entry, pipe->devid, NULL)
1757 && !IS_NULL_PTR(final_buf))
1758 {
1759 _update_backbuf_cache_reference(pipe, roi, final_entry);
1761 }
1762 else
1763 {
1765 "[picker/rec] final output cache missing pipe=%s hash=%" PRIu64 " history=%" PRIu64
1766 " devid=%d err=%d\n",
1768 dt_dev_pixelpipe_get_history_hash(pipe), pipe->devid, err);
1770 }
1771
1772 // Note : the last output (backbuf) of the pixelpipe cache is internally locked
1773 // Whatever consuming it will need to unlock it.
1774 }
1775 }
1776
1778
1779 // release resources:
1780 if(pipe->forms)
1781 {
1782 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_form_unref);
1783 pipe->forms = NULL;
1784 }
1785 if(pipe->devid >= 0)
1786 {
1788 pipe->devid = -1;
1789 }
1790
1791 // terminate
1793
1794 // If an intermediate module set that, be sure to reset it at the end
1795 pipe->flush_cache = FALSE;
1796 return err;
1797}
1798
1799// clang-format off
1800// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1801// vim: shiftwidth=2 expandtab tabstop=2 cindent
1802// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1803// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#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)
static void * dt_atomic_exch_ptr(dt_atomic_ptr *var, void *value)
Definition atomic.h:80
atomic_int dt_atomic_int
Definition atomic.h:68
int levels(struct dt_imageio_module_data_t *data)
Definition avif.c:641
void tiling_callback_blendop(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
Definition blend.c:1720
dt_iop_colorspace_type_t dt_develop_blend_colorspace(const dt_dev_pixelpipe_iop_t *const piece, dt_iop_colorspace_type_t cst)
Definition blend.c:234
@ DEVELOP_MASK_DISABLED
Definition blend.h:112
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
dt_iop_colorspace_type_t
@ IOP_CS_RAW
@ IOP_CS_LCH
@ IOP_CS_JZCZHZ
@ IOP_CS_RGB
@ IOP_CS_HSL
@ IOP_CS_LAB
@ IOP_CS_NONE
void dt_ioppr_cleanup_profile_info(dt_iop_order_iccprofile_info_t **profile_info)
Release a profile info: its tone-curve LUTs, the struct itself, and the caller's pointer – must be ca...
const float f
static const float const float const float min
const float max
void dt_print_mem_usage()
Definition darktable.c:2586
void dt_dev_free_history_item(gpointer data)
Release a reference to a history item (used as GList free callback).
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)
void dt_pixelpipe_get_global_hash(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_get_roi_in(dt_dev_pixelpipe_t *pipe, const struct dt_iop_roi_t roi_out)
dt_dev_roi_request_t dt_dev_roi_request_neutral(void)
The value a pipe carries before the worker has latched anything.
void dt_dev_set_backbuf(dt_backbuf_t *backbuf, const int width, const int height, const size_t bpp, const int64_t hash, const int64_t history_hash)
Definition develop.c:2134
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1645
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:122
const int res
Definition dtpthread.h:351
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
Definition dtpthread.h:104
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:132
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition format.h:75
@ IOP_CS_RGB_DISPLAY
Definition format.h:71
dt_iop_buffer_type_t
Definition format.h:54
@ TYPE_FLOAT
Definition format.h:56
@ TYPE_UNKNOWN
Definition format.h:55
@ TYPE_UINT8
Definition format.h:58
@ TYPE_UINT16
Definition format.h:57
dt_pthread_mutex_t * dt_pipeline_threadsafe_mutex(void)
Definition darktable.c:604
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition hash.h:55
#define UNKNOWN_IMAGE
Definition image.h:78
int bpp
@ IMAGEIO_RGB
@ IMAGEIO_INT8
gboolean dt_iop_is_raster_mask_used(dt_iop_module_t *module, int id)
Definition imageop.c:1825
void dt_iop_init_pipe(struct dt_iop_module_t *module, struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece)
Build one pixelpipe node's module-owned storage.
Definition imageop.c:604
void dt_iop_cleanup_pipe(struct dt_iop_module_t *module, struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece)
Release module-owned resources for one pixelpipe node.
Definition imageop.c:641
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_TAKE_NO_INPUT
Definition imageop.h:195
@ IOP_FLAGS_CPU_WRITES_OPENCL
Definition imageop.h:199
@ IOP_TAG_DISTORT
Definition imageop.h:170
GList * dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
Deep-copy an order list.
Definition iop_order.c:1970
_lib_location_type_t type
Definition location.c:1
@ DT_DEBUG_OPENCL
Definition logging.h:57
@ DT_DEBUG_PIPE
Definition logging.h:76
@ DT_DEBUG_PIPECACHE
Definition logging.h:56
@ DT_DEBUG_NAN
Definition logging.h:61
@ DT_DEBUG_MEMORY
Definition logging.h:59
@ DT_DEBUG_VERBOSE
Definition logging.h:78
@ DT_DEBUG_ALWAYS
Definition logging.h:49
@ DT_DEBUG_DEV
Definition logging.h:53
@ DT_DEBUG_NOCACHE_REUSE
Definition logging.h:80
int32_t dt_get_debug_flags(void)
Definition darktable.c:2085
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#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 macros.h:96
GList * dt_masks_snapshot_current_forms(dt_develop_t *dev, gboolean reset_changed)
void dt_masks_form_unref(dt_masks_form_t *form)
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
float iscale
Definition mipmap_cache.c:2
size_t size
Definition mipmap_cache.c:3
dt_mipmap_size_t
void dt_opencl_events_reset(const int devid)
Definition opencl.c:3416
cl_int dt_opencl_events_flush(const int devid, const int reset)
Definition opencl.c:3511
void dt_opencl_check_tuning(const int devid)
Definition opencl.c:3113
int dt_opencl_report_pipe_error(void)
Record that a pipe run failed on OpenCL, and decide whether to give up on it.
Definition opencl.c:2035
int dt_opencl_reserve_device_for_pipe(const int pipetype)
Reserve a device for a pipe run: choose a free one by the pipe's priority list and take its lock....
Definition opencl.c:1887
int dt_opencl_update_settings(void)
Definition opencl.c:3279
void dt_opencl_release_device(const int devid)
Release a device reserved by either reserve function.
Definition opencl.c:1972
#define __OMP_FOR_SIMD__(...)
Definition openmp.h:97
const char * name
Definition pdf.h:90
void dt_pipeline_message(const char *format,...)
void dt_pipeline_busy_printf(const char *format,...)
void dt_pipeline_busy_clear(void)
Processing finished; clear the banner.
Everything the pixel pipeline says to whoever is watching: messages for the user, and the busy banner...
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
uint64_t dt_dev_pixelpipe_raster_mask_hash(const dt_dev_pixelpipe_iop_t *piece, const int raster_mask_id)
Definition pixelpipe.c:53
void dt_dev_clear_rawdetail_mask(dt_dev_pixelpipe_t *pipe)
Release the side-band detail mask cache reference currently owned by the pipeline.
Definition pixelpipe.c:64
@ DT_REQUEST_ON
Definition pixelpipe.h:52
@ DT_REQUEST_ONLY_IN_GUI
Definition pixelpipe.h:53
dt_dev_pixelpipe_type_t
Definition pixelpipe.h:40
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:45
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:42
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:44
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:43
void dt_dev_pixelpipe_cache_wrlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the write lock on the entry.
int dt_dev_pixelpipe_cache_invalidate_hashes(const uint64_t *hashes, const size_t count)
Invalidate cache lines matching an explicit list of hashes.
void dt_dev_pixelpipe_cache_auto_destroy_apply(dt_pixel_cache_entry_t *cache_entry)
Free the entry if it has the flag "auto_destroy". See dt_dev_pixelpipe_cache_flag_auto_destroy()....
void * dt_pixel_cache_entry_get_data(dt_pixel_cache_entry_t *entry)
void dt_dev_pixelpipe_cache_ref_count_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
gboolean dt_dev_pixelpipe_cache_peek(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.
void dt_dev_pixelpipe_cache_rdlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
void dt_dev_pixelpipe_cache_flush_clmem_for_pipe(const int devid)
Like dt_dev_pixelpipe_cache_flush_clmem(), for callers that do not hold the device lock (dt_opencl_re...
void dt_dev_pixelpipe_cache_release_cl_buffer(void **cl_mem_buffer, dt_pixel_cache_entry_t *cache_entry, void *host_ptr, const gboolean cache_device)
Release or cache an OpenCL image associated with a host cache line.
void dt_dev_pixelpipe_cache_print(void)
void dt_dev_pixelpipe_cache_flush_entry_clmem(dt_pixel_cache_entry_t *entry)
Flush all reusable OpenCL payloads cached on one cache entry.
gboolean dt_dev_pixelpipe_cache_restore_host_payload(dt_pixel_cache_entry_t *cache_entry, const int preferred_devid, void **data)
Materialize a host payload for a live cache entry from its cached device payload.
dt_pixel_cache_entry_t * dt_dev_pixelpipe_cache_get_entry(const uint64_t hash)
Get an internal reference to the cache entry matching hash. If you are going to access this entry mor...
dt_dev_pixelpipe_cache_writable_status_t dt_dev_pixelpipe_cache_get_writable(const uint64_t hash, const size_t size, const char *name, const int id, const gboolean alloc, const gboolean allow_rekey_reuse, const dt_pixel_cache_entry_t *reuse_hint, void **data, dt_pixel_cache_entry_t **entry)
int dt_dev_pixelpipe_cache_remove(const gboolean force, dt_pixel_cache_entry_t *cache_entry)
Arbitrarily remove the cache entry matching hash. Entries having a reference count > 0 (inter-thread ...
size_t dt_pixel_cache_entry_get_size(dt_pixel_cache_entry_t *entry)
Peek the size (in bytes) reserved for the host buffer of a cache entry.
void dt_dev_pixelpipe_cache_unref_hash(const uint64_t hash)
Find the entry matching hash, and decrease its ref_count if found.
gboolean dt_dev_pixelpipe_cache_ref_entry_by_hash(const uint64_t hash, void **data, dt_pixel_cache_entry_t **entry)
Resolve and retain an existing cache entry by hash.
void dt_dev_pixelpipe_cache_flag_auto_destroy(dt_pixel_cache_entry_t *cache_entry)
Flag the cache entry as "auto_destroy". This is useful for short-lived/disposable cache entries,...
const char * dt_pixelpipe_cache_set_current_module(const char *module)
Set the current module name for cache diagnostics (thread-local).
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
dt_dev_pixelpipe_cache_writable_status_t
@ DT_DEV_PIXELPIPE_CACHE_WRITABLE_REKEYED
@ DT_DEV_PIXELPIPE_CACHE_WRITABLE_CREATED
@ DT_DEV_PIXELPIPE_CACHE_WRITABLE_EXACT_HIT
int pixelpipe_process_on_CPU(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const dt_dev_pixelpipe_iop_t *previous_piece, dt_develop_tiling_t *tiling, dt_pixelpipe_flow_t *pixelpipe_flow, gboolean *const cache_output, dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
int pixelpipe_process_on_GPU(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const dt_dev_pixelpipe_iop_t *previous_piece, dt_develop_tiling_t *tiling, dt_pixelpipe_flow_t *pixelpipe_flow, gboolean *const cache_output, dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
void dt_dev_pixelpipe_gpu_flush_host_pinned_images(dt_dev_pixelpipe_t *pipe, void *host_ptr, dt_pixel_cache_entry_t *cache_entry, const char *reason)
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)
dt_pixelpipe_blend_transform_t dt_dev_pixelpipe_transform_for_blend(const dt_iop_module_t *const self, const dt_dev_pixelpipe_iop_t *const piece, const dt_iop_buffer_dsc_t *const output_dsc)
static void _trace_buffer_content(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module, const char *phase, const void *buffer, const dt_iop_buffer_dsc_t *format, const dt_iop_roi_t *roi)
void dt_dev_pixelpipe_disable_before(dt_dev_pixelpipe_t *pipe, const char *op)
int dt_dev_pixelpipe_init_dummy(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
static gboolean _is_focused_realtime_gui_module(const dt_dev_pixelpipe_t *pipe, const dt_develop_t *dev, const dt_iop_module_t *module)
void dt_dev_pixelpipe_reset_reentry(dt_dev_pixelpipe_t *pipe)
int dt_dev_pixelpipe_init_cached(dt_dev_pixelpipe_t *pipe)
static void _update_backbuf_cache_reference(dt_dev_pixelpipe_t *pipe, dt_iop_roi_t roi, dt_pixel_cache_entry_t *entry)
gboolean dt_dev_pixelpipe_has_reentry(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_disable_after(dt_dev_pixelpipe_t *pipe, const char *op)
int dt_dev_pixelpipe_init(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
gboolean dt_dev_pixelpipe_cache_gpu_device_buffer(const dt_dev_pixelpipe_t *pipe, const dt_pixel_cache_entry_t *cache_entry)
gboolean dt_dev_pixelpipe_get_realtime(const dt_dev_pixelpipe_t *pipe)
static const char * _debug_type_to_string(const dt_iop_buffer_type_t type)
static const char * _debug_cst_to_string(const int cst)
#define KILL_SWITCH_ABORT
void dt_dev_pixelpipe_set_icc(dt_dev_pixelpipe_t *pipe, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent)
static int _abort_module_shutdown_cleanup(dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_module_t *module, const uint64_t input_hash, const void *input, dt_pixel_cache_entry_t *input_entry, const uint64_t output_hash, void **output, void **cl_mem_output, dt_pixel_cache_entry_t *output_entry)
static int dt_dev_pixelpipe_process_rec(dt_dev_pixelpipe_t *pipe, uint64_t *out_hash, const dt_dev_pixelpipe_iop_t **out_piece, GList *pieces, int pos)
char * dt_pixelpipe_get_pipe_name(dt_dev_pixelpipe_type_t pipe_type)
static void _print_opencl_errors(int error, dt_dev_pixelpipe_t *pipe)
static void _trace_cache_owner(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module, const char *phase, const char *slot, const uint64_t requested_hash, const void *buffer, const dt_pixel_cache_entry_t *entry, const gboolean verbose)
static GList * _get_requested_piece_node(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module, int *pos)
int dt_dev_pixelpipe_init_preview(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
static void _print_nan_debug(dt_dev_pixelpipe_t *pipe, void *cl_mem_output, void *output, const dt_iop_roi_t *roi_out, dt_iop_buffer_dsc_t *out_format, dt_iop_module_t *module)
void dt_dev_pixelpipe_create_nodes(dt_dev_pixelpipe_t *pipe)
int dt_dev_pixelpipe_init_thumbnail(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
int dt_dev_pixelpipe_init_export(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev, int levels, gboolean store_masks)
#define KILL_SWITCH_PIPE
void dt_dev_pixelpipe_cleanup(dt_dev_pixelpipe_t *pipe)
gboolean dt_dev_pixelpipe_unset_reentry(dt_dev_pixelpipe_t *pipe, uint64_t hash)
Remove the re-entry pipeline flag, only if the object identifier is the one that set it....
int dt_dev_pixelpipe_process(dt_dev_pixelpipe_t *pipe, dt_iop_roi_t roi)
void dt_dev_pixelpipe_set_mask_rasterization_step(dt_dev_pixelpipe_t *pipe, const int step)
Set the drawn-mask rasterisation step for the frame this pipe is about to run.
static void _print_perf_debug(dt_dev_pixelpipe_t *pipe, const dt_pixelpipe_flow_t pixelpipe_flow, dt_dev_pixelpipe_iop_t *piece, dt_iop_module_t *module, const gboolean recycled_output_cacheline, dt_times_t *start)
static void _uint8_to_float(const uint8_t *const input, float *const output, const size_t width, const size_t height, const size_t chan)
void dt_dev_pixelpipe_debug_dump_module_io(dt_dev_pixelpipe_t *pipe, dt_iop_module_t *module, const char *stage, const gboolean is_cl, const dt_iop_buffer_dsc_t *in_dsc, const dt_iop_buffer_dsc_t *out_dsc, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out, const size_t in_bpp, const size_t out_bpp, const int cst_before, const int cst_after)
#define KILL_SWITCH_AND_FLUSH_CACHE
gboolean dt_dev_pixelpipe_set_reentry(dt_dev_pixelpipe_t *pipe, uint64_t hash)
Set the re-entry pipeline flag, only if no object is already capturing it.
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 dt_dev_pixelpipe_cache_request_t dt_dev_pixelpipe_get_cache_request(const dt_dev_pixelpipe_t *pipe)
static void dt_dev_pixelpipe_set_hash(dt_dev_pixelpipe_t *pipe, const uint64_t hash)
static const struct dt_iop_module_t * dt_dev_pixelpipe_get_cache_request_module(const dt_dev_pixelpipe_t *pipe)
static uint64_t dt_dev_pixelpipe_get_hash(const dt_dev_pixelpipe_t *pipe)
static void dt_dev_pixelpipe_reset_cache_request(dt_dev_pixelpipe_t *pipe)
@ 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)
static uint64_t dt_dev_backbuf_get_hash(const dt_backbuf_t *backbuf)
dt_dev_pixelpipe_cache_request_t
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_NONE
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE
static void dt_dev_pixelpipe_set_changed(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_change_t v)
static gboolean dt_dev_pixelpipe_has_shutdown(const dt_dev_pixelpipe_t *pipe)
dt_pixelpipe_flow_t
@ PIXELPIPE_FLOW_HISTOGRAM_ON_GPU
@ PIXELPIPE_FLOW_HISTOGRAM_NONE
@ PIXELPIPE_FLOW_PROCESSED_ON_CPU
@ PIXELPIPE_FLOW_PROCESSED_WITH_TILING
@ PIXELPIPE_FLOW_PROCESSED_ON_GPU
@ PIXELPIPE_FLOW_NONE
@ PIXELPIPE_FLOW_BLENDED_ON_CPU
@ PIXELPIPE_FLOW_HISTOGRAM_ON_CPU
@ PIXELPIPE_FLOW_BLENDED_ON_GPU
static void _reset_piece_cache_entry(dt_dev_pixelpipe_iop_t *piece)
Drop the writable-reuse snapshot attached to a pipeline piece.
static gboolean _bypass_cache(const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Tell whether the current pipeline state forbids keeping this module output in cache.
dt_pixelpipe_blend_transform_t
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_INPUT
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_NONE
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT
Raster-mask retrieval and transport through already-processed pipeline nodes.
Raw-detail mask transport helpers.
The colour-profile vocabulary, and nothing else.
dt_iop_color_intent_t
ICC rendering intent, as stored in iop params and in conf.
@ DT_INTENT_LAST
Count of real intents, and the codebase's "unset" marker – never a user choice.
dt_colorspaces_color_profile_type_t
@ DT_COLORSPACE_NONE
No profile / "take it from the image settings". Never matches a list entry.
void dt_sentry_set_processed_image(const struct dt_image_t *img, const char *pipeline)
Definition sentry.c:696
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ 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:188
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
const float uint32_t state[4]
const float r
unsigned __int64 uint64_t
Definition strptime.c:75
dt_dev_request_flags_t request_histogram
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_atomic_ptr last_history_item
dt_colorspaces_color_profile_type_t icc_type
struct dt_iop_order_iccprofile_info_t * input_profile_info
gboolean gui_observable_source
uint64_t last_history_hash
dt_pthread_mutex_t busy_mutex
dt_imageio_levels_t levels
dt_atomic_int realtime
dt_backbuf_t backbuf
dt_mipmap_size_t size
dt_iop_color_intent_t icc_intent
dt_atomic_int shutdown
int mask_rasterization_step
Rasterisation step for drawn masks, in image pixels. Always >= 1.
dt_dev_pixelpipe_type_t type
gboolean store_all_raster_masks
uint64_t rawdetail_mask_hash
GArray * raster_mask_hashes
dt_dev_roi_request_store_t roi_request
The viewport snapshot this run was planned from.
struct dt_develop_t * dev
struct dt_iop_order_iccprofile_info_t * owned_input_profile_info
dt_dev_roi_request_t value
int32_t gui_attached
Definition develop.h:167
GList * iop_order_list
Definition develop.h:275
dt_image_t image_storage
Definition develop.h:225
GList * iop
Definition develop.h:269
struct dt_develop_t::@13 color_picker
Authoritative darkroom color-picker state.
struct dt_develop_t::@20 progress
struct dt_iop_module_t * pending_module
Definition develop.h:390
int completed
Definition develop.h:479
struct dt_dev_pixelpipe_t * pending_pipe
Definition develop.h:391
uint64_t piece_hash
Definition develop.h:384
unsigned overhead
Definition tiling.h:50
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85
dt_dev_operation_t op
Definition imageop.h:259
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
uint64_t hash
void * data
uint64_t producer_node_key
uint64_t dt_supervisor_node_key(const int pipe_type, const char *op_name, const int multi_priority)
Definition supervisor.c:165
void dt_supervisor_cacheline_create(const uint64_t hash, const uint64_t node_hash, const uint64_t param_hash, const uint64_t input_hash, const char *op_name, const int multi_priority, const int iop_order, const int pipe_type, const int32_t imgid, const int roi_w, const int roi_h, const int devid, const size_t size, const char *name)
Definition supervisor.c:891
void dt_supervisor_node(const dt_sv_op_t op, const uint64_t node_hash, const uint64_t param_hash, const uint64_t predecessor_hash, const char *op_name, const int multi_priority, const int iop_order, const int pipe_type, const int32_t imgid)
Definition supervisor.c:856
void dt_supervisor_backbuf(const dt_sv_op_t op, const uint64_t hash, const uint64_t history_hash, const int w, const int h, const int bpp, const int pipe_type, const int devid)
@ DT_SV_UPDATE
Definition supervisor.h:79
@ DT_SV_CREATE
Definition supervisor.h:78
@ DT_SV_DELETE
Definition supervisor.h:81
static gboolean dt_supervisor_active(void)
Definition supervisor.h:95
void dt_telemetry_record_file_type(const struct dt_image_t *img, const char *pipeline)
Definition telemetry.c:464
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
void dt_show_times(const dt_times_t *start, const char *prefix)
Definition darktable.c:2138
static void dt_get_times(dt_times_t *t)
Definition times.h:50
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...) __attribute__((format(printf
static const int mask_id
Definition useless.c:214
static const char * mask_name
Definition useless.c:215