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"
75#include "develop/tiling.h"
76#include "develop/masks.h"
77#include "gui/gtk.h"
78#include "libs/colorpicker.h"
79#include "libs/lib.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(!(darktable.unmuted & DT_DEBUG_PIPECACHE)) return;
100 if(!(darktable.unmuted & 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(!(darktable.unmuted & DT_DEBUG_PIPECACHE)) return;
125 if(!(darktable.unmuted & 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
166 "[pixelpipe_stats] pipe=%s module=%s phase=%s type=float ch=%u roi=%dx%d "
167 "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",
168 dt_pixelpipe_get_pipe_name(pipe->type), module->op, phase ? phase : "-",
169 channels, roi->width, roi->height,
170 minv[0], (channels > 1) ? minv[1] : 0.0f, (channels > 2) ? minv[2] : 0.0f,
171 maxv[0], (channels > 1) ? maxv[1] : 0.0f, (channels > 2) ? maxv[2] : 0.0f,
172 (channels > 3) ? minv[3] : 0.0f, (channels > 3) ? maxv[3] : 0.0f,
173 near_black, pixels, nonfinite);
174 }
175 else if(format->datatype == TYPE_UINT8 && channels >= 1)
176 {
177 const uint8_t *in = (const uint8_t *)buffer;
178 int minv[4] = { 255, 255, 255, 255 };
179 int maxv[4] = { 0, 0, 0, 0 };
180 size_t near_black = 0;
181
182 for(size_t k = 0; k < pixels; k++, in += channels)
183 {
184 for(unsigned int c = 0; c < MIN(channels, 4U); c++)
185 {
186 minv[c] = MIN(minv[c], in[c]);
187 maxv[c] = MAX(maxv[c], in[c]);
188 }
189
190 const int energy = in[0] + ((channels > 1) ? in[1] : 0) + ((channels > 2) ? in[2] : 0);
191 if(energy == 0) near_black++;
192 }
193
195 "[pixelpipe_stats] pipe=%s module=%s phase=%s type=u8 ch=%u roi=%dx%d "
196 "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",
197 dt_pixelpipe_get_pipe_name(pipe->type), module->op, phase ? phase : "-",
198 channels, roi->width, roi->height,
199 minv[0], (channels > 1) ? minv[1] : 0, (channels > 2) ? minv[2] : 0,
200 maxv[0], (channels > 1) ? maxv[1] : 0, (channels > 2) ? maxv[2] : 0,
201 (channels > 3) ? minv[3] : 0, (channels > 3) ? maxv[3] : 0,
202 near_black, pixels);
203 }
204}
205
207 dt_iop_module_t *module, const uint64_t input_hash,
208 const void *input, dt_pixel_cache_entry_t *input_entry,
209 const uint64_t output_hash, void **output,
210 void **cl_mem_output, dt_pixel_cache_entry_t *output_entry)
211{
212 _trace_cache_owner(pipe, module, "shutdown-drop", "input", input_hash, input, input_entry, FALSE);
213 _trace_cache_owner(pipe, module, "shutdown-drop", "output", output_hash,
214 output ? *output : NULL, output_entry, FALSE);
215
217
218 if(!IS_NULL_PTR(input_entry))
219 {
222 }
223
224 if(!IS_NULL_PTR(output_entry))
225 {
227
230 }
231
232 if(output) *output = NULL;
233
234 if(!IS_NULL_PTR(*cl_mem_output))
235 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_output, NULL, NULL, FALSE);
236
237 return 1;
238}
239
240static inline gboolean _is_focused_realtime_gui_module(const dt_dev_pixelpipe_t *pipe,
241 const dt_develop_t *dev,
242 const dt_iop_module_t *module)
243{
244 return (pipe->type == DT_DEV_PIXELPIPE_FULL || pipe->type == DT_DEV_PIXELPIPE_PREVIEW)
245 && pipe->realtime && dev && module && dev->gui_module == module;
246}
247
248
250 const dt_pixel_cache_entry_t *cache_entry)
251{
252 return (pipe->realtime
253 || (cache_entry && IS_NULL_PTR(dt_pixel_cache_entry_get_data((dt_pixel_cache_entry_t *)cache_entry)))
254 || !_bypass_cache(pipe, NULL));
255}
256
258{
259 char *r = NULL;
260
261 switch(pipe_type)
262 {
264 r = _("preview");
265 break;
267 r = _("full");
268 break;
270 r = _("thumbnail");
271 break;
273 r = _("export");
274 break;
275 default:
276 r = _("invalid");
277 }
278 return r;
279}
280
281
282inline static void _uint8_to_float(const uint8_t *const input, float *const output,
283 const size_t width, const size_t height, const size_t chan)
284{
285 __OMP_FOR_SIMD__(aligned(input, output: 64) )
286 for(size_t k = 0; k < height * width; k++)
287 {
288 const size_t index = k * chan;
289 // Warning: we take BGRa and put it back into RGBa
290 output[index + 0] = (float)input[index + 2] / 255.f;
291 output[index + 1] = (float)input[index + 1] / 255.f;
292 output[index + 2] = (float)input[index + 0] / 255.f;
293 output[index + 3] = 0.f;
294 }
295}
296
297static const char *_debug_cst_to_string(const int cst)
298{
299 switch(cst)
300 {
301 case IOP_CS_RAW:
302 return "raw";
303 case IOP_CS_LAB:
304 return "lab";
305 case IOP_CS_RGB:
306 return "rgb";
308 return "display-rgb";
309 case IOP_CS_LCH:
310 return "lch";
311 case IOP_CS_HSL:
312 return "hsl";
313 case IOP_CS_JZCZHZ:
314 return "jzczhz";
315 case IOP_CS_NONE:
316 return "none";
317 default:
318 return "unknown";
319 }
320}
321
323{
324 switch(type)
325 {
326 case TYPE_FLOAT:
327 return "float";
328 case TYPE_UINT16:
329 return "uint16";
330 case TYPE_UINT8:
331 return "uint8";
332 case TYPE_UNKNOWN:
333 default:
334 return "unknown";
335 }
336}
337
339 const gboolean is_cl,
340 const dt_iop_buffer_dsc_t *in_dsc, const dt_iop_buffer_dsc_t *out_dsc,
341 const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out,
342 const size_t in_bpp, const size_t out_bpp,
343 const int cst_before, const int cst_after)
344{
345 if(!(darktable.unmuted & DT_DEBUG_PIPE)) return;
346 if(!(darktable.unmuted & DT_DEBUG_VERBOSE)) return;
347 const char *module_name = module ? module->op : "base";
348 const char *pipe_name = dt_pixelpipe_get_pipe_name(pipe->type);
349 const char *stage_name = stage ? stage : "process";
350
351 if(!IS_NULL_PTR(in_dsc) && !IS_NULL_PTR(out_dsc))
352 {
354 "[pixelpipe] %s %s %s %s: in cst=%s->%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d | "
355 "out cst=%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d\n",
356 pipe_name, module_name, is_cl ? "cl" : "cpu", stage_name,
357 _debug_cst_to_string(cst_before), _debug_cst_to_string(cst_after),
358 in_dsc->channels, _debug_type_to_string(in_dsc->datatype), in_bpp,
359 roi_in ? roi_in->width : 0, roi_in ? roi_in->height : 0,
360 _debug_cst_to_string(out_dsc->cst), out_dsc->channels, _debug_type_to_string(out_dsc->datatype),
361 out_bpp, roi_out ? roi_out->width : 0, roi_out ? roi_out->height : 0);
362 }
363 else if(!IS_NULL_PTR(out_dsc))
364 {
366 "[pixelpipe] %s %s %s %s: out cst=%s ch=%d type=%s bpp=%" G_GSIZE_FORMAT " roi=%dx%d\n",
367 pipe_name, module_name, is_cl ? "cl" : "cpu", stage_name,
368 _debug_cst_to_string(out_dsc->cst), out_dsc->channels, _debug_type_to_string(out_dsc->datatype),
369 out_bpp, roi_out ? roi_out->width : 0, roi_out ? roi_out->height : 0);
370 }
371}
372
373
374int dt_dev_pixelpipe_init_export(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev, int levels, gboolean store_masks)
375{
376 const int res = dt_dev_pixelpipe_init_cached(pipe);
379 pipe->levels = levels;
380 pipe->store_all_raster_masks = store_masks;
381 pipe->dev = dev;
382 return res;
383}
384
386{
387 const int res = dt_dev_pixelpipe_init_cached(pipe);
389 pipe->no_cache = TRUE;
390 pipe->dev = dev;
391 return res;
392}
393
395{
396 const int res = dt_dev_pixelpipe_init_cached(pipe);
398 pipe->no_cache = TRUE;
399 pipe->dev = dev;
400 return res;
401}
402
404{
405 // Init with the size of MIPMAP_F
406 const int res = dt_dev_pixelpipe_init_cached(pipe);
409
410 // Needed for caching
412 pipe->dev = dev;
413 return res;
414}
415
417{
418 const int res = dt_dev_pixelpipe_init_cached(pipe);
420
421 // Needed for caching
423 pipe->dev = dev;
424 return res;
425}
426
428{
429 // Set everything to 0 = NULL = FALSE
430 memset(pipe, 0, sizeof(dt_dev_pixelpipe_t));
431
432 // Set only the stuff that doesn't take 0 as default
433 pipe->devid = -1;
434 pipe->last_devid = -1;
438 dt_dev_set_backbuf(&pipe->backbuf, 0, 0, 0, -1, -1);
441 pipe->raster_mask_hashes = g_array_new(FALSE, FALSE, sizeof(uint64_t));
442
444 pipe->iscale = 1.0f;
448
450 dt_pthread_mutex_init(&(pipe->busy_mutex), NULL);
451
454
456 return 1;
457}
458
460{
461 if(IS_NULL_PTR(pipe)) return;
463}
464
466{
467 return pipe ? dt_atomic_get_int((dt_atomic_int *)&pipe->realtime) : FALSE;
468}
469
470void dt_dev_pixelpipe_set_input(dt_dev_pixelpipe_t *pipe, int32_t imgid, int width, int height,
472{
473 pipe->iwidth = width;
474 pipe->iheight = height;
475 pipe->iscale = iscale;
476 pipe->imgid = imgid;
477 pipe->dev->image_storage = pipe->dev->image_storage;
478 pipe->size = size;
479}
480
482 const gchar *icc_filename, dt_iop_color_intent_t icc_intent)
483{
484 pipe->icc_type = icc_type;
485 dt_free(pipe->icc_filename);
486 pipe->icc_filename = g_strdup(icc_filename ? icc_filename : "");
487 pipe->icc_intent = icc_intent;
488}
489
491{
492 /* Device-side cache payloads are only an acceleration layer. Drop the cl_mem
493 * objects this pipe produced on the device it last ran on -- but only that
494 * device, so we never touch cache entries another, still-running pipe holds
495 * on a different (or the same) OpenCL device. */
497
498 // blocks while busy and sets shutdown bit:
500 // so now it's safe to clean up cache:
501 const uint64_t old_backbuf_hash = dt_dev_backbuf_get_hash(&pipe->backbuf);
502 if(old_backbuf_hash != DT_PIXELPIPE_CACHE_HASH_INVALID)
503 {
504 /* Backbuffer ownership belongs to the pipeline, not its GUI consumers. Once the pipe itself is
505 * torn down, always release that keepalive ref and invalidate the published backbuffer metadata. */
507
508 if(pipe->no_cache)
509 {
510 dt_pixel_cache_entry_t *old_backbuf_entry
512 if(old_backbuf_entry)
513 {
516 }
517 }
518 }
523 dt_free(pipe->icc_filename);
524
526
528
529 // Every hash in this array corresponds to one cache reference acquired
530 // either while reopening current provider masks or while publishing a new
531 // one. Release the exact same number of references before destroying it.
532 for(guint k = 0; k < pipe->raster_mask_hashes->len; k++)
533 {
534 const uint64_t hash = g_array_index(pipe->raster_mask_hashes, uint64_t, k);
536 }
537 g_array_free(pipe->raster_mask_hashes, TRUE);
538 pipe->raster_mask_hashes = NULL;
539
540 if(pipe->forms)
541 {
542 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_free_form);
543 pipe->forms = NULL;
544 }
545}
546
547
549{
551 {
552 pipe->reentry = TRUE;
553 pipe->reentry_hash = hash;
554 dt_print(DT_DEBUG_DEV, "[dev_pixelpipe] re-entry flag set for %" PRIu64 "\n", hash);
555 return TRUE;
556 }
557
558 return FALSE;
559}
560
561
563{
564 if(pipe->reentry_hash == hash)
565 {
566 pipe->reentry = FALSE;
568 dt_print(DT_DEBUG_DEV, "[dev_pixelpipe] re-entry flag unset for %" PRIu64 "\n", hash);
569 return TRUE;
570 }
571
572 return FALSE;
573}
574
576{
577 return pipe->reentry;
578}
579
586
588{
589 // destroy all nodes
590 for(GList *nodes = g_list_first(pipe->nodes); nodes; nodes = g_list_next(nodes))
591 {
593 if(IS_NULL_PTR(piece)) continue;
594 // printf("cleanup module `%s'\n", piece->module->name());
595 if(piece->module) dt_iop_cleanup_pipe(piece->module, pipe, piece);
596 dt_free(piece);
597 }
598 g_list_free(pipe->nodes);
599 pipe->nodes = NULL;
600 // and iop order
601 g_list_free_full(pipe->iop_order_list, dt_free_gpointer);
602 pipe->iop_order_list = NULL;
604}
605
607{
608 // check that the pipe was actually properly cleaned up after the last run
609 g_assert(IS_NULL_PTR(pipe->nodes));
610 g_assert(IS_NULL_PTR(pipe->iop_order_list));
612
613 // for all modules in dev:
614 for(GList *modules = g_list_first(pipe->dev->iop); modules; modules = g_list_next(modules))
615 {
616 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
618 if(IS_NULL_PTR(piece)) continue;
619 piece->enabled = module->enabled;
620 piece->request_histogram = DT_REQUEST_ONLY_IN_GUI;
621 piece->histogram_params.bins_count = 256;
622 piece->iwidth = pipe->iwidth;
623 piece->iheight = pipe->iheight;
624 piece->module = module;
626 piece->blendop_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
627 piece->global_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
628 piece->global_mask_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
630 piece->cache_output_on_ram = TRUE;
631
632 // dsc_mask is static, single channel float image
633 piece->dsc_mask.channels = 1;
634 piece->dsc_mask.datatype = TYPE_FLOAT;
635 dt_iop_buffer_dsc_update_bpp(&piece->dsc_mask);
636
637 dt_iop_init_pipe(piece->module, pipe, piece);
638
639 pipe->nodes = g_list_append(pipe->nodes, piece);
640 }
641}
642
644 const dt_dev_pixelpipe_iop_t *const piece,
645 const dt_iop_buffer_dsc_t *const output_dsc)
646{
651
652 const dt_iop_colorspace_type_t blend_cst = dt_develop_blend_colorspace(piece, output_dsc->cst);
654 if(piece->dsc_in.cst != blend_cst
657 if(output_dsc->cst != blend_cst
658 && !(dt_iop_colorspace_is_rgb(output_dsc->cst) && dt_iop_colorspace_is_rgb(blend_cst)))
660
661 return transforms;
662}
663
664#define KILL_SWITCH_ABORT \
665 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
666 { \
667 if(!IS_NULL_PTR(cl_mem_output)) \
668 { \
669 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_output, NULL, NULL, FALSE); \
670 } \
671 return 1; \
672 }
673
674// Once we have a cache, stopping computation before full completion
675// has good chances of leaving it corrupted. So we invalidate it.
676#define KILL_SWITCH_AND_FLUSH_CACHE \
677 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
678 { \
679 return _abort_module_shutdown_cleanup(pipe, piece, module, input_hash, input, input_entry, hash, &output, \
680 &cl_mem_output, output_entry); \
681 }
682
683static void _print_perf_debug(dt_dev_pixelpipe_t *pipe, const dt_pixelpipe_flow_t pixelpipe_flow,
685 const gboolean recycled_output_cacheline, dt_times_t *start)
686{
687 char histogram_log[32] = "";
688 if(!(pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_NONE))
689 {
690 snprintf(histogram_log, sizeof(histogram_log), ", collected histogram on %s",
691 (pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_ON_GPU
692 ? "GPU"
693 : pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_ON_CPU ? "CPU" : ""));
694 }
695
696 gchar *module_label = dt_history_item_get_name(module);
698 start, "[dev_pixelpipe]", "processed `%s' on %s%s%s%s, blended on %s [%s]", module_label,
699 pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_GPU
700 ? "GPU"
701 : pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_CPU ? "CPU" : "",
702 pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_WITH_TILING ? " with tiling" : "",
703 recycled_output_cacheline ? ", recycled cacheline" : "",
704 (!(pixelpipe_flow & PIXELPIPE_FLOW_HISTOGRAM_NONE) && (piece->request_histogram & DT_REQUEST_ON))
705 ? histogram_log
706 : "",
707 pixelpipe_flow & PIXELPIPE_FLOW_BLENDED_ON_GPU
708 ? "GPU"
709 : pixelpipe_flow & PIXELPIPE_FLOW_BLENDED_ON_CPU ? "CPU" : "",
711 dt_free(module_label);
712}
713
714
715static void _print_nan_debug(dt_dev_pixelpipe_t *pipe, void *cl_mem_output, void *output,
716 const dt_iop_roi_t *roi_out, dt_iop_buffer_dsc_t *out_format,
717 dt_iop_module_t *module)
718{
719 if(!(darktable.unmuted & DT_DEBUG_NAN)) return;
720 if(!(darktable.unmuted & DT_DEBUG_VERBOSE)) return;
721 if((darktable.unmuted & DT_DEBUG_NAN) && strcmp(module->op, "gamma") != 0)
722 {
723 gchar *module_label = dt_history_item_get_name(module);
724
725 if(out_format->datatype == TYPE_FLOAT && out_format->channels == 4)
726 {
727 int hasinf = 0, hasnan = 0;
728 dt_aligned_pixel_t min = { FLT_MAX };
729 dt_aligned_pixel_t max = { FLT_MIN };
730
731 for(int k = 0; k < 4 * roi_out->width * roi_out->height; k++)
732 {
733 if((k & 3) < 3)
734 {
735 float f = ((float *)(output))[k];
736 if(isnan(f))
737 hasnan = 1;
738 else if(!isfinite(f))
739 hasinf = 1;
740 else
741 {
742 min[k & 3] = fmin(f, min[k & 3]);
743 max[k & 3] = fmax(f, max[k & 3]);
744 }
745 }
746 }
747 if(hasnan)
748 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs NaNs! [%s]\n", module_label,
750 if(hasinf)
751 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs non-finite floats! [%s]\n", module_label,
753 fprintf(stderr, "[dev_pixelpipe] module `%s' min: (%f; %f; %f) max: (%f; %f; %f) [%s]\n", module_label,
754 min[0], min[1], min[2], max[0], max[1], max[2], dt_pixelpipe_get_pipe_name(pipe->type));
755 }
756 else if(out_format->datatype == TYPE_FLOAT && out_format->channels == 1)
757 {
758 int hasinf = 0, hasnan = 0;
759 float min = FLT_MAX;
760 float max = FLT_MIN;
761
762 for(int k = 0; k < roi_out->width * roi_out->height; k++)
763 {
764 float f = ((float *)(output))[k];
765 if(isnan(f))
766 hasnan = 1;
767 else if(!isfinite(f))
768 hasinf = 1;
769 else
770 {
771 min = fmin(f, min);
772 max = fmax(f, max);
773 }
774 }
775 if(hasnan)
776 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs NaNs! [%s]\n", module_label,
778 if(hasinf)
779 fprintf(stderr, "[dev_pixelpipe] module `%s' outputs non-finite floats! [%s]\n", module_label,
781 fprintf(stderr, "[dev_pixelpipe] module `%s' min: (%f) max: (%f) [%s]\n", module_label, min, max,
783 }
784
785 dt_free(module_label);
786 }
787}
788
790 uint64_t *out_hash, const dt_dev_pixelpipe_iop_t **out_piece,
791 GList *pieces, int pos)
792{
793 // The pipeline is executed recursively, from the end. For each module n, starting from the end,
794 // if output is cached, take it, else if input is cached, take it, process it and output,
795 // else recurse to the previous module n-1 to get a an input.
796 void *input = NULL;
797 void *output = NULL;
798 void *cl_mem_output = NULL;
799
801
802 if(IS_NULL_PTR(pieces))
803 {
805 *out_piece = NULL;
806 return 0;
807 }
808
809 dt_iop_module_t *module = NULL;
811
813
814 // skip this module?
815 if(!piece->enabled)
816 return dt_dev_pixelpipe_process_rec(pipe, out_hash, out_piece, g_list_previous(pieces), pos - 1);
817
818 module = piece->module;
819
820 if(pipe->dev->gui_attached) pipe->dev->progress.total++;
821
823
824 const uint64_t hash = dt_dev_pixelpipe_node_hash(pipe, piece, piece->roi_out, pos);
825
826 // 1) Fast-track:
827 // If we have a cache entry for this hash, return it straight away,
828 // don't recurse through pipeline and don't process, unless this module still
829 // needs GUI-side sampling from its host input or the gamma display histogram
830 // needs the upstream cache entry.
831 dt_pixel_cache_entry_t *existing_cache = NULL;
832 void *existing_output = NULL;
833 const gboolean exact_output_cache_hit
834 = !_bypass_cache(pipe, piece)
835 && dt_dev_pixelpipe_cache_peek(darktable.pixelpipe_cache, hash, &existing_output, &existing_cache,
836 pipe->devid, NULL)
837 && !IS_NULL_PTR(existing_output);
838
839 if(exact_output_cache_hit)
840 {
841 /* An exact-hit child still needs one ref reserved for the immediate caller that will consume it next.
842 * `process_rec()` returns that upcoming-consumer ref as part of its contract instead of asking the caller
843 * to bump the counter again on input acquisition. */
845 _trace_cache_owner(pipe, module, "exact-hit-direct", "output", hash, NULL, existing_cache, FALSE);
846 *out_hash = hash;
847 *out_piece = piece;
848 return 0;
849 }
850
851 // 3) now recurse through the pipeline.
853 const dt_dev_pixelpipe_iop_t *previous_piece = NULL;
854 if(dt_dev_pixelpipe_process_rec(pipe, &input_hash, &previous_piece, g_list_previous(pieces), pos - 1))
855 {
856 /* Child recursion failed before this module acquired any output cache entry.
857 * Dropping `hash` here underflows cached exact-hit outputs during shutdown. */
859 "[pipeline] module=%s child recursion failed input_hash=%" PRIu64 " output_hash=%" PRIu64 "\n",
860 module->op, input_hash, hash);
861 return 1;
862 }
863
865
866 // Child recursion just published or exact-hit returned this hash with one ref already reserved for
867 // this immediate consumer. Reopen the live cache entry directly instead of going through exact-hit
868 // lookup, because exact-hit intentionally rejects auto-destroy entries while the parent recursion
869 // still needs to consume transient outputs in the same run.
870 dt_pixel_cache_entry_t *input_entry
872 if(!IS_NULL_PTR(previous_piece))
874 if(IS_NULL_PTR(input_entry) && !(module->flags() & IOP_FLAGS_TAKE_NO_INPUT))
875 {
877 "[pipeline] module=%s input cache entry missing input_hash=%" PRIu64 " output_hash=%" PRIu64
878 " prev_module=%s prev_hash=%" PRIu64 "\n",
879 module->op, input_hash, hash,
880 !IS_NULL_PTR(previous_piece) ? previous_piece->module->op : "",
881 !IS_NULL_PTR(previous_piece) ? previous_piece->global_hash : -1);
882 return 1;
883 }
884 input = input_entry ? dt_pixel_cache_entry_get_data(input_entry) : NULL;
885 _trace_cache_owner(pipe, module, "acquire", "input", input_hash, input, input_entry, FALSE);
886 if(input_entry)
887 _trace_buffer_content(pipe, module, "input-acquire", input, &piece->dsc_in, &piece->roi_in);
888 const size_t bufsize = (size_t)piece->dsc_out.bpp * piece->roi_out.width * piece->roi_out.height;
889 // Note: IS_NULL_PTR(input) is valid if we are on a GPU-only path, aka previous module ran on GPU
890 // without leaving its output on a RAM cache copy, and current module will also run on GPU.
891 // In this case, we rely on cl_mem_input for best performance (avoid memcpy between RAM and GPU).
892 // Should the GPU path fail at process time, we will init input and flush cl_mem_input into it.
893 // In any case, this avoids carrying a possibly-uninited input buffer, without knowing if it has
894 // data on it (or having to blindly copy back from vRAM to RAM).
895
896 // 3c) actually process this module BUT treat all bypasses first.
897 // special case: user requests to see channel data in the parametric mask of a module, or the blending
898 // mask. In that case we skip all modules manipulating pixel content and only process image distorting
899 // modules. Finally "gamma" is responsible for displaying channel/mask data accordingly.
900 if(pipe->dev->gui_attached
902 && !(module->operation_tags() & IOP_TAG_DISTORT)
903 && (piece->dsc_in.bpp == piece->dsc_out.bpp)
904 && !memcmp(&piece->roi_in, &piece->roi_out, sizeof(struct dt_iop_roi_t)))
905 {
906 /* Mask/channel passthrough keeps the exact child buffer alive for the next
907 * downstream module. This stage does not publish a new cacheline, so it
908 * must forward the child hash and actual previous piece contract exactly as
909 * they came from recursion, including the single reserved ref for the next
910 * real consumer. */
911 *out_hash = input_hash;
912 *out_piece = previous_piece;
913 return 0;
914 }
915
916 if(pipe->dev->gui_attached)
917 {
918 gchar *module_label = dt_history_item_get_name(module);
921 darktable.main_message = g_strdup_printf(_("Processing module `%s` for pipeline %s (%ix%i px @ %0.f%%)..."),
922 module_label, dt_pixelpipe_get_pipe_name(pipe->type),
923 piece->roi_out.width, piece->roi_out.height, piece->roi_out.scale * 100.f);
925 dt_free(module_label);
927 }
928
929 dt_pixel_cache_entry_t *output_entry = NULL;
930 gchar *type = dt_pixelpipe_get_pipe_name(pipe->type);
931
933
934 gchar *name = g_strdup_printf("module %s (%s) for pipe %s", module->op, module->multi_name, type);
935 // Cache bypass makes intermediate module outputs disposable, but the final output is the
936 // backbuffer consumed by Cairo. It must keep a host payload even when an edit mode (crop,
937 // clipping, ashift...) bypasses the downstream cache. Otherwise the GUI requests that host
938 // cacheline after every GPU render, while each cache-request pass publishes another device-only
939 // backbuffer and feeds an infinite preview recompute loop.
940 const gboolean keep_final_output = (hash == dt_dev_pixelpipe_get_hash(pipe));
941 gboolean cache_ram_output
942 = piece->cache_output_on_ram && (!_bypass_cache(pipe, piece) || keep_final_output);
943
944 /* `piece->cache_entry` is only valid as a writable-reuse hint for transient outputs that will
945 * be fully overwritten later. As soon as we keep the current output as a published cacheline in
946 * RAM, rekey reuse must stop for that piece so later runs cannot overwrite a long-term state in
947 * place just because the pipe is running in realtime. */
948 const gboolean allow_rekey_reuse = !(darktable.unmuted & DT_DEBUG_NOCACHE_REUSE) && !cache_ram_output;
951 cache_ram_output, allow_rekey_reuse,
952 &piece->cache_entry,
953 &output, &output_entry);
954 dt_free(name);
956 {
957 /* Another pipe already owns a cacheline for this exact hash. If that cacheline is
958 * still write-locked, this is not a processing error: it only means the concurrent
959 * publisher has not finished exposing the exact-hit payload yet. Wait for that
960 * publication to complete instead of aborting the whole recursion. */
961 dt_pixel_cache_entry_t *exact_entry
963 if(IS_NULL_PTR(exact_entry))
964 {
966 "[pipeline] module=%s exact-hit entry missing output_hash=%" PRIu64 "\n",
967 module->op, hash);
968 if(input_entry)
970 return 1;
971 }
972
978
979 _trace_cache_owner(pipe, module, "exact-hit-wait", "output", hash,
980 dt_pixel_cache_entry_get_data(exact_entry), exact_entry, FALSE);
981
982 if(input_entry)
984 *out_hash = hash;
985 *out_piece = piece;
986 return 0;
987 }
988 if(IS_NULL_PTR(output_entry))
989 {
991 "[pipeline] module=%s writable output acquisition failed output_hash=%" PRIu64
992 " acquire_status=%d\n",
993 module->op, hash, acquire_status);
994 if(input_entry)
996 return 1;
997 }
998 const gboolean new_entry = (acquire_status == DT_DEV_PIXELPIPE_CACHE_WRITABLE_CREATED);
999 _trace_cache_owner(pipe, module, (acquire_status == DT_DEV_PIXELPIPE_CACHE_WRITABLE_REKEYED) ? "acquire-rekeyed"
1000 : (new_entry ? "acquire-new" : "acquire-existing"),
1001 "output", hash, output, output_entry, FALSE);
1002
1003 /* get tiling requirement of module */
1005 tiling.factor_cl = tiling.maxbuf_cl = -1; // set sentinel value to detect whether callback set sizes
1006 module->tiling_callback(module, pipe, piece, &tiling);
1007 if (tiling.factor_cl < 0) tiling.factor_cl = tiling.factor; // default to CPU size if callback didn't set GPU
1008 if (tiling.maxbuf_cl < 0) tiling.maxbuf_cl = tiling.maxbuf;
1009
1010 /* does this module involve blending? */
1011 if(piece->blendop_data && ((dt_develop_blend_params_t *)piece->blendop_data)->mask_mode != DEVELOP_MASK_DISABLED)
1012 {
1013 /* get specific memory requirement for blending */
1014 dt_develop_tiling_t tiling_blendop = { 0 };
1015 tiling_callback_blendop(module, pipe, piece, &tiling_blendop);
1016
1017 /* aggregate in structure tiling */
1018 tiling.factor = fmax(tiling.factor, tiling_blendop.factor);
1019 tiling.factor_cl = fmax(tiling.factor_cl, tiling_blendop.factor);
1020 tiling.maxbuf = fmax(tiling.maxbuf, tiling_blendop.maxbuf);
1021 tiling.maxbuf_cl = fmax(tiling.maxbuf_cl, tiling_blendop.maxbuf);
1022 tiling.overhead = fmax(tiling.overhead, tiling_blendop.overhead);
1023 }
1024
1025 /* remark: we do not do tiling for blendop step, neither in opencl nor on cpu. if overall tiling
1026 requirements (maximum of module and blendop) require tiling for opencl path, then following blend
1027 step is anyhow done on cpu. we assume that blending itself will never require tiling in cpu path,
1028 because memory requirements will still be low enough. */
1029
1030 assert(tiling.factor > 0.0f);
1031 assert(tiling.factor_cl > 0.0f);
1032
1033 // Actual pixel processing for this module
1034 int error = 0;
1035 dt_times_t start;
1036 dt_get_times(&start);
1037
1038 const char *prev_module = dt_pixelpipe_cache_set_current_module(module ? module->op : NULL);
1039
1040#ifdef HAVE_OPENCL
1041 error = pixelpipe_process_on_GPU(pipe, piece, previous_piece, &tiling, &pixelpipe_flow,
1042 &cache_ram_output,
1043 input_entry, output_entry);
1044#else
1045 error = pixelpipe_process_on_CPU(pipe, piece, previous_piece, &tiling, &pixelpipe_flow,
1046 &cache_ram_output,
1047 input_entry, output_entry);
1048#endif
1049
1051 output = dt_pixel_cache_entry_get_data(output_entry);
1052
1053 _print_perf_debug(pipe, pixelpipe_flow, piece, module,
1054 (acquire_status != DT_DEV_PIXELPIPE_CACHE_WRITABLE_CREATED), &start);
1055
1056 if(pipe->dev->gui_attached) pipe->dev->progress.completed++;
1057
1058 if(error)
1059 {
1061 "[pipeline] module=%s backend processing failed input_hash=%" PRIu64 " output_hash=%" PRIu64
1062 " input_cst=%d output_cst=%d roi_in=%dx%d roi_out=%dx%d\n",
1063 module->op, input_hash, hash, piece->dsc_in.cst, piece->dsc_out.cst,
1064 piece->roi_in.width, piece->roi_in.height, piece->roi_out.width, piece->roi_out.height);
1065 _trace_cache_owner(pipe, module, "error-cleanup", "input", input_hash, input, input_entry, FALSE);
1066 _trace_cache_owner(pipe, module, "error-cleanup", "output", hash, output, output_entry, FALSE);
1067 // Ensure we always release locks and cache references on error, otherwise cache eviction/GC will stall.
1070 if(input_entry)
1071 {
1074 }
1075
1076 // No point in keeping garbled output
1080 return 1;
1081 }
1082
1083 // Publish the module output descriptor authored for this stage. The cache entry keeps the
1084 // descriptor of the pixels this module actually published, not the stale descriptor of its input.
1085 _trace_cache_owner(pipe, module, "publish", "output", hash, output, output_entry, FALSE);
1086 _trace_buffer_content(pipe, module, "publish", output, &piece->dsc_out, &piece->roi_out);
1087
1088 if(!IS_NULL_PTR(output_entry))
1089 {
1090 piece->cache_entry = *output_entry;
1091 }
1092 else
1094
1095 if(!IS_NULL_PTR(output_entry) && !IS_NULL_PTR(output) && !(module->flags() & IOP_FLAGS_CPU_WRITES_OPENCL)
1096 && ((pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_ON_CPU)
1097 || (pixelpipe_flow & PIXELPIPE_FLOW_PROCESSED_WITH_TILING)))
1098 {
1099 dt_dev_pixelpipe_gpu_flush_host_pinned_images(pipe, output, output_entry, "module output host rewrite");
1100 /* CPU/tiling processing rewrote the whole host buffer for this output. If the cache entry was rekeyed
1101 * from an older GPU stage, any cached device-only images still hanging off the same entry now point to
1102 * obsolete pixels. Drop them here so later mixed GPU/CPU modules cannot resurrect stale device payloads. */
1104 }
1105
1106 // Flag to throw away intermediate outputs as soon as the next module consumes them.
1107 // `cache_output` only means the backend had to keep a host-authoritative payload for this
1108 // stage (for example because the next module may need RAM or because the current stage ran
1109 // through an OpenCL cache path). In no-cache/bypass pipelines, that does not make the
1110 // published cacheline long-lived: once the downstream module takes its input ref, this
1111 // stage is transient and must disappear on release. Only the final published output needs
1112 // to survive long enough for dt_dev_pixelpipe_process() to promote it to the backbuffer,
1113 // otherwise thumbnail/export callers only see a missing exact-hit and fall back to invalid
1114 // placeholder pixels.
1115 if(_bypass_cache(pipe, piece) && !keep_final_output)
1117
1118 if(pipe->dev->gui_attached)
1119 {
1122 darktable.main_message = NULL;
1125 }
1126
1127 // From here on we only publish/inspect the finished output. Keep the writable lock strictly
1128 // around cacheline allocation and backend processing, then release it at one visible point
1129 // before the generic tail cleanup shared by darkroom and headless paths.
1131
1133
1134 // Decrease reference count on input and flush it if it was flagged for auto destroy previously
1135 _trace_cache_owner(pipe, module, "release", "input", input_hash, input, input_entry, FALSE);
1136 if(input_entry)
1137 {
1140 }
1141
1142 // Print min/max/Nan in debug mode only
1143 if((darktable.unmuted & DT_DEBUG_NAN) && strcmp(module->op, "gamma") != 0 && !IS_NULL_PTR(output))
1144 {
1146 _print_nan_debug(pipe, cl_mem_output, output, &piece->roi_out, &piece->dsc_out, module);
1148 }
1149
1151
1152 *out_hash = hash;
1153 *out_piece = piece;
1154 return 0;
1155}
1156
1158{
1159 GList *nodes = g_list_last(pipe->nodes);
1161 while(strcmp(piece->module->op, op))
1162 {
1163 piece->enabled = 0;
1164 piece = NULL;
1165 nodes = g_list_previous(nodes);
1166 if(!nodes) break;
1167 piece = (dt_dev_pixelpipe_iop_t *)nodes->data;
1168 }
1169}
1170
1172{
1173 GList *nodes = pipe->nodes;
1175 while(strcmp(piece->module->op, op))
1176 {
1177 piece->enabled = 0;
1178 piece = NULL;
1179 nodes = g_list_next(nodes);
1180 if(!nodes) break;
1181 piece = (dt_dev_pixelpipe_iop_t *)nodes->data;
1182 }
1183}
1184
1185#define KILL_SWITCH_PIPE \
1186 if(dt_dev_pixelpipe_has_shutdown(pipe)) \
1187 { \
1188 if(pipe->devid >= 0) \
1189 { \
1190 dt_opencl_unlock_device(pipe->devid); \
1191 pipe->devid = -1; \
1192 } \
1193 if(pipe->forms) \
1194 { \
1195 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_free_form); \
1196 pipe->forms = NULL; \
1197 } \
1198 dt_pthread_mutex_unlock(&darktable.pipeline_threadsafe); \
1199 return 1; \
1200 }
1201
1202
1204{
1205 switch(error)
1206 {
1207 case 1:
1208 dt_print(DT_DEBUG_OPENCL, "[opencl] Opencl errors; disabling opencl for %s pipeline!\n", dt_pixelpipe_get_pipe_name(pipe->type));
1209 dt_control_log(_("Ansel discovered problems with your OpenCL setup; disabling OpenCL for %s pipeline!"), dt_pixelpipe_get_pipe_name(pipe->type));
1210 break;
1211 case 2:
1213 "[opencl] Too many opencl errors; disabling opencl for this session!\n");
1214 dt_control_log(_("Ansel discovered problems with your OpenCL setup; disabling OpenCL for this session!"));
1215 break;
1216 default:
1217 break;
1218 }
1219}
1220
1222{
1223 const uint64_t requested_hash = dt_dev_pixelpipe_get_hash(pipe);
1224 const uint64_t entry_hash = entry->hash;
1225
1226 _trace_cache_owner(pipe, NULL, "backbuf-update", "backbuf", requested_hash,
1227 entry ? entry->data : NULL, entry, FALSE);
1228
1229 if(requested_hash == DT_PIXELPIPE_CACHE_HASH_INVALID
1230 || entry_hash == DT_PIXELPIPE_CACHE_HASH_INVALID
1231 || entry_hash != requested_hash)
1232 {
1236 return;
1237 }
1238
1239 // Keep exactly one cache reference to the last valid output ("backbuf") for display.
1240 // This prevents the cache entry from being evicted while still in use by the GUI,
1241 // without leaking references on repeated cache hits.
1242 const gboolean hash_changed = (dt_dev_backbuf_get_hash(&pipe->backbuf) != entry_hash);
1243 if(hash_changed)
1244 {
1247 }
1248
1249 int bpp = 0;
1250 if(roi.width > 0 && roi.height > 0)
1251 bpp = (int)(dt_pixel_cache_entry_get_size(entry) / ((size_t)roi.width * (size_t)roi.height));
1252
1253 // Always refresh backbuf geometry/state, even when the cache key is unchanged.
1254 // Realtime drawing can update pixels in-place in the same cacheline, so width/height/history
1255 // must stay synchronized independently from key changes.
1256 dt_dev_set_backbuf(&pipe->backbuf, roi.width, roi.height, bpp, entry_hash,
1258}
1259
1260static GList *_get_requested_piece_node(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module, int *pos)
1261{
1262 if(pos) *pos = 0;
1263 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(module)) return NULL;
1264
1265 int current_pos = 1;
1266 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node), current_pos++)
1267 {
1268 dt_dev_pixelpipe_iop_t *const piece = node->data;
1269 if(piece && piece->enabled && piece->module == module)
1270 {
1271 if(pos) *pos = current_pos;
1272 return node;
1273 }
1274 }
1275
1276 return NULL;
1277}
1278
1280{
1281 /* `pipe->devid` is only valid while the current run owns the OpenCL device lock.
1282 * Reset it before any cache probe so callers never reuse a stale device id from a
1283 * previous pipeline pass. */
1284 pipe->devid = -1;
1285
1286 /* Record what image / pipeline is being processed, for crash reports. */
1287 if(pipe->dev)
1288 {
1289 const char *pl = "other";
1290 switch(pipe->type)
1291 {
1292 case DT_DEV_PIXELPIPE_FULL: pl = "darkroom"; break;
1293 case DT_DEV_PIXELPIPE_PREVIEW: pl = "darkroom-preview"; break;
1294 case DT_DEV_PIXELPIPE_EXPORT: pl = "export"; break;
1295 case DT_DEV_PIXELPIPE_THUMBNAIL: pl = "thumbnail"; break;
1296 default: break;
1297 }
1300 }
1301
1303 {
1304 fprintf(stderr, "[memory] before pixelpipe process\n");
1306 }
1307
1309
1310 if(pipe->dev->gui_attached)
1311 {
1312 pipe->dev->color_picker.pending_module = NULL;
1313 pipe->dev->color_picker.pending_pipe = NULL;
1315 }
1316
1317 // Get the roi_out hash of all nodes.
1318 // Get the previous output size of the module, for cache invalidation.
1319 dt_dev_pixelpipe_get_roi_in(pipe, roi);
1321 const guint pos = g_list_length(pipe->dev->iop);
1322
1323 const guint previous_raster_refs = pipe->raster_mask_hashes->len;
1324
1331 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node))
1332 {
1333 const dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)node->data;
1334 if(!piece->enabled) continue;
1335
1336 GHashTableIter mask_iter;
1337 gpointer mask_key = NULL;
1338 gpointer mask_name = NULL;
1339 g_hash_table_iter_init(&mask_iter, piece->module->raster_mask.source.masks);
1340 while(g_hash_table_iter_next(&mask_iter, &mask_key, &mask_name))
1341 {
1342 const int mask_id = GPOINTER_TO_INT(mask_key);
1343 if(!pipe->store_all_raster_masks
1344 && !dt_iop_is_raster_mask_used(piece->module, mask_id))
1345 continue;
1346
1347 const uint64_t mask_hash = dt_dev_pixelpipe_raster_mask_hash(piece, mask_id);
1349 darktable.pixelpipe_cache, mask_hash, NULL, NULL))
1350 g_array_append_val(pipe->raster_mask_hashes, mask_hash);
1351 }
1352 }
1353
1354 for(guint k = 0; k < previous_raster_refs; k++)
1355 {
1356 const uint64_t hash = g_array_index(pipe->raster_mask_hashes, uint64_t, k);
1358 }
1359 if(previous_raster_refs > 0)
1360 g_array_remove_range(pipe->raster_mask_hashes, 0, previous_raster_refs);
1361
1363 const dt_iop_module_t *const requested_module = dt_dev_pixelpipe_get_cache_request_module(pipe);
1365
1366 GList *requested_pieces = g_list_last(pipe->nodes);
1367 int requested_pos = (int)pos;
1368 dt_dev_pixelpipe_iop_t *requested_piece = NULL;
1369 gboolean requested_backbuf = TRUE;
1370
1371 if(cache_request == DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE && !IS_NULL_PTR(requested_module))
1372 {
1373 requested_pieces = _get_requested_piece_node(pipe, requested_module, &requested_pos);
1374 if(requested_pieces)
1375 {
1376 requested_piece = requested_pieces->data;
1377 requested_backbuf = FALSE;
1378 }
1379 else
1380 {
1381 dt_print(DT_DEBUG_DEV, "[pixelpipe/gui] requested module cache target disappeared pipe=%s module=%s\n",
1382 dt_pixelpipe_get_pipe_name(pipe->type), requested_module->op);
1383 }
1384 }
1385
1386 void *buf = NULL;
1387
1388 /* GUI cache requests can target either the final backbuffer or one module output in the middle of the
1389 current synchronized graph. Exact-hit checks must therefore look at the requested target instead of
1390 always assuming the run goes to the pipe end. */
1391 const uint64_t requested_hash = requested_backbuf ? dt_dev_pixelpipe_get_hash(pipe)
1392 : requested_piece ? requested_piece->global_hash
1394 dt_pixel_cache_entry_t *entry = NULL;
1395 if(!_bypass_cache(pipe, requested_piece)
1396 && requested_hash != DT_PIXELPIPE_CACHE_HASH_INVALID
1397 && dt_dev_pixelpipe_cache_peek(darktable.pixelpipe_cache, requested_hash, &buf, &entry,
1398 pipe->devid, NULL)
1399 && !IS_NULL_PTR(buf))
1400 {
1401 if(requested_backbuf)
1402 _update_backbuf_cache_reference(pipe, roi, entry);
1403 return 0;
1404 }
1405
1406 dt_print(DT_DEBUG_DEV, "[pixelpipe] Started %s pipeline recompute at %i×%i px\n",
1407 dt_pixelpipe_get_pipe_name(pipe->type), roi.width, roi.height);
1408
1409 // get a snapshot of the mask list
1411 pipe->forms = dt_masks_dup_forms_deep(pipe->dev->forms, NULL);
1413
1414 // go through the list of modules from the end:
1415 GList *pieces = g_list_last(pipe->nodes);
1416
1417 // Because it's possible here that we export at full resolution,
1418 // and our memory planning doesn't account for several concurrent pipelines
1419 // at full size, we allow only one pipeline at a time to run.
1420 // This is because wavelets decompositions and such use 6 copies,
1421 // so the RAM usage can go out of control here.
1423
1425 {
1426 const guint node_count = g_list_length(pipe->nodes);
1427 uint64_t *invalidated_hashes = g_new(uint64_t, node_count);
1428 size_t invalidated_count = 0;
1429 gboolean reached_provider = FALSE;
1430
1442 for(GList *node = g_list_first(pipe->nodes); node; node = g_list_next(node))
1443 {
1445 if(!piece->enabled) continue;
1446
1447 reached_provider |= (piece->global_hash == pipe->reentry_hash);
1448 if(reached_provider)
1449 invalidated_hashes[invalidated_count++] = piece->global_hash;
1450 }
1451
1452 const int retained = dt_dev_pixelpipe_cache_invalidate_hashes(
1453 darktable.pixelpipe_cache, invalidated_hashes, invalidated_count);
1455 "[raster masks] invalidated %" G_GSIZE_FORMAT " cache states at retry from provider=%" PRIu64
1456 " retained=%d pipe=%s\n",
1457 invalidated_count, pipe->reentry_hash, retained,
1459 dt_free(invalidated_hashes);
1460 }
1461
1462 pipe->opencl_enabled = dt_opencl_update_settings(); // update enabled flag and profile from preferences
1463 pipe->devid = (pipe->opencl_enabled) ? dt_opencl_lock_device(pipe->type)
1464 : -1; // try to get/lock opencl resource
1465
1466 if(pipe->devid > -1)
1467 {
1469 pipe->last_devid = pipe->devid;
1470 }
1471 dt_print(DT_DEBUG_OPENCL, "[pixelpipe_process] [%s] using device %d\n", dt_pixelpipe_get_pipe_name(pipe->type),
1472 pipe->devid);
1473
1475
1476 gboolean keep_running = TRUE;
1477 int runs = 0;
1478 int opencl_error = 0;
1479 int err = 0;
1480
1481 while(keep_running && runs < 3)
1482 {
1483 ++runs;
1484
1485 /* Mask preview is authored while the current run advances through blend.c.
1486 * Reset it for each retry so a stale state from a previous pass cannot leak
1487 * into the next recursion before the active module reaches its own blend. */
1489
1490#ifdef HAVE_OPENCL
1492#endif
1493
1495
1496 dt_times_t start;
1497 dt_get_times(&start);
1498 uint64_t final_hash = -1;
1499 const dt_dev_pixelpipe_iop_t *final_piece = NULL;
1500 err = dt_dev_pixelpipe_process_rec(pipe, &final_hash, &final_piece,
1501 requested_backbuf ? pieces : requested_pieces,
1502 requested_backbuf ? pos : requested_pos);
1503 (void)final_piece;
1504 gchar *msg = g_strdup_printf("[pixelpipe] %s internal pixel pipeline processing", dt_pixelpipe_get_pipe_name(pipe->type));
1505 dt_show_times(&start, msg);
1506 dt_free(msg);
1507
1508 // get status summary of opencl queue by checking the eventlist
1509 const int oclerr = (pipe->devid > -1) ? dt_opencl_events_flush(pipe->devid, TRUE) != 0 : 0;
1510
1511 // Check if we had opencl errors ....
1512 // remark: opencl errors can come in two ways: pipe->opencl_error is TRUE (and err is TRUE) OR oclerr is
1513 // TRUE
1514 keep_running = (oclerr || (err && pipe->opencl_error));
1515 if(keep_running)
1516 {
1517 // Log the error
1518 darktable.opencl->error_count++; // increase error count
1519 opencl_error = 1; // = any OpenCL error, next run goes to CPU
1520
1521 // Disable OpenCL for this pipe
1523 pipe->opencl_enabled = 0;
1524 pipe->opencl_error = 0;
1525 pipe->devid = -1;
1526
1528 {
1529 // Too many errors : dispable OpenCL for this session
1531 dt_capabilities_remove("opencl");
1532 opencl_error = 2; // = too many OpenCL errors, all runs go to CPU
1533 }
1534
1535 _print_opencl_errors(opencl_error, pipe);
1536 }
1537 else if(!dt_dev_pixelpipe_has_shutdown(pipe))
1538 {
1539 // No opencl errors, no killswitch triggered: we should have a valid output buffer now.
1540 dt_pixel_cache_entry_t *final_entry = NULL;
1541 void *final_buf = NULL;
1542 if(!requested_backbuf)
1543 {
1545 }
1547 &final_entry, pipe->devid, NULL)
1548 && !IS_NULL_PTR(final_buf))
1549 {
1550 _update_backbuf_cache_reference(pipe, roi, final_entry);
1552 }
1553 else
1554 {
1556 "[picker/rec] final output cache missing pipe=%s hash=%" PRIu64 " history=%" PRIu64
1557 " devid=%d err=%d\n",
1559 dt_dev_pixelpipe_get_history_hash(pipe), pipe->devid, err);
1561 }
1562
1563 // Note : the last output (backbuf) of the pixelpipe cache is internally locked
1564 // Whatever consuming it will need to unlock it.
1565 }
1566 }
1567
1569
1570 // release resources:
1571 if(pipe->forms)
1572 {
1573 g_list_free_full(pipe->forms, (void (*)(void *))dt_masks_free_form);
1574 pipe->forms = NULL;
1575 }
1576 if(pipe->devid >= 0)
1577 {
1579 pipe->devid = -1;
1580 }
1581
1582 // terminate
1584
1585 // If an intermediate module set that, be sure to reset it at the end
1586 pipe->flush_cache = FALSE;
1587 return err;
1588}
1589
1590// clang-format off
1591// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1592// vim: shiftwidth=2 expandtab tabstop=2 cindent
1593// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1594// 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:1636
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:179
@ 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:761
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:861
void dt_show_times(const dt_times_t *start, const char *prefix)
Definition darktable.c:1580
darktable_t darktable
Definition darktable.c:181
void dt_print_mem_usage()
Definition darktable.c:1801
void dt_capabilities_remove(char *capability)
Definition darktable.c:1784
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...)
Definition darktable.c:1594
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define UNKNOWN_IMAGE
Definition darktable.h:182
@ DT_DEBUG_OPENCL
Definition darktable.h:722
@ DT_DEBUG_PIPE
Definition darktable.h:741
@ DT_DEBUG_PIPECACHE
Definition darktable.h:720
@ DT_DEBUG_NAN
Definition darktable.h:726
@ DT_DEBUG_MEMORY
Definition darktable.h:724
@ DT_DEBUG_VERBOSE
Definition darktable.h:743
@ DT_DEBUG_DEV
Definition darktable.h:717
@ DT_DEBUG_NOCACHE_REUSE
Definition darktable.h:745
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
static void dt_get_times(dt_times_t *t)
Definition darktable.h:921
#define __OMP_FOR_SIMD__(...)
Definition darktable.h:260
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
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:1886
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1493
@ 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:374
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:359
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:379
#define dt_pthread_rwlock_unlock
Definition dtpthread.h:392
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
#define dt_pthread_rwlock_rdlock
Definition dtpthread.h:393
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
Definition format.c:28
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:3067
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:558
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:576
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:213
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_FLAGS_TAKE_NO_INPUT
Definition imageop.h:176
@ IOP_FLAGS_CPU_WRITES_OPENCL
Definition imageop.h:180
@ IOP_CS_RGB_DISPLAY
Definition imageop.h:210
@ IOP_TAG_DISTORT
Definition imageop.h:151
GList * dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
Deep-copy an order list.
Definition iop_order.c:1996
float *const restrict const size_t k
GList * dt_masks_dup_forms_deep(GList *forms, dt_masks_form_t *form)
Duplicate the list of forms, replacing a single item by formid match.
void dt_masks_free_form(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:1648
void dt_opencl_events_reset(const int devid)
Definition opencl.c:2891
cl_int dt_opencl_events_flush(const int devid, const int reset)
Definition opencl.c:2968
int dt_opencl_lock_device(const int pipetype)
Definition opencl.c:1563
void dt_opencl_check_tuning(const int devid)
Definition opencl.c:2647
int dt_opencl_update_settings(void)
Definition opencl.c:2754
#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)
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_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:496
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:790
struct dt_opencl_t * opencl
Definition darktable.h:785
int32_t unmuted
Definition darktable.h:760
dt_pthread_mutex_t pipeline_threadsafe
Definition darktable.h:808
char * main_message
Definition darktable.h:837
struct dt_control_t * control
Definition darktable.h:773
dt_pthread_mutex_t log_mutex
Definition control.h:240
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:285
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:279
struct dt_iop_module_t * pending_module
Definition develop.h:407
int completed
Definition develop.h:493
struct dt_dev_pixelpipe_t * pending_pipe
Definition develop.h:408
dt_pthread_rwlock_t masks_mutex
Definition develop.h:333
struct dt_develop_t::@26 progress
uint64_t piece_hash
Definition develop.h:401
GList * forms
Definition develop.h:321
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:256
Region of interest passed through the pixelpipe.
Definition imageop.h:72
int error_count
Definition opencl.h:238
int stopped
Definition opencl.h:235
uint64_t hash
void * data
void dt_telemetry_record_file_type(const struct dt_image_t *img, const char *pipeline)
Definition telemetry.c:426
#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