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*/
54#include "common/colorspaces.h"
55#include "common/darktable.h"
56#include "common/histogram.h"
57#include "common/imageio.h"
58#include "common/atomic.h"
59#include "common/opencl.h"
60#include "common/iop_order.h"
61#include "control/control.h"
62#include "control/conf.h"
63#include "control/signal.h"
64#include "develop/blend.h"
66#include "develop/format.h"
68#include "common/sentry.h"
69#include "common/telemetry.h"
70#include "develop/pixelpipe.h"
72#include "develop/supervisor.h"
76#include "develop/tiling.h"
77#include "develop/masks.h"
78#include "gui/gtk.h"
79#include "libs/colorpicker.h"
80#include "libs/lib.h"
82
83#include <assert.h>
84#include <inttypes.h>
85#include <math.h>
86#include <stdint.h>
87#include <stdlib.h>
88#include <string.h>
89#include <strings.h>
90#include <unistd.h>
91
94
95static void _trace_cache_owner(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module,
96 const char *phase, const char *slot, const uint64_t requested_hash,
97 const void *buffer, const dt_pixel_cache_entry_t *entry,
98 const gboolean verbose)
99{
100 if(!(darktable.unmuted & DT_DEBUG_PIPECACHE)) return;
101 if(!(darktable.unmuted & DT_DEBUG_VERBOSE)) return;
102
104 "[pixelpipe_owner] pipe=%s module=%s phase=%s slot=%s req=%" PRIu64
105 " entry=%" PRIu64 "/%" PRIu64 " refs=%i auto=%i data=%p buf=%p name=%s\n",
106 pipe ? dt_pixelpipe_get_pipe_name(pipe->type) : "-",
107 module ? module->op : "base",
108 phase ? phase : "-",
109 slot ? slot : "-",
110 requested_hash,
111 entry ? entry->hash : DT_PIXELPIPE_CACHE_HASH_INVALID,
112 entry ? entry->serial : 0,
113 entry ? dt_atomic_get_int((dt_atomic_int *)&entry->refcount) : -1,
114 entry ? entry->auto_destroy : -1,
115 entry ? entry->data : NULL,
116 buffer,
117 (entry && entry->name) ? entry->name : "-");
118}
119
120
121static void _trace_buffer_content(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module,
122 const char *phase, const void *buffer,
123 const dt_iop_buffer_dsc_t *format, const dt_iop_roi_t *roi)
124{
125 if(!(darktable.unmuted & DT_DEBUG_PIPECACHE)) return;
126 if(!(darktable.unmuted & DT_DEBUG_VERBOSE)) return;
127 if(IS_NULL_PTR(buffer) || IS_NULL_PTR(format) || IS_NULL_PTR(roi)) return;
128 if(roi->width <= 0 || roi->height <= 0) return;
129
130 const size_t pixels = (size_t)roi->width * (size_t)roi->height;
131 const unsigned int channels = format->channels;
132
133 if(format->datatype == TYPE_FLOAT && channels >= 1)
134 {
135 const float *in = (const float *)buffer;
136 float minv[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
137 float maxv[4] = { -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX };
138 size_t nonfinite = 0;
139 size_t near_black = 0;
140
141 for(size_t k = 0; k < pixels; k++, in += channels)
142 {
143 gboolean finite = TRUE;
144 for(unsigned int c = 0; c < MIN(channels, 4U); c++)
145 {
146 if(!isfinite(in[c]))
147 {
148 finite = FALSE;
149 continue;
150 }
151 minv[c] = fminf(minv[c], in[c]);
152 maxv[c] = fmaxf(maxv[c], in[c]);
153 }
154
155 if(!finite)
156 {
157 nonfinite++;
158 continue;
159 }
160
161 const float energy = fabsf(in[0]) + ((channels > 1) ? fabsf(in[1]) : 0.0f)
162 + ((channels > 2) ? fabsf(in[2]) : 0.0f);
163 if(energy < 1e-6f) near_black++;
164 }
165
166 const uint64_t content_hash = dt_hash(5381, (const char *)buffer, pixels * channels * sizeof(float));
167
169 "[pixelpipe_stats] pipe=%s module=%s phase=%s type=float ch=%u roi=%dx%d ptr=%p "
170 "content_hash=%" PRIu64 " "
171 "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",
172 dt_pixelpipe_get_pipe_name(pipe->type), module->op, phase ? phase : "-",
173 channels, roi->width, roi->height, buffer, content_hash,
174 minv[0], (channels > 1) ? minv[1] : 0.0f, (channels > 2) ? minv[2] : 0.0f,
175 maxv[0], (channels > 1) ? maxv[1] : 0.0f, (channels > 2) ? maxv[2] : 0.0f,
176 (channels > 3) ? minv[3] : 0.0f, (channels > 3) ? maxv[3] : 0.0f,
177 near_black, pixels, nonfinite);
178 }
179 else if(format->datatype == TYPE_UINT8 && channels >= 1)
180 {
181 const uint8_t *in = (const uint8_t *)buffer;
182 int minv[4] = { 255, 255, 255, 255 };
183 int maxv[4] = { 0, 0, 0, 0 };
184 size_t near_black = 0;
185
186 for(size_t k = 0; k < pixels; k++, in += channels)
187 {
188 for(unsigned int c = 0; c < MIN(channels, 4U); c++)
189 {
190 minv[c] = MIN(minv[c], in[c]);
191 maxv[c] = MAX(maxv[c], in[c]);
192 }
193
194 const int energy = in[0] + ((channels > 1) ? in[1] : 0) + ((channels > 2) ? in[2] : 0);
195 if(energy == 0) near_black++;
196 }
197
198 const uint64_t content_hash = dt_hash(5381, (const char *)buffer, pixels * channels * sizeof(uint8_t));
199
201 "[pixelpipe_stats] pipe=%s module=%s phase=%s type=u8 ch=%u roi=%dx%d ptr=%p "
202 "content_hash=%" PRIu64 " "
203 "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",
204 dt_pixelpipe_get_pipe_name(pipe->type), module->op, phase ? phase : "-",
205 channels, roi->width, roi->height, buffer, content_hash,
206 minv[0], (channels > 1) ? minv[1] : 0, (channels > 2) ? minv[2] : 0,
207 maxv[0], (channels > 1) ? maxv[1] : 0, (channels > 2) ? maxv[2] : 0,
208 (channels > 3) ? minv[3] : 0, (channels > 3) ? maxv[3] : 0,
209 near_black, pixels);
210 }
211}
212
214 dt_iop_module_t *module, const uint64_t input_hash,
215 const void *input, dt_pixel_cache_entry_t *input_entry,
216 const uint64_t output_hash, void **output,
217 void **cl_mem_output, dt_pixel_cache_entry_t *output_entry)
218{
219 _trace_cache_owner(pipe, module, "shutdown-drop", "input", input_hash, input, input_entry, FALSE);
220 _trace_cache_owner(pipe, module, "shutdown-drop", "output", output_hash,
221 output ? *output : NULL, output_entry, FALSE);
222
224
225 if(!IS_NULL_PTR(input_entry))
226 {
229 }
230
231 if(!IS_NULL_PTR(output_entry))
232 {
234
237 }
238
239 if(output) *output = NULL;
240
241 if(!IS_NULL_PTR(*cl_mem_output))
242 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_output, NULL, NULL, FALSE);
243
244 return 1;
245}
246
247static inline gboolean _is_focused_realtime_gui_module(const dt_dev_pixelpipe_t *pipe,
248 const dt_develop_t *dev,
249 const dt_iop_module_t *module)
250{
251 return (pipe->type == DT_DEV_PIXELPIPE_FULL || pipe->type == DT_DEV_PIXELPIPE_PREVIEW)
252 && pipe->realtime && dev && module && dev->gui_module == module;
253}
254
255
257 const dt_pixel_cache_entry_t *cache_entry)
258{
259 return (pipe->realtime
260 || (cache_entry && IS_NULL_PTR(dt_pixel_cache_entry_get_data((dt_pixel_cache_entry_t *)cache_entry)))
261 || !_bypass_cache(pipe, NULL));
262}
263
265{
266 char *r = NULL;
267
268 switch(pipe_type)
269 {
271 r = _("preview");
272 break;
274 r = _("full");
275 break;
277 r = _("thumbnail");
278 break;
280 r = _("export");
281 break;
282 default:
283 r = _("invalid");
284 }
285 return r;
286}
287
288
289inline static void _uint8_to_float(const uint8_t *const input, float *const output,
290 const size_t width, const size_t height, const size_t chan)
291{
292 __OMP_FOR_SIMD__(aligned(input, output: 64) )
293 for(size_t k = 0; k < height * width; k++)
294 {
295 const size_t index = k * chan;
296 // Warning: we take BGRa and put it back into RGBa
297 output[index + 0] = (float)input[index + 2] / 255.f;
298 output[index + 1] = (float)input[index + 1] / 255.f;
299 output[index + 2] = (float)input[index + 0] / 255.f;
300 output[index + 3] = 0.f;
301 }
302}
303
304static const char *_debug_cst_to_string(const int cst)
305{
306 switch(cst)
307 {
308 case IOP_CS_RAW:
309 return "raw";
310 case IOP_CS_LAB:
311 return "lab";
312 case IOP_CS_RGB:
313 return "rgb";
315 return "display-rgb";
316 case IOP_CS_LCH:
317 return "lch";
318 case IOP_CS_HSL:
319 return "hsl";
320 case IOP_CS_JZCZHZ:
321 return "jzczhz";
322 case IOP_CS_NONE:
323 return "none";
324 default:
325 return "unknown";
326 }
327}
328
330{
331 switch(type)
332 {
333 case TYPE_FLOAT:
334 return "float";
335 case TYPE_UINT16:
336 return "uint16";
337 case TYPE_UINT8:
338 return "uint8";
339 case TYPE_UNKNOWN:
340 default:
341 return "unknown";
342 }
343}
344
346 const gboolean is_cl,
347 const dt_iop_buffer_dsc_t *in_dsc, const dt_iop_buffer_dsc_t *out_dsc,
348 const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out,
349 const size_t in_bpp, const size_t out_bpp,
350 const int cst_before, const int cst_after)
351{
352 if(!(darktable.unmuted & DT_DEBUG_PIPE)) return;
353 if(!(darktable.unmuted & DT_DEBUG_VERBOSE)) return;
354 const char *module_name = module ? module->op : "base";
355 const char *pipe_name = dt_pixelpipe_get_pipe_name(pipe->type);
356 const char *stage_name = stage ? stage : "process";
357
358 if(!IS_NULL_PTR(in_dsc) && !IS_NULL_PTR(out_dsc))
359 {
361 "[pixelpipe] %s %s %s %s: in cst=%s->%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d | "
362 "out cst=%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d\n",
363 pipe_name, module_name, is_cl ? "cl" : "cpu", stage_name,
364 _debug_cst_to_string(cst_before), _debug_cst_to_string(cst_after),
365 in_dsc->channels, _debug_type_to_string(in_dsc->datatype), in_bpp,
366 roi_in ? roi_in->width : 0, roi_in ? roi_in->height : 0,
367 _debug_cst_to_string(out_dsc->cst), out_dsc->channels, _debug_type_to_string(out_dsc->datatype),
368 out_bpp, roi_out ? roi_out->width : 0, roi_out ? roi_out->height : 0);
369 }
370 else if(!IS_NULL_PTR(out_dsc))
371 {
373 "[pixelpipe] %s %s %s %s: out cst=%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d\n",
374 pipe_name, module_name, is_cl ? "cl" : "cpu", stage_name,
375 _debug_cst_to_string(out_dsc->cst), out_dsc->channels, _debug_type_to_string(out_dsc->datatype),
376 out_bpp, roi_out ? roi_out->width : 0, roi_out ? roi_out->height : 0);
377 }
378}
379
380
381int dt_dev_pixelpipe_init_export(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev, int levels, gboolean store_masks)
382{
383 const int res = dt_dev_pixelpipe_init_cached(pipe);
386 pipe->levels = levels;
387 pipe->store_all_raster_masks = store_masks;
388 pipe->dev = dev;
389 return res;
390}
391
393{
394 const int res = dt_dev_pixelpipe_init_cached(pipe);
396 pipe->no_cache = TRUE;
397 pipe->dev = dev;
398 return res;
399}
400
402{
403 const int res = dt_dev_pixelpipe_init_cached(pipe);
405 pipe->no_cache = TRUE;
406 pipe->dev = dev;
407 return res;
408}
409
411{
412 // Init with the size of MIPMAP_F
413 const int res = dt_dev_pixelpipe_init_cached(pipe);
416
417 // Needed for caching
419 pipe->dev = dev;
420 return res;
421}
422
424{
425 const int res = dt_dev_pixelpipe_init_cached(pipe);
427
428 // Needed for caching
430 pipe->dev = dev;
431 return res;
432}
433
435{
436 // Set everything to 0 = NULL = FALSE
437 memset(pipe, 0, sizeof(dt_dev_pixelpipe_t));
438
439 // Set only the stuff that doesn't take 0 as default
440 pipe->devid = -1;
441 pipe->last_devid = -1;
445 dt_dev_set_backbuf(&pipe->backbuf, 0, 0, 0, -1, -1);
448 pipe->raster_mask_hashes = g_array_new(FALSE, FALSE, sizeof(uint64_t));
449
451 pipe->iscale = 1.0f;
455
457 dt_pthread_mutex_init(&(pipe->busy_mutex), NULL);
458
461
463 return 1;
464}
465
467{
468 if(IS_NULL_PTR(pipe)) return;
470}
471
473{
474 return pipe ? dt_atomic_get_int((dt_atomic_int *)&pipe->realtime) : FALSE;
475}
476
477void dt_dev_pixelpipe_set_input(dt_dev_pixelpipe_t *pipe, int32_t imgid, int width, int height,
479{
480 pipe->iwidth = width;
481 pipe->iheight = height;
482 pipe->iscale = iscale;
483 pipe->imgid = imgid;
484 pipe->dev->image_storage = pipe->dev->image_storage;
485 pipe->size = size;
486
487 // Nodes may already exist if this pipe was set up before its input size was known: export
488 // pipes call dt_dev_pixelpipe_create_nodes() before dt_dev_pixelpipe_set_input() (see
489 // common/imageio.c), and create_nodes() bakes pipe->iwidth/iheight into each piece at
490 // node-creation time. Without this, every piece stays stuck with whatever (zero) size the
491 // pipe had at that point, so any module reading piece->iwidth/iheight directly (toneequal,
492 // soften, ashift) silently gets 0 during export while darkroom pipes (whose input is set
493 // before their nodes are created) see the real size -- e.g. toneequal's blending radius
494 // collapses to 0 on export only, disabling its local smoothing there regardless of the
495 // module's actual settings.
496 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
497 {
499 if(IS_NULL_PTR(piece)) continue;
500 piece->iwidth = width;
501 piece->iheight = height;
502 }
503}
504
506 const gchar *icc_filename, dt_iop_color_intent_t icc_intent)
507{
508 pipe->icc_type = icc_type;
509 dt_free(pipe->icc_filename);
510 pipe->icc_filename = g_strdup(icc_filename ? icc_filename : "");
511 pipe->icc_intent = icc_intent;
512}
513
515{
516 /* Device-side cache payloads are only an acceleration layer. Drop the cl_mem
517 * objects this pipe produced on the device it last ran on -- but only that
518 * device, so we never touch cache entries another, still-running pipe holds
519 * on a different (or the same) OpenCL device. */
521
522 // blocks while busy and sets shutdown bit:
524 // so now it's safe to clean up cache:
525 const uint64_t old_backbuf_hash = dt_dev_backbuf_get_hash(&pipe->backbuf);
526 if(old_backbuf_hash != DT_PIXELPIPE_CACHE_HASH_INVALID)
527 {
528 /* Backbuffer ownership belongs to the pipeline, not its GUI consumers. Once the pipe itself is
529 * torn down, always release that keepalive ref and invalidate the published backbuffer metadata. */
531
532 if(pipe->no_cache)
533 {
534 dt_pixel_cache_entry_t *old_backbuf_entry
536 if(old_backbuf_entry)
537 {
540 }
541 }
542 }
547 dt_free(pipe->icc_filename);
548
550
552
553 // Every hash in this array corresponds to one cache reference acquired
554 // either while reopening current provider masks or while publishing a new
555 // one. Release the exact same number of references before destroying it.
556 for(guint k = 0; k < pipe->raster_mask_hashes->len; k++)
557 {
558 const uint64_t hash = g_array_index(pipe->raster_mask_hashes, uint64_t, k);
560 }
561 g_array_free(pipe->raster_mask_hashes, TRUE);
562 pipe->raster_mask_hashes = NULL;
563
564 if(pipe->forms)
565 {
566 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_form_unref);
567 pipe->forms = NULL;
568 }
569}
570
571
573{
575 {
576 pipe->reentry = TRUE;
577 pipe->reentry_hash = hash;
578 dt_print(DT_DEBUG_DEV, "[dev_pixelpipe] re-entry flag set for %" PRIu64 "\n", hash);
579 return TRUE;
580 }
581
582 return FALSE;
583}
584
585
587{
588 if(pipe->reentry_hash == hash)
589 {
590 pipe->reentry = FALSE;
592 dt_print(DT_DEBUG_DEV, "[dev_pixelpipe] re-entry flag unset for %" PRIu64 "\n", hash);
593 return TRUE;
594 }
595
596 return FALSE;
597}
598
600{
601 return pipe->reentry;
602}
603
610
612{
613 // destroy all nodes
614 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
615 {
617 if(IS_NULL_PTR(piece)) continue;
618 // printf("cleanup module `%s'\n", piece->module->name());
619 if(dt_supervisor_active() && piece->module)
621 dt_supervisor_node_key(pipe->type, piece->module->op, piece->module->multi_priority),
623 piece->module->op, piece->module->multi_priority, piece->module->iop_order,
624 pipe->type, pipe->imgid);
625 if(piece->module) dt_iop_cleanup_pipe(piece->module, pipe, piece);
626 dt_free(piece);
627 }
628 g_list_free(pipe->nodes);
629 pipe->nodes = NULL;
630 // and iop order
631 g_list_free_full(pipe->iop_order_list, dt_free_gpointer);
632 pipe->iop_order_list = NULL;
634}
635
637{
638 // check that the pipe was actually properly cleaned up after the last run
639 g_assert(IS_NULL_PTR(pipe->nodes));
640 g_assert(IS_NULL_PTR(pipe->iop_order_list));
642
643 // for all modules in dev (iterated in iop_order, so each is the predecessor of the next):
644 dt_iop_module_t *prev_module = NULL;
645 for(GList *modules = g_list_first(pipe->dev->iop); modules; modules = g_list_next(modules))
646 {
647 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
649 if(IS_NULL_PTR(piece)) continue;
650 piece->enabled = module->enabled;
651 piece->request_histogram = DT_REQUEST_ONLY_IN_GUI;
652 piece->histogram_params.bins_count = 256;
653 piece->iwidth = pipe->iwidth;
654 piece->iheight = pipe->iheight;
655 piece->module = module;
657 piece->blendop_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
658 piece->global_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
659 piece->global_mask_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
661 piece->cache_output_on_ram = TRUE;
662
663 // dsc_mask is static, single channel float image
664 piece->dsc_mask.channels = 1;
665 piece->dsc_mask.datatype = TYPE_FLOAT;
666 dt_iop_buffer_dsc_update_bpp(&piece->dsc_mask);
667
668 dt_iop_init_pipe(piece->module, pipe, piece);
669
670 pipe->nodes = g_list_append(pipe->nodes, piece);
671
672 // Topology node: created once here, immutable until the nodes are torn down.
673 // param_hash is bound later, at history synchronization (dt_supervisor_node UPDATE).
675 {
676 const uint64_t pred = prev_module
677 ? dt_supervisor_node_key(pipe->type, prev_module->op, prev_module->multi_priority)
680 dt_supervisor_node_key(pipe->type, module->op, module->multi_priority),
681 DT_PIXELPIPE_CACHE_HASH_INVALID, pred, module->op, module->multi_priority,
682 module->iop_order, pipe->type, pipe->imgid);
683 }
684 prev_module = module;
685 }
686}
687
689 const dt_dev_pixelpipe_iop_t *const piece,
690 const dt_iop_buffer_dsc_t *const output_dsc)
691{
696
697 const dt_iop_colorspace_type_t blend_cst = dt_develop_blend_colorspace(piece, output_dsc->cst);
699 if(piece->dsc_in.cst != blend_cst
702 if(output_dsc->cst != blend_cst
703 && !(dt_iop_colorspace_is_rgb(output_dsc->cst) && dt_iop_colorspace_is_rgb(blend_cst)))
705
706 return transforms;
707}
708
709#define KILL_SWITCH_ABORT \
710 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
711 { \
712 if(!IS_NULL_PTR(cl_mem_output)) \
713 { \
714 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_output, NULL, NULL, FALSE); \
715 } \
716 return 1; \
717 }
718
719// Once we have a cache, stopping computation before full completion
720// has good chances of leaving it corrupted. So we invalidate it.
721#define KILL_SWITCH_AND_FLUSH_CACHE \
722 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
723 { \
724 return _abort_module_shutdown_cleanup(pipe, piece, module, input_hash, input, input_entry, hash, &output, \
725 &cl_mem_output, output_entry); \
726 }
727
728static void _print_perf_debug(dt_dev_pixelpipe_t *pipe, const dt_pixelpipe_flow_t pixelpipe_flow,
730 const gboolean recycled_output_cacheline, dt_times_t *start)
731{
732 char histogram_log[32] = "";
733 if(!(pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_NONE))
734 {
735 snprintf(histogram_log, sizeof(histogram_log), ", collected histogram on %s",
736 (pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_ON_GPU
737 ? "GPU"
738 : pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_ON_CPU ? "CPU" : ""));
739 }
740
741 gchar *module_label = dt_history_item_get_name(module);
743 start, "[dev_pixelpipe]", "processed `%s' on %s%s%s%s, blended on %s [%s]", module_label,
744 pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_GPU
745 ? "GPU"
746 : pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_CPU ? "CPU" : "",
747 pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_WITH_TILING ? " with tiling" : "",
748 recycled_output_cacheline ? ", recycled cacheline" : "",
749 (!(pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_NONE) && (piece->request_histogram & DT_REQUEST_ON))
750 ? histogram_log
751 : "",
752 pixelpipe_flow & PIXELPIPE_FLOW_BLENDED_ON_GPU
753 ? "GPU"
754 : pixelpipe_flow & PIXELPIPE_FLOW_BLENDED_ON_CPU ? "CPU" : "",
756 dt_free(module_label);
757}
758
759
760static void _print_nan_debug(dt_dev_pixelpipe_t *pipe, void *cl_mem_output, void *output,
761 const dt_iop_roi_t *roi_out, dt_iop_buffer_dsc_t *out_format,
762 dt_iop_module_t *module)
763{
764 if(!(darktable.unmuted & DT_DEBUG_NAN)) return;
765 if(!(darktable.unmuted & DT_DEBUG_VERBOSE)) return;
766 if((darktable.unmuted & DT_DEBUG_NAN) && strcmp(module->op, "gamma") != 0)
767 {
768 gchar *module_label = dt_history_item_get_name(module);
769
770 if(out_format->datatype == TYPE_FLOAT && out_format->channels == 4)
771 {
772 int hasinf = 0, hasnan = 0;
773 dt_aligned_pixel_t min = { FLT_MAX };
774 dt_aligned_pixel_t max = { FLT_MIN };
775
776 for(int k = 0; k < 4 * roi_out->width * roi_out->height; k++)
777 {
778 if((k & 3) < 3)
779 {
780 float f = ((float *)(output))[k];
781 if(isnan(f))
782 hasnan = 1;
783 else if(!isfinite(f))
784 hasinf = 1;
785 else
786 {
787 min[k & 3] = fmin(f, min[k & 3]);
788 max[k & 3] = fmax(f, max[k & 3]);
789 }
790 }
791 }
792 if(hasnan)
793 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs NaNs! [%s]\n", module_label,
795 if(hasinf)
796 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs non-finite floats! [%s]\n", module_label,
798 fprintf(stderr, "[dev_pixelpipe] module `%s' min: (%f; %f; %f) max: (%f; %f; %f) [%s]\n", module_label,
799 min[0], min[1], min[2], max[0], max[1], max[2], dt_pixelpipe_get_pipe_name(pipe->type));
800 }
801 else if(out_format->datatype == TYPE_FLOAT && out_format->channels == 1)
802 {
803 int hasinf = 0, hasnan = 0;
804 float min = FLT_MAX;
805 float max = FLT_MIN;
806
807 for(int k = 0; k < roi_out->width * roi_out->height; k++)
808 {
809 float f = ((float *)(output))[k];
810 if(isnan(f))
811 hasnan = 1;
812 else if(!isfinite(f))
813 hasinf = 1;
814 else
815 {
816 min = fmin(f, min);
817 max = fmax(f, max);
818 }
819 }
820 if(hasnan)
821 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs NaNs! [%s]\n", module_label,
823 if(hasinf)
824 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs non-finite floats! [%s]\n", module_label,
826 fprintf(stderr, "[dev_pixelpipe] module `%s' min: (%f) max: (%f) [%s]\n", module_label, min, max,
828 }
829
830 dt_free(module_label);
831 }
832}
833
835 uint64_t *out_hash, const dt_dev_pixelpipe_iop_t **out_piece,
836 GList *pieces, int pos)
837{
838 // The pipeline is executed recursively, from the end. For each module n, starting from the end,
839 // if output is cached, take it, else if input is cached, take it, process it and output,
840 // else recurse to the previous module n-1 to get a an input.
841 void *input = NULL;
842 void *output = NULL;
843 void *cl_mem_output = NULL;
844
846
847 if(IS_NULL_PTR(pieces))
848 {
850 *out_piece = NULL;
851 return 0;
852 }
853
854 dt_iop_module_t *module = NULL;
856
858
859 // skip this module?
860 if(!piece->enabled)
861 return dt_dev_pixelpipe_process_rec(pipe, out_hash, out_piece, g_list_previous(pieces), pos - 1);
862
863 module = piece->module;
864
865 if(pipe->dev->gui_attached) pipe->dev->progress.total++;
866
868
869 const uint64_t hash = dt_dev_pixelpipe_node_hash(pipe, piece, piece->roi_out, pos);
870
871 // 1) Fast-track:
872 // If we have a cache entry for this hash, return it straight away,
873 // don't recurse through pipeline and don't process, unless this module still
874 // needs GUI-side sampling from its host input or the gamma display histogram
875 // needs the upstream cache entry.
876 dt_pixel_cache_entry_t *existing_cache = NULL;
877 void *existing_output = NULL;
878 /* Atomically look up the entry and increment its refcount so the caller receives a
879 * fully-owned reference. peek() + separate ref_count_entry() has a TOCTOU window:
880 * peek releases its tryrdlock immediately, so the entry can reach refcount 0 and be
881 * evicted between peek() and ref_count_entry(). */
882 const gboolean exact_output_cache_hit
883 = !_bypass_cache(pipe, piece)
885 &existing_output, &existing_cache)
886 && !IS_NULL_PTR(existing_output);
887
888 if(exact_output_cache_hit)
889 {
890 _trace_cache_owner(pipe, module, "exact-hit-direct", "output", hash, NULL, existing_cache, FALSE);
891 *out_hash = hash;
892 *out_piece = piece;
893 return 0;
894 }
895 else if(existing_cache)
896 {
897 /* ref_entry_by_hash succeeded but data was NULL (device-only entry); undo the ref. */
899 }
900
901 // 3) now recurse through the pipeline.
903 const dt_dev_pixelpipe_iop_t *previous_piece = NULL;
904 if(dt_dev_pixelpipe_process_rec(pipe, &input_hash, &previous_piece, g_list_previous(pieces), pos - 1))
905 {
906 /* Child recursion failed before this module acquired any output cache entry.
907 * Dropping `hash` here underflows cached exact-hit outputs during shutdown. */
909 "[pipeline] module=%s child recursion failed input_hash=%" PRIu64 " output_hash=%" PRIu64 "\n",
910 module->op, input_hash, hash);
911 return 1;
912 }
913
915
916 // Child recursion just published or exact-hit returned this hash with one ref already reserved for
917 // this immediate consumer. Reopen the live cache entry directly instead of going through exact-hit
918 // lookup, because exact-hit intentionally rejects auto-destroy entries while the parent recursion
919 // still needs to consume transient outputs in the same run.
920 dt_pixel_cache_entry_t *input_entry
922 if(!IS_NULL_PTR(previous_piece))
924 if(IS_NULL_PTR(input_entry) && !(module->flags() & IOP_FLAGS_TAKE_NO_INPUT))
925 {
927 "[pipeline] module=%s input cache entry missing input_hash=%" PRIu64 " output_hash=%" PRIu64
928 " prev_module=%s prev_hash=%" PRIu64 "\n",
929 module->op, input_hash, hash,
930 !IS_NULL_PTR(previous_piece) ? previous_piece->module->op : "",
931 !IS_NULL_PTR(previous_piece) ? previous_piece->global_hash : -1);
932 return 1;
933 }
934 input = input_entry ? dt_pixel_cache_entry_get_data(input_entry) : NULL;
935 _trace_cache_owner(pipe, module, "acquire", "input", input_hash, input, input_entry, FALSE);
936 if(input_entry)
937 _trace_buffer_content(pipe, module, "input-acquire", input, &piece->dsc_in, &piece->roi_in);
938 const size_t bufsize = (size_t)piece->dsc_out.bpp * piece->roi_out.width * piece->roi_out.height;
939 // Note: IS_NULL_PTR(input) is valid if we are on a GPU-only path, aka previous module ran on GPU
940 // without leaving its output on a RAM cache copy, and current module will also run on GPU.
941 // In this case, we rely on cl_mem_input for best performance (avoid memcpy between RAM and GPU).
942 // Should the GPU path fail at process time, we will init input and flush cl_mem_input into it.
943 // In any case, this avoids carrying a possibly-uninited input buffer, without knowing if it has
944 // data on it (or having to blindly copy back from vRAM to RAM).
945
946 // 3c) actually process this module BUT treat all bypasses first.
947 // special case: user requests to see channel data in the parametric mask of a module, or the blending
948 // mask. In that case we skip all modules manipulating pixel content and only process image distorting
949 // modules. Finally "gamma" is responsible for displaying channel/mask data accordingly.
950 if(pipe->dev->gui_attached
952 && !(module->operation_tags() & IOP_TAG_DISTORT)
953 && (piece->dsc_in.bpp == piece->dsc_out.bpp)
954 && !memcmp(&piece->roi_in, &piece->roi_out, sizeof(struct dt_iop_roi_t)))
955 {
956 /* Mask/channel passthrough keeps the exact child buffer alive for the next
957 * downstream module. This stage does not publish a new cacheline, so it
958 * must forward the child hash and actual previous piece contract exactly as
959 * they came from recursion, including the single reserved ref for the next
960 * real consumer. */
961 *out_hash = input_hash;
962 *out_piece = previous_piece;
963 return 0;
964 }
965
966 if(pipe->dev->gui_attached)
967 {
968 gchar *module_label = dt_history_item_get_name(module);
971 darktable.main_message = g_strdup_printf(_("Processing module `%s` for pipeline %s (%ix%i px @ %0.f%%)..."),
972 module_label, dt_pixelpipe_get_pipe_name(pipe->type),
973 piece->roi_out.width, piece->roi_out.height, piece->roi_out.scale * 100.f);
975 dt_free(module_label);
977 }
978
979 dt_pixel_cache_entry_t *output_entry = NULL;
980 gchar *type = dt_pixelpipe_get_pipe_name(pipe->type);
981
983
984 gchar *name = g_strdup_printf("module %s (%s) for pipe %s", module->op, module->multi_name, type);
985 // Cache bypass makes intermediate module outputs disposable, but the final output is the
986 // backbuffer consumed by Cairo. It must keep a host payload even when an edit mode (crop,
987 // clipping, ashift...) bypasses the downstream cache. Otherwise the GUI requests that host
988 // cacheline after every GPU render, while each cache-request pass publishes another device-only
989 // backbuffer and feeds an infinite preview recompute loop.
990 const gboolean keep_final_output = (hash == dt_dev_pixelpipe_get_hash(pipe));
991 gboolean cache_ram_output
992 = piece->cache_output_on_ram && (!_bypass_cache(pipe, piece) || keep_final_output);
993
994 /* `piece->cache_entry` is only valid as a writable-reuse hint for transient outputs that will
995 * be fully overwritten later. As soon as we keep the current output as a published cacheline in
996 * RAM, rekey reuse must stop for that piece so later runs cannot overwrite a long-term state in
997 * place just because the pipe is running in realtime. */
998 const gboolean allow_rekey_reuse = !(darktable.unmuted & DT_DEBUG_NOCACHE_REUSE) && !cache_ram_output;
1001 cache_ram_output, allow_rekey_reuse,
1002 &piece->cache_entry,
1003 &output, &output_entry);
1004 dt_free(name);
1005 if(acquire_status == DT_DEV_PIXELPIPE_CACHE_WRITABLE_EXACT_HIT)
1006 {
1007 /* Another pipe already owns a cacheline for this exact hash. If that cacheline is
1008 * still write-locked, this is not a processing error: it only means the concurrent
1009 * publisher has not finished exposing the exact-hit payload yet. Wait for that
1010 * publication to complete instead of aborting the whole recursion. */
1011 dt_pixel_cache_entry_t *exact_entry
1013 if(IS_NULL_PTR(exact_entry))
1014 {
1016 "[pipeline] module=%s exact-hit entry missing output_hash=%" PRIu64 "\n",
1017 module->op, hash);
1018 if(input_entry)
1020 return 1;
1021 }
1022
1028
1030 "[pipeline] module=%s writable-exact-hit output_hash=%" PRIu64 " has_host_data=%d"
1031 " cache_ram_output=%d devid=%d\n",
1032 module->op, hash, !IS_NULL_PTR(dt_pixel_cache_entry_get_data(exact_entry)), cache_ram_output,
1033 pipe->devid);
1034
1035 /* This entry may predate the current run's host-caching requirement: it was created device-only
1036 * (OpenCL, no RAM copy) back when nothing needed a host copy of it, and an exact-hash hit here
1037 * short-circuits without ever re-running the module, so `cache_ram_output` newly turning TRUE
1038 * (e.g. a color picker or histogram just started sampling this piece) would otherwise never take
1039 * effect on an already-cached entry. Materialize the host copy now from the device payload we
1040 * already own the OpenCL context for, instead of silently leaving GUI host-only consumers
1041 * (dt_dev_pixelpipe_cache_peek_gui, preferred_devid = -1) missing this hash forever. */
1042 if(cache_ram_output && IS_NULL_PTR(dt_pixel_cache_entry_get_data(exact_entry)))
1043 {
1044 void *restored = NULL;
1045 const gboolean restored_ok
1047 &restored);
1049 "[pipeline] module=%s exact-hit was device-only, host materialize %s output_hash=%" PRIu64 "\n",
1050 module->op, restored_ok ? "succeeded" : "failed", hash);
1051 }
1052
1053 _trace_cache_owner(pipe, module, "exact-hit-wait", "output", hash,
1054 dt_pixel_cache_entry_get_data(exact_entry), exact_entry, FALSE);
1055
1056 if(input_entry)
1058 *out_hash = hash;
1059 *out_piece = piece;
1060 return 0;
1061 }
1062 if(IS_NULL_PTR(output_entry))
1063 {
1065 "[pipeline] module=%s writable output acquisition failed output_hash=%" PRIu64
1066 " acquire_status=%d\n",
1067 module->op, hash, acquire_status);
1068 if(input_entry)
1070 return 1;
1071 }
1072 const gboolean new_entry = (acquire_status == DT_DEV_PIXELPIPE_CACHE_WRITABLE_CREATED);
1073 _trace_cache_owner(pipe, module, (acquire_status == DT_DEV_PIXELPIPE_CACHE_WRITABLE_REKEYED) ? "acquire-rekeyed"
1074 : (new_entry ? "acquire-new" : "acquire-existing"),
1075 "output", hash, output, output_entry, FALSE);
1076
1077 /* get tiling requirement of module */
1079 tiling.factor_cl = tiling.maxbuf_cl = -1; // set sentinel value to detect whether callback set sizes
1080 module->tiling_callback(module, pipe, piece, &tiling);
1081 if (tiling.factor_cl < 0) tiling.factor_cl = tiling.factor; // default to CPU size if callback didn't set GPU
1082 if (tiling.maxbuf_cl < 0) tiling.maxbuf_cl = tiling.maxbuf;
1083
1084 /* does this module involve blending? */
1085 if(piece->blendop_data && ((dt_develop_blend_params_t *)piece->blendop_data)->mask_mode != DEVELOP_MASK_DISABLED)
1086 {
1087 /* get specific memory requirement for blending */
1088 dt_develop_tiling_t tiling_blendop = { 0 };
1089 tiling_callback_blendop(module, pipe, piece, &tiling_blendop);
1090
1091 /* aggregate in structure tiling */
1092 tiling.factor = fmax(tiling.factor, tiling_blendop.factor);
1093 tiling.factor_cl = fmax(tiling.factor_cl, tiling_blendop.factor);
1094 tiling.maxbuf = fmax(tiling.maxbuf, tiling_blendop.maxbuf);
1095 tiling.maxbuf_cl = fmax(tiling.maxbuf_cl, tiling_blendop.maxbuf);
1096 tiling.overhead = fmax(tiling.overhead, tiling_blendop.overhead);
1097 }
1098
1099 /* remark: we do not do tiling for blendop step, neither in opencl nor on cpu. if overall tiling
1100 requirements (maximum of module and blendop) require tiling for opencl path, then following blend
1101 step is anyhow done on cpu. we assume that blending itself will never require tiling in cpu path,
1102 because memory requirements will still be low enough. */
1103
1104 assert(tiling.factor > 0.0f);
1105 assert(tiling.factor_cl > 0.0f);
1106
1107 // Actual pixel processing for this module
1108 int error = 0;
1109 dt_times_t start;
1110 dt_get_times(&start);
1111
1112 const char *prev_module = dt_pixelpipe_cache_set_current_module(module ? module->op : NULL);
1113
1114#ifdef HAVE_OPENCL
1115 error = pixelpipe_process_on_GPU(pipe, piece, previous_piece, &tiling, &pixelpipe_flow,
1116 &cache_ram_output,
1117 input_entry, output_entry);
1118#else
1119 error = pixelpipe_process_on_CPU(pipe, piece, previous_piece, &tiling, &pixelpipe_flow,
1120 &cache_ram_output,
1121 input_entry, output_entry);
1122#endif
1123
1125 output = dt_pixel_cache_entry_get_data(output_entry);
1126
1127 _print_perf_debug(pipe, pixelpipe_flow, piece, module,
1128 (acquire_status != DT_DEV_PIXELPIPE_CACHE_WRITABLE_CREATED), &start);
1129
1130 if(pipe->dev->gui_attached) pipe->dev->progress.completed++;
1131
1132 if(error)
1133 {
1135 "[pipeline] module=%s backend processing failed input_hash=%" PRIu64 " output_hash=%" PRIu64
1136 " input_cst=%d output_cst=%d roi_in=%dx%d roi_out=%dx%d\n",
1137 module->op, input_hash, hash, piece->dsc_in.cst, piece->dsc_out.cst,
1138 piece->roi_in.width, piece->roi_in.height, piece->roi_out.width, piece->roi_out.height);
1139 _trace_cache_owner(pipe, module, "error-cleanup", "input", input_hash, input, input_entry, FALSE);
1140 _trace_cache_owner(pipe, module, "error-cleanup", "output", hash, output, output_entry, FALSE);
1141 // Ensure we always release locks and cache references on error, otherwise cache eviction/GC will stall.
1144 if(input_entry)
1145 {
1148 }
1149
1150 // No point in keeping garbled output
1154 return 1;
1155 }
1156
1157 // Publish the module output descriptor authored for this stage. The cache entry keeps the
1158 // descriptor of the pixels this module actually published, not the stale descriptor of its input.
1159 _trace_cache_owner(pipe, module, "publish", "output", hash, output, output_entry, FALSE);
1160 _trace_buffer_content(pipe, module, "publish", output, &piece->dsc_out, &piece->roi_out);
1161
1162 if(!IS_NULL_PTR(output_entry))
1163 {
1164 piece->cache_entry = *output_entry;
1165 }
1166 else
1168
1169 if(!IS_NULL_PTR(output_entry) && !IS_NULL_PTR(output) && !(module->flags() & IOP_FLAGS_CPU_WRITES_OPENCL)
1170 && ((pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_CPU)
1171 || (pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_WITH_TILING)))
1172 {
1173 dt_dev_pixelpipe_gpu_flush_host_pinned_images(pipe, output, output_entry, "module output host rewrite");
1174 /* CPU/tiling processing rewrote the whole host buffer for this output. If the cache entry was rekeyed
1175 * from an older GPU stage, any cached device-only images still hanging off the same entry now point to
1176 * obsolete pixels. Drop them here so later mixed GPU/CPU modules cannot resurrect stale device payloads. */
1178 }
1179
1180 // Flag to throw away intermediate outputs as soon as the next module consumes them.
1181 // `cache_output` only means the backend had to keep a host-authoritative payload for this
1182 // stage (for example because the next module may need RAM or because the current stage ran
1183 // through an OpenCL cache path). In no-cache/bypass pipelines, that does not make the
1184 // published cacheline long-lived: once the downstream module takes its input ref, this
1185 // stage is transient and must disappear on release. Only the final published output needs
1186 // to survive long enough for dt_dev_pixelpipe_process() to promote it to the backbuffer,
1187 // otherwise thumbnail/export callers only see a missing exact-hit and fall back to invalid
1188 // placeholder pixels.
1189 if(_bypass_cache(pipe, piece) && !keep_final_output)
1191
1192 if(pipe->dev->gui_attached)
1193 {
1196 darktable.main_message = NULL;
1199 }
1200
1201 // From here on we only publish/inspect the finished output. Keep the writable lock strictly
1202 // around cacheline allocation and backend processing, then release it at one visible point
1203 // before the generic tail cleanup shared by darkroom and headless paths.
1204 if(dt_supervisor_active() && !IS_NULL_PTR(output_entry))
1206 dt_supervisor_node_key(pipe->type, module->op, module->multi_priority),
1207 piece->hash, input_hash, module->op, module->multi_priority,
1208 module->iop_order, pipe->type, pipe->imgid, piece->roi_out.width,
1209 piece->roi_out.height, pipe->devid,
1210 dt_pixel_cache_entry_get_size(output_entry), NULL);
1211 // Stamp the producing node identity so the CACHELINE_READY raised on the write-lock
1212 // release below carries it: GUI cache-wait consumers can then be served by producer
1213 // node even when the exact output hash drifted between their request and this publish
1214 // (doc/pipeline-cache.md §8). Set on every publish so a rekey-reused entry never keeps
1215 // a stale producer.
1216 if(!IS_NULL_PTR(output_entry))
1217 output_entry->producer_node_key
1218 = dt_supervisor_node_key(pipe->type, module->op, module->multi_priority);
1220
1222
1223 // Decrease reference count on input and flush it if it was flagged for auto destroy previously
1224 _trace_cache_owner(pipe, module, "release", "input", input_hash, input, input_entry, FALSE);
1225 if(input_entry)
1226 {
1229 }
1230
1231 // Print min/max/Nan in debug mode only
1232 if((darktable.unmuted & DT_DEBUG_NAN) && strcmp(module->op, "gamma") != 0 && !IS_NULL_PTR(output))
1233 {
1235 _print_nan_debug(pipe, cl_mem_output, output, &piece->roi_out, &piece->dsc_out, module);
1237 }
1238
1240
1241 *out_hash = hash;
1242 *out_piece = piece;
1243 return 0;
1244}
1245
1247{
1248 GList *nodes = g_list_last(pipe->nodes);
1250 while(strcmp(piece->module->op, op))
1251 {
1252 piece->enabled = 0;
1253 piece = NULL;
1254 nodes = g_list_previous(nodes);
1255 if(!nodes) break;
1256 piece = (dt_dev_pixelpipe_iop_t *)nodes->data;
1257 }
1258}
1259
1261{
1262 GList *nodes = pipe->nodes;
1264 while(strcmp(piece->module->op, op))
1265 {
1266 piece->enabled = 0;
1267 piece = NULL;
1268 nodes = g_list_next(nodes);
1269 if(!nodes) break;
1270 piece = (dt_dev_pixelpipe_iop_t *)nodes->data;
1271 }
1272}
1273
1274#define KILL_SWITCH_PIPE \
1275 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
1276 { \
1277 if(pipe->devid >= 0) \
1278 { \
1279 dt_opencl_unlock_device(pipe->devid); \
1280 pipe->devid = -1; \
1281 } \
1282 if(pipe->forms) \
1283 { \
1284 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_form_unref); \
1285 pipe->forms = NULL; \
1286 } \
1287 dt_pthread_mutex_unlock(&darktable.pipeline_threadsafe); \
1288 return 1; \
1289 }
1290
1291
1293{
1294 switch(error)
1295 {
1296 case 1:
1297 dt_print(DT_DEBUG_OPENCL, "[opencl] Opencl errors; disabling opencl for %s pipeline!\n", dt_pixelpipe_get_pipe_name(pipe->type));
1298 dt_control_log(_("Ansel discovered problems with your OpenCL setup; disabling OpenCL for %s pipeline!"), dt_pixelpipe_get_pipe_name(pipe->type));
1299 break;
1300 case 2:
1302 "[opencl] Too many opencl errors; disabling opencl for this session!\n");
1303 dt_control_log(_("Ansel discovered problems with your OpenCL setup; disabling OpenCL for this session!"));
1304 break;
1305 default:
1306 break;
1307 }
1308}
1309
1311{
1312 const uint64_t requested_hash = dt_dev_pixelpipe_get_hash(pipe);
1313 const uint64_t entry_hash = entry->hash;
1314
1315 _trace_cache_owner(pipe, NULL, "backbuf-update", "backbuf", requested_hash,
1316 entry ? entry->data : NULL, entry, FALSE);
1317
1318 if(requested_hash == DT_PIXELPIPE_CACHE_HASH_INVALID
1319 || entry_hash == DT_PIXELPIPE_CACHE_HASH_INVALID
1320 || entry_hash != requested_hash)
1321 {
1325 return;
1326 }
1327
1328 // Keep exactly one cache reference to the last valid output ("backbuf") for display.
1329 // This prevents the cache entry from being evicted while still in use by the GUI,
1330 // without leaking references on repeated cache hits.
1331 const gboolean hash_changed = (dt_dev_backbuf_get_hash(&pipe->backbuf) != entry_hash);
1332 if(hash_changed)
1333 {
1336 }
1337
1338 int bpp = 0;
1339 if(roi.width > 0 && roi.height > 0)
1340 bpp = (int)(dt_pixel_cache_entry_get_size(entry) / ((size_t)roi.width * (size_t)roi.height));
1341
1342 // Always refresh backbuf geometry/state, even when the cache key is unchanged.
1343 // Realtime drawing can update pixels in-place in the same cacheline, so width/height/history
1344 // must stay synchronized independently from key changes.
1345 dt_dev_set_backbuf(&pipe->backbuf, roi.width, roi.height, bpp, entry_hash,
1347
1350 roi.width, roi.height, bpp, pipe->type,
1351 pipe->devid >= 0 ? pipe->devid : pipe->last_devid);
1352}
1353
1354static GList *_get_requested_piece_node(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module, int *pos)
1355{
1356 if(pos) *pos = 0;
1357 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(module)) return NULL;
1358
1359 int current_pos = 1;
1360 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node), current_pos++)
1361 {
1362 dt_dev_pixelpipe_iop_t *const piece = node->data;
1363 if(piece && piece->enabled && piece->module == module)
1364 {
1365 if(pos) *pos = current_pos;
1366 return node;
1367 }
1368 }
1369
1370 return NULL;
1371}
1372
1374{
1375 /* `pipe->devid` is only valid while the current run owns the OpenCL device lock.
1376 * Reset it before any cache probe so callers never reuse a stale device id from a
1377 * previous pipeline pass. */
1378 pipe->devid = -1;
1379
1380 /* Record what image / pipeline is being processed, for crash reports. */
1381 if(pipe->dev)
1382 {
1383 const char *pl = "other";
1384 switch(pipe->type)
1385 {
1386 case DT_DEV_PIXELPIPE_FULL: pl = "darkroom"; break;
1387 case DT_DEV_PIXELPIPE_PREVIEW: pl = "darkroom-preview"; break;
1388 case DT_DEV_PIXELPIPE_EXPORT: pl = "export"; break;
1389 case DT_DEV_PIXELPIPE_THUMBNAIL: pl = "thumbnail"; break;
1390 default: break;
1391 }
1394 }
1395
1397 {
1398 fprintf(stderr, "[memory] before pixelpipe process\n");
1400 }
1401
1403
1404 if(pipe->dev->gui_attached)
1405 {
1406 pipe->dev->color_picker.pending_module = NULL;
1407 pipe->dev->color_picker.pending_pipe = NULL;
1409 }
1410
1411 // Get the roi_out hash of all nodes.
1412 // Get the previous output size of the module, for cache invalidation.
1413 dt_dev_pixelpipe_get_roi_in(pipe, roi);
1415 const guint pos = g_list_length(pipe->dev->iop);
1416
1417 const guint previous_raster_refs = pipe->raster_mask_hashes->len;
1418
1425 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node))
1426 {
1427 const dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)node->data;
1428 if(!piece->enabled) continue;
1429
1430 GHashTableIter mask_iter;
1431 gpointer mask_key = NULL;
1432 gpointer mask_name = NULL;
1433 g_hash_table_iter_init(&mask_iter, piece->module->raster_mask.source.masks);
1434 while(g_hash_table_iter_next(&mask_iter, &mask_key, &mask_name))
1435 {
1436 const int mask_id = GPOINTER_TO_INT(mask_key);
1437 if(!pipe->store_all_raster_masks
1438 && !dt_iop_is_raster_mask_used(piece->module, mask_id))
1439 continue;
1440
1441 const uint64_t mask_hash = dt_dev_pixelpipe_raster_mask_hash(piece, mask_id);
1443 darktable.pixelpipe_cache, mask_hash, NULL, NULL))
1444 g_array_append_val(pipe->raster_mask_hashes, mask_hash);
1445 }
1446 }
1447
1448 for(guint k = 0; k < previous_raster_refs; k++)
1449 {
1450 const uint64_t hash = g_array_index(pipe->raster_mask_hashes, uint64_t, k);
1452 }
1453 if(previous_raster_refs > 0)
1454 g_array_remove_range(pipe->raster_mask_hashes, 0, previous_raster_refs);
1455
1457 const dt_iop_module_t *const requested_module = dt_dev_pixelpipe_get_cache_request_module(pipe);
1459
1460 GList *requested_pieces = g_list_last(pipe->nodes);
1461 int requested_pos = (int)pos;
1462 dt_dev_pixelpipe_iop_t *requested_piece = NULL;
1463 gboolean requested_backbuf = TRUE;
1464
1465 if(cache_request == DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE && !IS_NULL_PTR(requested_module))
1466 {
1467 requested_pieces = _get_requested_piece_node(pipe, requested_module, &requested_pos);
1468 if(requested_pieces)
1469 {
1470 requested_piece = requested_pieces->data;
1471 requested_backbuf = FALSE;
1472 }
1473 else
1474 {
1475 dt_print(DT_DEBUG_DEV, "[pixelpipe/gui] requested module cache target disappeared pipe=%s module=%s\n",
1476 dt_pixelpipe_get_pipe_name(pipe->type), requested_module->op);
1477 }
1478 }
1479
1480 void *buf = NULL;
1481
1482 /* GUI cache requests can target either the final backbuffer or one module output in the middle of the
1483 current synchronized graph. Exact-hit checks must therefore look at the requested target instead of
1484 always assuming the run goes to the pipe end. */
1485 const uint64_t requested_hash = requested_backbuf ? dt_dev_pixelpipe_get_hash(pipe)
1486 : requested_piece ? requested_piece->global_hash
1488 dt_pixel_cache_entry_t *entry = NULL;
1489 if(!_bypass_cache(pipe, requested_piece)
1490 && requested_hash != DT_PIXELPIPE_CACHE_HASH_INVALID
1491 && dt_dev_pixelpipe_cache_peek(darktable.pixelpipe_cache, requested_hash, &buf, &entry,
1492 pipe->devid, NULL)
1493 && !IS_NULL_PTR(buf))
1494 {
1495 if(requested_backbuf)
1496 _update_backbuf_cache_reference(pipe, roi, entry);
1497
1498 /* A GUI consumer explicitly requested this target (color picker, histogram, autoset) and is
1499 * blocked on it in the cache-wait manager. The output is already host-cached, so the run stops
1500 * here without ever taking a write lock — hence no DT_SIGNAL_CACHELINE_READY would fire and the
1501 * waiter would hang forever, even though its buffer is right there (doc/pipeline-cache.md §8).
1502 * This is the "no node update, nothing" deadlock: it bites when the hash the GUI predicted
1503 * (H_gui) drifted from the one now cached (H_pipe), so the waiter never sees its exact hash and
1504 * the pipe never republishes. Wake it here, carrying the producing node key so the manager's
1505 * producer-node match serves the waiter even under that drift; its restart then re-reads the
1506 * module's current hash and hits. Gated on an actual pending request so ordinary cache-hit
1507 * renders add no signal traffic. */
1508 if(cache_request != DT_DEV_PIXELPIPE_CACHE_REQUEST_NONE)
1509 {
1510 const uint64_t ready_node_key
1511 = (!requested_backbuf && !IS_NULL_PTR(requested_piece) && !IS_NULL_PTR(requested_piece->module))
1512 ? dt_supervisor_node_key(pipe->type, requested_piece->module->op,
1513 requested_piece->module->multi_priority)
1516 ready_node_key);
1517 }
1518 return 0;
1519 }
1520
1521 dt_print(DT_DEBUG_DEV, "[pixelpipe] Started %s pipeline recompute at %i×%i px\n",
1522 dt_pixelpipe_get_pipe_name(pipe->type), roi.width, roi.height);
1523
1524 // get a snapshot of the mask list: shared by reference (dt_masks_cow_touch on the GUI side
1525 // clones instead of mutating in place whenever refcount > 1, so this snapshot stays frozen
1526 // even if dev->forms is edited mid-run), not deep-copied on every single pipeline recompute.
1528
1529 // go through the list of modules from the end:
1530 GList *pieces = g_list_last(pipe->nodes);
1531
1532 // Because it's possible here that we export at full resolution,
1533 // and our memory planning doesn't account for several concurrent pipelines
1534 // at full size, we allow only one pipeline at a time to run.
1535 // This is because wavelets decompositions and such use 6 copies,
1536 // so the RAM usage can go out of control here.
1538
1540 {
1541 const guint node_count = g_list_length(pipe->nodes);
1542 uint64_t *invalidated_hashes = g_new(uint64_t, node_count);
1543 size_t invalidated_count = 0;
1544 gboolean reached_provider = FALSE;
1545
1557 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node))
1558 {
1560 if(!piece->enabled) continue;
1561
1562 reached_provider |= (piece->global_hash == pipe->reentry_hash);
1563 if(reached_provider)
1564 invalidated_hashes[invalidated_count++] = piece->global_hash;
1565 }
1566
1567 const int retained = dt_dev_pixelpipe_cache_invalidate_hashes(
1568 darktable.pixelpipe_cache, invalidated_hashes, invalidated_count);
1570 "[raster masks] invalidated %" G_GSIZE_FORMAT " cache states at retry from provider=%" PRIu64
1571 " retained=%d pipe=%s\n",
1572 invalidated_count, pipe->reentry_hash, retained,
1574 dt_free(invalidated_hashes);
1575 }
1576
1577 pipe->opencl_enabled = dt_opencl_update_settings(); // update enabled flag and profile from preferences
1578 pipe->devid = (pipe->opencl_enabled) ? dt_opencl_lock_device(pipe->type)
1579 : -1; // try to get/lock opencl resource
1580
1581 if(pipe->devid > -1)
1582 {
1584 pipe->last_devid = pipe->devid;
1585 }
1586 dt_print(DT_DEBUG_OPENCL, "[pixelpipe_process] [%s] using device %d\n", dt_pixelpipe_get_pipe_name(pipe->type),
1587 pipe->devid);
1588
1590
1591 gboolean keep_running = TRUE;
1592 int runs = 0;
1593 int opencl_error = 0;
1594 int err = 0;
1595
1596 while(keep_running && runs < 3)
1597 {
1598 ++runs;
1599
1600 /* Mask preview is authored while the current run advances through blend.c.
1601 * Reset it for each retry so a stale state from a previous pass cannot leak
1602 * into the next recursion before the active module reaches its own blend. */
1604
1605#ifdef HAVE_OPENCL
1607#endif
1608
1610
1611 dt_times_t start;
1612 dt_get_times(&start);
1613 uint64_t final_hash = -1;
1614 const dt_dev_pixelpipe_iop_t *final_piece = NULL;
1615 err = dt_dev_pixelpipe_process_rec(pipe, &final_hash, &final_piece,
1616 requested_backbuf ? pieces : requested_pieces,
1617 requested_backbuf ? pos : requested_pos);
1618 (void)final_piece;
1619 gchar *msg = g_strdup_printf("[pixelpipe] %s internal pixel pipeline processing", dt_pixelpipe_get_pipe_name(pipe->type));
1620 dt_show_times(&start, msg);
1621 dt_free(msg);
1622
1623 // get status summary of opencl queue by checking the eventlist
1624 const int oclerr = (pipe->devid > -1) ? dt_opencl_events_flush(pipe->devid, TRUE) != 0 : 0;
1625
1626 // Check if we had opencl errors ....
1627 // remark: opencl errors can come in two ways: pipe->opencl_error is TRUE (and err is TRUE) OR oclerr is
1628 // TRUE
1629 keep_running = (oclerr || (err && pipe->opencl_error));
1630 if(keep_running)
1631 {
1632 // Log the error
1633 darktable.opencl->error_count++; // increase error count
1634 opencl_error = 1; // = any OpenCL error, next run goes to CPU
1635
1636 // Disable OpenCL for this pipe
1638 pipe->opencl_enabled = 0;
1639 pipe->opencl_error = 0;
1640 pipe->devid = -1;
1641
1643 {
1644 // Too many errors : dispable OpenCL for this session
1646 dt_capabilities_remove("opencl");
1647 opencl_error = 2; // = too many OpenCL errors, all runs go to CPU
1648 }
1649
1650 _print_opencl_errors(opencl_error, pipe);
1651 }
1652 else if(!dt_dev_pixelpipe_has_shutdown(pipe))
1653 {
1654 // No opencl errors, no killswitch triggered: we should have a valid output buffer now.
1655 dt_pixel_cache_entry_t *final_entry = NULL;
1656 void *final_buf = NULL;
1657 if(!requested_backbuf)
1658 {
1660 }
1662 &final_entry, pipe->devid, NULL)
1663 && !IS_NULL_PTR(final_buf))
1664 {
1665 _update_backbuf_cache_reference(pipe, roi, final_entry);
1667 }
1668 else
1669 {
1671 "[picker/rec] final output cache missing pipe=%s hash=%" PRIu64 " history=%" PRIu64
1672 " devid=%d err=%d\n",
1674 dt_dev_pixelpipe_get_history_hash(pipe), pipe->devid, err);
1676 }
1677
1678 // Note : the last output (backbuf) of the pixelpipe cache is internally locked
1679 // Whatever consuming it will need to unlock it.
1680 }
1681 }
1682
1684
1685 // release resources:
1686 if(pipe->forms)
1687 {
1688 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_form_unref);
1689 pipe->forms = NULL;
1690 }
1691 if(pipe->devid >= 0)
1692 {
1694 pipe->devid = -1;
1695 }
1696
1697 // terminate
1699
1700 // If an intermediate module set that, be sure to reset it at the end
1701 pipe->flush_cache = FALSE;
1702 return err;
1703}
1704
1705// clang-format off
1706// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1707// vim: shiftwidth=2 expandtab tabstop=2 cindent
1708// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1709// 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)
atomic_int dt_atomic_int
Definition atomic.h:66
int levels(struct dt_imageio_module_data_t *data)
Definition avif.c:635
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
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:1657
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:180
@ DEVELOP_MASK_DISABLED
Definition blend.h:114
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
dt_iop_color_intent_t
Definition colorspaces.h:63
@ DT_INTENT_LAST
Definition colorspaces.h:68
dt_colorspaces_color_profile_type_t
Definition colorspaces.h:81
@ DT_COLORSPACE_NONE
Definition colorspaces.h:82
const dt_aligned_pixel_t f
static const float const float const float min
const float max
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
int type
char * name
void dt_control_log(const char *msg,...)
Definition control.c:777
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:877
void dt_show_times(const dt_times_t *start, const char *prefix)
Definition darktable.c:1638
darktable_t darktable
Definition darktable.c:183
void dt_print_mem_usage()
Definition darktable.c:1859
void dt_capabilities_remove(char *capability)
Definition darktable.c:1842
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...)
Definition darktable.c:1652
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define UNKNOWN_IMAGE
Definition darktable.h:194
@ DT_DEBUG_OPENCL
Definition darktable.h:744
@ DT_DEBUG_PIPE
Definition darktable.h:763
@ DT_DEBUG_PIPECACHE
Definition darktable.h:742
@ DT_DEBUG_NAN
Definition darktable.h:748
@ DT_DEBUG_MEMORY
Definition darktable.h:746
@ DT_DEBUG_VERBOSE
Definition darktable.h:765
@ DT_DEBUG_DEV
Definition darktable.h:739
@ DT_DEBUG_NOCACHE_REUSE
Definition darktable.h:767
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:485
#define dt_free(ptr)
Definition darktable.h:478
static void dt_get_times(dt_times_t *t)
Definition darktable.h:983
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition darktable.h:1123
#define __OMP_FOR_SIMD__(...)
Definition darktable.h:272
#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:293
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)
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:1957
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1557
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:117
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:384
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:369
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:389
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
Definition format.c:28
dt_iop_buffer_type_t
Definition format.h:44
@ TYPE_FLOAT
Definition format.h:46
@ TYPE_UNKNOWN
Definition format.h:45
@ TYPE_UINT8
Definition format.h:48
@ TYPE_UINT16
Definition format.h:47
int bpp
@ IMAGEIO_RGB
Definition imageio.h:70
@ IMAGEIO_INT8
Definition imageio.h:62
gboolean dt_iop_is_raster_mask_used(dt_iop_module_t *module, int id)
Definition imageop.c:3146
void dt_iop_init_pipe(struct dt_iop_module_t *module, struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece)
Definition imageop.c:564
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:582
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:243
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_FLAGS_TAKE_NO_INPUT
Definition imageop.h:206
@ IOP_FLAGS_CPU_WRITES_OPENCL
Definition imageop.h:210
@ IOP_CS_RGB_DISPLAY
Definition imageop.h:240
@ IOP_TAG_DISTORT
Definition imageop.h:181
GList * dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
Deep-copy an order list.
Definition iop_order.c:2002
float *const restrict const size_t k
GList * dt_masks_snapshot_current_forms(dt_develop_t *dev, gboolean reset_changed)
void dt_masks_form_unref(dt_masks_form_t *form)
float iscale
Definition mipmap_cache.c:2
size_t size
Definition mipmap_cache.c:3
dt_mipmap_size_t
float dt_aligned_pixel_t[4]
void dt_opencl_unlock_device(const int dev)
Definition opencl.c:1676
void dt_opencl_events_reset(const int devid)
Definition opencl.c:2989
cl_int dt_opencl_events_flush(const int devid, const int reset)
Definition opencl.c:3084
int dt_opencl_lock_device(const int pipetype)
Definition opencl.c:1591
void dt_opencl_check_tuning(const int devid)
Definition opencl.c:2723
int dt_opencl_update_settings(void)
Definition opencl.c:2852
#define DT_OPENCL_MAX_ERRORS
Definition opencl.h:49
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:48
@ DT_REQUEST_ONLY_IN_GUI
Definition pixelpipe.h:49
dt_dev_pixelpipe_type_t
Definition pixelpipe.h:36
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:41
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:38
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:40
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:39
int dt_dev_pixelpipe_cache_invalidate_hashes(dt_dev_pixelpipe_cache_t *cache, const uint64_t *hashes, const size_t count)
Invalidate cache lines matching an explicit list of hashes.
void * dt_pixel_cache_entry_get_data(dt_pixel_cache_entry_t *entry)
void dt_dev_pixelpipe_cache_ref_count_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
void dt_dev_pixelpipe_cache_print(dt_dev_pixelpipe_cache_t *cache)
void dt_dev_pixelpipe_cache_unref_hash(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash)
Find the entry matching hash, and decrease its ref_count if found.
void dt_dev_pixelpipe_cache_flush_clmem_for_pipe(dt_dev_pixelpipe_cache_t *cache, const int devid)
Like dt_dev_pixelpipe_cache_flush_clmem(), for callers that do not hold darktable....
void dt_dev_pixelpipe_cache_flag_auto_destroy(dt_dev_pixelpipe_cache_t *cache, dt_pixel_cache_entry_t *cache_entry)
Flag the cache entry as "auto_destroy". This is useful for short-lived/disposable cache entries,...
void dt_dev_pixelpipe_cache_auto_destroy_apply(dt_dev_pixelpipe_cache_t *cache, 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_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.
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.
void dt_dev_pixelpipe_cache_flush_entry_clmem(dt_pixel_cache_entry_t *entry)
Flush all reusable OpenCL payloads cached on one cache entry.
dt_pixel_cache_entry_t * dt_dev_pixelpipe_cache_get_entry(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash)
Get an internal reference to the cache entry matching hash. If you are going to access this entry mor...
int dt_dev_pixelpipe_cache_remove(dt_dev_pixelpipe_cache_t *cache, 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 ...
gboolean dt_dev_pixelpipe_cache_ref_entry_by_hash(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, void **data, dt_pixel_cache_entry_t **entry)
Resolve and retain an existing cache entry by hash.
dt_dev_pixelpipe_cache_writable_status_t dt_dev_pixelpipe_cache_get_writable(dt_dev_pixelpipe_cache_t *cache, 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)
gboolean dt_dev_pixelpipe_cache_restore_host_payload(dt_dev_pixelpipe_cache_t *cache, 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.
void dt_dev_pixelpipe_cache_wrlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the write lock on the entry.
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_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
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)
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.
void dt_sentry_set_processed_image(const struct dt_image_t *img, const char *pipeline)
Definition sentry.c:637
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ 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
const float uint32_t state[4]
const float r
unsigned __int64 uint64_t
Definition strptime.c:75
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:818
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_opencl_t * opencl
Definition darktable.h:813
int32_t unmuted
Definition darktable.h:788
dt_pthread_mutex_t pipeline_threadsafe
Definition darktable.h:836
char * main_message
Definition darktable.h:865
struct dt_control_t * control
Definition darktable.h:801
dt_pthread_mutex_t log_mutex
Definition control.h:244
dt_dev_request_flags_t request_histogram
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_colorspaces_color_profile_type_t icc_type
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
dt_dev_pixelpipe_type_t type
gboolean store_all_raster_masks
uint64_t rawdetail_mask_hash
GArray * raster_mask_hashes
struct dt_develop_t * dev
int32_t gui_attached
Definition develop.h:162
GList * iop_order_list
Definition develop.h:291
dt_image_t image_storage
Definition develop.h:259
struct dt_develop_t::@19 color_picker
Authoritative darkroom color-picker state.
GList * iop
Definition develop.h:285
struct dt_iop_module_t * pending_module
Definition develop.h:413
int completed
Definition develop.h:499
struct dt_dev_pixelpipe_t * pending_pipe
Definition develop.h:414
struct dt_develop_t::@26 progress
uint64_t piece_hash
Definition develop.h:407
unsigned overhead
Definition tiling.h:49
unsigned int channels
Definition format.h:54
dt_iop_buffer_type_t datatype
Definition format.h:56
GModule *dt_dev_operation_t op
Definition imageop.h:286
Region of interest passed through the pixelpipe.
Definition imageop.h:72
int error_count
Definition opencl.h:265
int stopped
Definition opencl.h:262
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:161
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:78
@ DT_SV_CREATE
Definition supervisor.h:77
@ DT_SV_DELETE
Definition supervisor.h:80
static gboolean dt_supervisor_active(void)
Definition supervisor.h:94
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
static const int mask_id
Definition useless.c:210
static const char * mask_name
Definition useless.c:211