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