Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
pixelpipe_gpu.c
Go to the documentation of this file.
1/*
2 Private OpenCL pixelpipe backend.
3*/
4
6#include "system/macros.h"
8#include "common/logging.h"
10#include "develop/iop_order.h"
11#include "common/opencl.h"
12#include "develop/blend.h"
16
17#include <math.h>
18#include <stdio.h>
19
21 dt_pixel_cache_entry_t *cache_entry, const char *reason)
22{
23#ifdef HAVE_OPENCL
24 if(pipe && !pipe->realtime && pipe->devid >= 0 && host_ptr && cache_entry)
25 {
26 /* Non-realtime host writes invalidate reusable pinned images bound to the previous ROI/hash.
27 * Realtime keeps its pinned reuse untouched to avoid stalling the live draw path. */
29 pipe->devid))
30 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] flushed pinned OpenCL images after %s\n",
31 reason ? reason : "host write");
32 }
33#else
34 (void)pipe;
35 (void)host_ptr;
36 (void)cache_entry;
37 (void)reason;
38#endif
39}
40
41#ifdef HAVE_OPENCL
42
44{
45 return dt_opencl_is_inited() && piece->process_cl_ready && module->process_cl;
46}
47
49 float **input, void **cl_mem_input,
51 dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
52{
53 dt_iop_module_t *module = piece->module;
54
55 // An input that only lives in RAM (its producer ran on CPU, or it came from the cache) is
56 // already the authoritative copy, and there is no device buffer to read back.
57 // dt_dev_pixelpipe_cache_sync_cl_buffer() fails on a NULL device buffer, which sent every such
58 // input of an OpenCL-tiled module to the CPU.
59 if(!IS_NULL_PTR(*input) && IS_NULL_PTR(*cl_mem_input)) return 0;
60
61 if(IS_NULL_PTR(*input))
62 {
64 *input = dt_pixel_cache_alloc(input_entry);
66 }
67
68 if(IS_NULL_PTR(*input))
69 {
71 "[dev_pixelpipe] %s CPU fallback has no input buffer (cache allocation failed?)\n",
72 module->name());
73 return 1;
74 }
75
77 const int fail = dt_dev_pixelpipe_cache_sync_cl_buffer(pipe->devid, *input, *cl_mem_input, &piece->roi_in, CL_MAP_READ,
78 piece->dsc_in.bpp, module,
79 "cpu fallback input copy to cache");
80 if(!fail) dt_dev_pixelpipe_cache_flag_host_written(input_entry);
82
83 if(fail)
84 {
86 "[dev_pixelpipe] %s couldn't resync GPU input to cache for CPU fallback\n",
87 module->name());
88 return 1;
89 }
90 return 0;
91}
92
94 void **cl_mem_input,
95 gboolean *const borrowed_cl_mem_input,
97 const dt_dev_pixelpipe_iop_t *previous_piece,
99 dt_pixelpipe_flow_t *pixelpipe_flow,
100 gboolean *const cache_output,
101 dt_pixel_cache_entry_t *input_entry,
102 dt_pixel_cache_entry_t *output_entry)
103{
104 dt_iop_module_t *module = piece->module;
105
106 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] %s will run directly on CPU\n", module->name());
107
113 if(module->flags() & IOP_FLAGS_TAKE_NO_INPUT)
114 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
115 cache_output, input_entry, output_entry);
116
117 /* CPU fallback only needs a valid host buffer. If `input` already exists here, the upstream
118 * hand-off has already materialized authoritative RAM and re-reading the same pixels back out
119 * of the cached OpenCL image is redundant. A host buffer left over from the entry's previous
120 * hash is not `input`: dt_pixel_cache_entry_get_data() reported it as NULL. */
121 if(input && !IS_NULL_PTR(*input))
122 {
124 "[dev_pixelpipe] %s CPU fallback will reuse host input\n",
125 module->name());
126 }
127 else if(cl_mem_input && !IS_NULL_PTR(*cl_mem_input))
128 {
129 if(input && IS_NULL_PTR(*input))
130 {
132 *input = dt_pixel_cache_alloc(input_entry);
134 }
135
136 if(IS_NULL_PTR(input) || IS_NULL_PTR(*input))
137 {
139 "[dev_pixelpipe] %s CPU fallback has no input buffer (cache allocation failed?)\n",
140 module->name());
141 if(borrowed_cl_mem_input && *borrowed_cl_mem_input)
142 {
143 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, *cl_mem_input);
144 *cl_mem_input = NULL;
145 *borrowed_cl_mem_input = FALSE;
146 }
147 else
148 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_input, input_entry, NULL,
150 return 1;
151 }
152
153 *input = dt_dev_pixelpipe_cache_restore_cl_buffer(pipe, *input, *cl_mem_input, &piece->roi_in, module,
154 piece->dsc_in.bpp, input_entry,
155 "cpu fallback input copy to cache");
156 if(IS_NULL_PTR(*input))
157 {
159 "[dev_pixelpipe] %s couldn't resync GPU input to cache for CPU fallback\n",
160 module->name());
161 if(borrowed_cl_mem_input && *borrowed_cl_mem_input)
162 {
163 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, *cl_mem_input);
164 *cl_mem_input = NULL;
165 *borrowed_cl_mem_input = FALSE;
166 }
167 else
168 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_input, input_entry, NULL,
170 return 1;
171 }
172 }
173 else if(!input || IS_NULL_PTR(*input))
174 {
176 "[dev_pixelpipe] %s CPU fallback has no input buffer (cache allocation failed?)\n",
177 module->name());
178 return 1;
179 }
180
181 if(borrowed_cl_mem_input && *borrowed_cl_mem_input)
182 {
183 /* Device-only inputs borrowed from the cache stay owned by the cache entry.
184 * CPU fallback only needs to drop the temporary borrow after the device->host
185 * sync, otherwise releasing the cl_mem here leaves a stale cache-side pointer
186 * that later thumbnail runs may reopen as corrupted input. */
187 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, *cl_mem_input);
188 *cl_mem_input = NULL;
189 *borrowed_cl_mem_input = FALSE;
190 }
191 else
192 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_input, input_entry, *input,
194
195 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
196 cache_output, input_entry, output_entry);
197}
198
200 const dt_dev_pixelpipe_iop_t *previous_piece,
202 dt_pixelpipe_flow_t *pixelpipe_flow,
203 gboolean *const cache_output,
204 dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
205{
206 dt_iop_module_t *module = piece->module;
207 /* The module's timed region is exactly this function, so a module that costs more than its
208 * own process_cl() is spending the difference in here -- input colourspace transform, buffer
209 * acquisition, blend, readback. Split the prologue from the kernel so the log says which. */
210 const gint64 gpu_stage_t0 = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
211 /* The prologue's two candidate costs, split out because guessing between them has already
212 * been wrong once: borrowing the upstream vRAM payload, and allocating this node's own host
213 * output -- the latter going through the cache allocator, which evicts to make room. */
214 double gpu_in_borrow_ms = 0.0;
215 double gpu_out_alloc_ms = 0.0;
216 double gpu_in_prepare_ms = 0.0;
217 double gpu_out_cl_ms = 0.0;
218 double gpu_cst_ms = 0.0;
219 gboolean gpu_out_cl_reused = FALSE;
220 float *input = input_entry ? dt_pixel_cache_entry_get_data(input_entry) : NULL;
221 // The buffer, not its pixels: a rekeyed output's host buffer backs the pinned output image even
222 // when the output is not copied back to RAM.
223 void *output = dt_pixel_cache_entry_get_buffer(output_entry);
224 void *cl_mem_input = NULL;
225 void *cl_mem_output = NULL;
226 void *cl_mem_process_input = NULL;
227 void *cl_mem_blend_input = NULL;
228 void *cl_mem_blend_output = NULL;
229 void *cl_mem_process_input_temp = NULL;
230 void *cl_mem_blend_input_temp = NULL;
231 void *cl_mem_blend_output_temp = NULL;
232 dt_pixel_cache_entry_t *cpu_input_entry = input_entry;
233 dt_pixel_cache_entry_t *locked_input_entry = NULL;
234 gboolean borrowed_cl_mem_input = FALSE;
235 const dt_iop_buffer_dsc_t actual_input_dsc = previous_piece ? previous_piece->dsc_out : pipe->dev->image_storage.dsc;
236 dt_iop_buffer_dsc_t process_input_dsc = actual_input_dsc;
237 dt_iop_buffer_dsc_t blend_input_dsc = actual_input_dsc;
238 dt_iop_buffer_dsc_t blend_output_dsc = piece->dsc_out;
239
240 // Try to reuse the cached vRAM buffer for the input entry if available
241 // except for basebuffer module which takes no input
242 if(!(piece->module->flags() & IOP_FLAGS_TAKE_NO_INPUT))
243 {
244 const gint64 borrow_t0 = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
245 cl_mem_input = dt_dev_pixelpipe_cache_borrow_cl_payload(input_entry, pipe->devid,
246 piece->roi_in.width, piece->roi_in.height,
247 actual_input_dsc.bpp);
249 gpu_in_borrow_ms = (g_get_monotonic_time() - borrow_t0) / 1000.0;
250 borrowed_cl_mem_input = (!IS_NULL_PTR(cl_mem_input));
251 if(IS_NULL_PTR(cl_mem_input))
252 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] %s could not get a cached vRAM input buffer.\n", module->name());
253
254 // Note: if that fails, we will attempt resync from RAM cache later
255
256 if(IS_NULL_PTR(input) && IS_NULL_PTR(cl_mem_input))
257 {
258 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] %s has no RAM nor vRAM input... aborting.\n", module->name());
259 return 1;
260 }
261 }
262
263 if(!_is_opencl_supported(pipe, piece, module) || !pipe->opencl_enabled || !(pipe->devid >= 0))
264 {
265 return _gpu_early_cpu_fallback_if_unsupported(pipe, &input, &cl_mem_input,
266 &borrowed_cl_mem_input, piece, previous_piece, tiling,
267 pixelpipe_flow, cache_output,
268 input_entry, output_entry);
269 }
270
271 const dt_iop_order_iccprofile_info_t *const work_profile
272 = (process_input_dsc.cst != IOP_CS_RAW || piece->dsc_in.cst != IOP_CS_RAW)
274 : NULL;
275
276 const float required_factor_cl
277 = fmaxf(1.0f, (!IS_NULL_PTR(cl_mem_input)) ? tiling->factor_cl - 1.0f : tiling->factor_cl);
278
279 const size_t precheck_width = ROUNDUPDWD(MAX(piece->roi_in.width, piece->roi_out.width), pipe->devid);
280 const size_t precheck_height = ROUNDUPDHT(MAX(piece->roi_in.height, piece->roi_out.height), pipe->devid);
281 // Remember *why* the pre-check (dis)allowed OpenCL so the CPU-fallback message on the
282 // error path can quote the limit that was actually exceeded instead of a fixed one.
283 size_t fit_needed = 0, fit_limit = 0;
284 dt_opencl_fit_reason_t fit_reason
285 = dt_opencl_image_fits_device_reason(pipe->devid, precheck_width, precheck_height,
286 MAX(piece->dsc_in.bpp, piece->dsc_out.bpp),
287 required_factor_cl, tiling->overhead, &fit_needed, &fit_limit);
288 gboolean fits_on_device = (fit_reason == DT_OPENCL_FIT_OK);
289 if(!fits_on_device)
290 {
292 "[dev_pixelpipe] %s pre-check didn't fit on device, flushing cached pinned buffers and retrying\n",
293 module->name());
295 fit_reason = dt_opencl_image_fits_device_reason(pipe->devid, precheck_width, precheck_height,
296 MAX(piece->dsc_in.bpp, piece->dsc_out.bpp),
297 required_factor_cl, tiling->overhead, &fit_needed, &fit_limit);
298 fits_on_device = (fit_reason == DT_OPENCL_FIT_OK);
299 }
300
301 gboolean possible_cl = !(pipe->type == DT_DEV_PIXELPIPE_PREVIEW
302 && (module->flags() & IOP_FLAGS_PREVIEW_NON_OPENCL))
303 && (fits_on_device || piece->process_tiling_ready);
304
305 if(!possible_cl || !fits_on_device) *cache_output = TRUE;
306 if(*cache_output && IS_NULL_PTR(output))
307 {
308 const gint64 alloc_t0 = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
309 output = dt_pixel_cache_alloc(output_entry);
311 gpu_out_alloc_ms = (g_get_monotonic_time() - alloc_t0) / 1000.0;
312 if(IS_NULL_PTR(output)) goto error;
313 }
314
315 if(possible_cl && !fits_on_device)
316 {
317 // Prepare the input buffer for tiling
318 const float cl_px = dt_opencl_get_device_available(pipe->devid)
319 / (sizeof(float) * MAX(piece->dsc_in.bpp, piece->dsc_out.bpp)
320 * ceilf(required_factor_cl));
321 const float dx = MAX(piece->roi_in.width, piece->roi_out.width);
322 const float dy = MAX(piece->roi_in.height, piece->roi_out.height);
323 const float border = tiling->overlap + 1;
324 const gboolean possible = (cl_px > dx * border) || (cl_px > dy * border) || (cl_px > border * border);
325 if(!possible)
326 {
328 "[dt_dev_pixelpipe_process_rec] CL: tiling impossible in module `%s'. avail=%.1fM, requ=%.1fM (%ix%i). overlap=%i\n",
329 module->name(), cl_px / 1e6f, dx * dy / 1e6f, (int)dx, (int)dy, (int)tiling->overlap);
330 goto error;
331 }
332
333 // Ensure the input image is present on RAM cache,
334 // tiling on OpenCL will only copy tiles from it to GPU.
335 if(_gpu_init_input(pipe, &input, &cl_mem_input, piece, tiling,
336 input_entry, output_entry))
337 goto error;
338 }
339
340 if(!possible_cl) goto error;
341
342 if(fits_on_device)
343 {
344 // Alloc input GPU buffer if we didn't already borrow it
345 const gint64 inprep_t0 = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
346 if(!(piece->module->flags() & IOP_FLAGS_TAKE_NO_INPUT))
347 if(dt_dev_pixelpipe_cache_prepare_cl_input(pipe, module, input, &cl_mem_input,
348 &piece->roi_in, piece->dsc_in.bpp, input_entry,
349 &locked_input_entry, NULL))
350 goto error;
352 gpu_in_prepare_ms = (g_get_monotonic_time() - inprep_t0) / 1000.0;
353
354 cl_mem_process_input = cl_mem_input;
355
356 /* The output DEVICE buffer. Suspected of being where display encoding's prologue goes:
357 * its output is 4 bpp, the only such size in a 16 bpp pipe, so it can never be served by
358 * a device buffer another module just released -- unlike colorout, whose prologue is a
359 * twentieth of it. Measured rather than assumed, because the two obvious candidates
360 * before it (the input borrow, the host output allocation) both came back at 0.00. */
361 const gint64 outcl_t0 = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
362 // Alloc output GPU buffer - non-optional
363 cl_mem_output = dt_dev_pixelpipe_cache_get_cl_buffer(pipe->devid, output, &piece->roi_out, piece->dsc_out.bpp, module,
364 "output", output_entry,
365 &gpu_out_cl_reused, cl_mem_input);
367 gpu_out_cl_ms = (g_get_monotonic_time() - outcl_t0) / 1000.0;
368 if(IS_NULL_PTR(cl_mem_output)) goto error;
369
370 const int cst_before_cl = process_input_dsc.cst;
371 const gint64 cst_t0 = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
372 if(process_input_dsc.cst != piece->dsc_in.cst
373 && !(dt_iop_colorspace_is_rgb(process_input_dsc.cst) && dt_iop_colorspace_is_rgb(piece->dsc_in.cst)))
374 {
375 cl_mem_process_input_temp = dt_dev_pixelpipe_cache_alloc_cl_device_buffer(pipe->devid, &piece->roi_in, piece->dsc_in.bpp,
376 module, "module input colorspace temp",
377 cl_mem_input);
378 if(IS_NULL_PTR(cl_mem_process_input_temp))
379 goto error;
380
381 if(!dt_colorspaces_apply_profile_cl(module->op, module->multi_name, pipe->devid, cl_mem_input, cl_mem_process_input_temp,
382 piece->roi_in.width, piece->roi_in.height,
383 process_input_dsc.cst, piece->dsc_in.cst,
384 &process_input_dsc.cst, work_profile))
385 goto error;
386 cl_mem_process_input = cl_mem_process_input_temp;
387 }
388 else if(process_input_dsc.cst != piece->dsc_in.cst)
389 {
390 process_input_dsc.cst = piece->dsc_in.cst;
391 }
393 gpu_cst_ms = (g_get_monotonic_time() - cst_t0) / 1000.0;
394 const int cst_after_cl = process_input_dsc.cst;
395
396 dt_dev_pixelpipe_debug_dump_module_io(pipe, module, "pre", TRUE, &piece->dsc_in, &piece->dsc_out,
397 &piece->roi_in, &piece->roi_out,
398 process_input_dsc.bpp, piece->dsc_out.bpp,
399 cst_before_cl, cst_after_cl);
400
401 const gint64 gpu_prologue_end = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
402 if(!module->process_cl(module, pipe, piece, cl_mem_process_input, cl_mem_output))
403 goto error;
406 "[dev_pixelpipe] %s gpu prologue=%.2f ms"
407 " (inbuf=%.2f outalloc=%.2f inprep=%.2f outcl=%.2f cst=%.2f) process_cl=%.2f ms"
408 " (in %dx%d bpp=%" G_GSIZE_FORMAT " -> out bpp=%" G_GSIZE_FORMAT
409 " cache_out=%d outcl_reused=%d host=%p)\n",
410 module->op, (gpu_prologue_end - gpu_stage_t0) / 1000.0,
411 gpu_in_borrow_ms, gpu_out_alloc_ms, gpu_in_prepare_ms, gpu_out_cl_ms, gpu_cst_ms,
412 (g_get_monotonic_time() - gpu_prologue_end) / 1000.0,
413 piece->roi_in.width, piece->roi_in.height, process_input_dsc.bpp, piece->dsc_out.bpp,
414 *cache_output ? 1 : 0, gpu_out_cl_reused ? 1 : 0, output);
415
416 *pixelpipe_flow |= PIXELPIPE_FLOW_PROCESSED_ON_GPU;
418
419 /* Measured on a painting stroke: the pipeline reports `Drawing' at 47.6 ms a frame while
420 * the module's own process_cl measures 22.0 -- so ~25 ms is spent between process_cl
421 * returning and this module being declared done. Both blend early-outs should fire when
422 * nothing is blended (`transform_for_blend' returns NONE on DEVELOP_MASK_DISABLED, and
423 * `dt_develop_blend_process_cl' returns on !top_enabled), so the candidates are the
424 * colourspace temps around the blend, the blend kernel itself, or the CPU simply blocking
425 * here on kernels process_cl only ENQUEUED. Those have nothing in common as fixes. */
426 const gint64 blend_t0 = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
427 if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)
428 {
429 const dt_pixelpipe_blend_transform_t blend_transforms
431 cl_mem_blend_input = cl_mem_process_input;
432 cl_mem_blend_output = cl_mem_output;
433 blend_input_dsc = process_input_dsc;
434 blend_output_dsc = piece->dsc_out;
435 if(blend_transforms != DT_DEV_PIXELPIPE_BLEND_TRANSFORM_NONE)
436 {
438 int success = 1;
439 const int blend_in_before = blend_input_dsc.cst;
440 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_INPUT)
441 {
442 cl_mem_blend_input_temp = dt_dev_pixelpipe_cache_alloc_cl_device_buffer(pipe->devid, &piece->roi_in, piece->dsc_in.bpp,
443 module, "blend input colorspace temp",
444 cl_mem_process_input);
445 if(IS_NULL_PTR(cl_mem_blend_input_temp))
446 goto error;
447
448 success &= dt_colorspaces_apply_profile_cl(module->op, module->multi_name, pipe->devid,
449 cl_mem_process_input, cl_mem_blend_input_temp,
450 piece->roi_in.width, piece->roi_in.height,
451 blend_input_dsc.cst, blend_cst,
452 &blend_input_dsc.cst, work_profile);
453 cl_mem_blend_input = cl_mem_blend_input_temp;
454 }
455 const int blend_in_after = blend_input_dsc.cst;
457 &process_input_dsc, &blend_input_dsc,
458 &piece->roi_in, &piece->roi_in,
459 process_input_dsc.bpp, blend_input_dsc.bpp,
460 blend_in_before, blend_in_after);
461 const int blend_out_before = blend_output_dsc.cst;
462 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT)
463 {
464 cl_mem_blend_output_temp = dt_dev_pixelpipe_cache_alloc_cl_device_buffer(pipe->devid, &piece->roi_out,
465 piece->dsc_out.bpp, module,
466 "blend output colorspace temp", cl_mem_output);
467 if(IS_NULL_PTR(cl_mem_blend_output_temp))
468 goto error;
469
470 success &= dt_colorspaces_apply_profile_cl(module->op, module->multi_name, pipe->devid, cl_mem_output,
471 cl_mem_blend_output_temp, piece->roi_out.width,
472 piece->roi_out.height, blend_output_dsc.cst, blend_cst,
473 &blend_output_dsc.cst, work_profile);
474 cl_mem_blend_output = cl_mem_blend_output_temp;
475 }
476 const int blend_out_after = blend_output_dsc.cst;
477 dt_dev_pixelpipe_debug_dump_module_io(pipe, module, "blend-out", TRUE,
478 &piece->dsc_out, &blend_output_dsc,
479 &piece->roi_out, &piece->roi_out,
480 piece->dsc_out.bpp, blend_output_dsc.bpp,
481 blend_out_before, blend_out_after);
482
483 if(!success)
484 {
485 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] couldn't transform blending colorspace for module %s\n",
486 module->name());
487 goto error;
488 }
489 }
490
491 const gint64 blend_pre = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
492 if(dt_develop_blend_process_cl(module, pipe, piece, cl_mem_blend_input, cl_mem_blend_output))
493 goto error;
495 {
496 const dt_develop_blend_params_t *const bp = (const dt_develop_blend_params_t *)piece->blendop_data;
498 "[blend] %s transforms_in+out=%.2f ms kernel=%.2f ms mask_mode=%u transforms=%d\n",
499 module->op, (blend_pre - blend_t0) / 1000.0,
500 (g_get_monotonic_time() - blend_pre) / 1000.0,
501 bp ? bp->mask_mode : 0u, (int)blend_transforms);
502 }
503
504 // a mask or channel preview is converted like any output, see pixelpipe_cpu.c
505 if((blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT)
506 && !dt_colorspaces_apply_profile_cl(module->op, module->multi_name, pipe->devid, cl_mem_blend_output,
507 cl_mem_output, piece->roi_out.width,
508 piece->roi_out.height, blend_output_dsc.cst,
509 piece->dsc_out.cst, &blend_output_dsc.cst,
510 work_profile))
511 goto error;
512
513 *pixelpipe_flow |= PIXELPIPE_FLOW_BLENDED_ON_GPU;
514 *pixelpipe_flow &= ~(PIXELPIPE_FLOW_BLENDED_ON_CPU);
515 }
516
517 if(*cache_output)
518 {
519 const gint64 readback_t0 = (dt_get_debug_flags() & DT_DEBUG_PERF) ? g_get_monotonic_time() : 0;
520 if(dt_dev_pixelpipe_cache_sync_cl_buffer(pipe->devid, output, cl_mem_output, &piece->roi_out, CL_MAP_READ,
521 piece->dsc_out.bpp, module,
522 "output to cache"))
523 goto error;
524 /* This readback is inside the module's timed region, so it is charged to the module in
525 * the "processed `X'" line although the module did not ask for it: the seal sets
526 * cache_output_on_ram from a DOWNSTREAM consumer's need for host data. On a painting
527 * stroke it accounted for ~25 of `Drawing''s 47.6 ms a frame -- a 48.9 MB device->host
528 * copy of a 2144x1427 float4 buffer -- and the same flag separately disables the
529 * output cacheline's in-place rekey, which is what makes drawlayer's damage-limited
530 * composite gate report devout=0 and fall back to a full resample every frame. One flag,
531 * both costs, and neither visible without asking. */
533 dt_print(DT_DEBUG_PERF, "[dev_pixelpipe] %s output readback %dx%d bpp=%" G_GSIZE_FORMAT " took %.2f ms\n",
534 module->op, piece->roi_out.width, piece->roi_out.height, piece->dsc_out.bpp,
535 (g_get_monotonic_time() - readback_t0) / 1000.0);
536 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] output memory was copied to cache for %s\n", module->name());
537 }
538 }
539 else if(piece->process_tiling_ready && !IS_NULL_PTR(input))
540 {
541 // FIXME: we don't cover the case (piece->module->flags() & IOP_FLAGS_TAKE_NO_INPUT)
542 // in tiling path
543 const float *module_input = input;
544 const float *blend_input = input;
545 float *module_input_temp = NULL;
546 float *blend_input_temp = NULL;
547 gboolean input_locked = FALSE;
548
549 if(borrowed_cl_mem_input)
550 {
551 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, cl_mem_input);
552 cl_mem_input = NULL;
553 borrowed_cl_mem_input = FALSE;
554 }
555 else
556 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, input_entry, input,
558
559 if(process_input_dsc.cst != piece->dsc_in.cst
560 && !(dt_iop_colorspace_is_rgb(process_input_dsc.cst) && dt_iop_colorspace_is_rgb(piece->dsc_in.cst)))
561 {
562 module_input_temp
563 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_in.width * piece->roi_in.height * 4, pipe);
564 if(IS_NULL_PTR(module_input_temp))
565 goto error;
566
568 input_locked = TRUE;
569 dt_colorspaces_apply_profile(module->op, module->multi_name, input, module_input_temp, piece->roi_in.width,
570 piece->roi_in.height, process_input_dsc.cst, piece->dsc_in.cst,
571 &process_input_dsc.cst, work_profile);
573 input_locked = FALSE;
574 module_input = module_input_temp;
575 }
576 else if(process_input_dsc.cst != piece->dsc_in.cst)
577 {
578 process_input_dsc.cst = piece->dsc_in.cst;
580 input_locked = TRUE;
581 }
582 else
583 {
585 input_locked = TRUE;
586 }
587
588 int fail = !module->process_tiling_cl(module, pipe, piece, module_input, output, piece->dsc_in.bpp);
590
591 if(fail)
592 {
593 if(input_locked)
595 dt_pixelpipe_cache_free_align(module_input_temp);
596 goto error;
597 }
598
600 *pixelpipe_flow &= ~(PIXELPIPE_FLOW_PROCESSED_ON_CPU);
601
602 blend_input = module_input;
603 blend_input_dsc = process_input_dsc;
604 void *blend_output = output;
605 blend_output_dsc = piece->dsc_out;
606
607 const dt_pixelpipe_blend_transform_t blend_transforms
609 if(blend_transforms != DT_DEV_PIXELPIPE_BLEND_TRANSFORM_NONE)
610 {
612
613 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_INPUT)
614 {
615 blend_input_temp
616 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_in.width * piece->roi_in.height * 4, pipe);
617 if(IS_NULL_PTR(blend_input_temp))
618 {
619 if(input_locked)
621 dt_pixelpipe_cache_free_align(module_input_temp);
622 goto error;
623 }
624
625 dt_colorspaces_apply_profile(module->op, module->multi_name, module_input, blend_input_temp, piece->roi_in.width,
626 piece->roi_in.height, blend_input_dsc.cst, blend_cst,
627 &blend_input_dsc.cst, work_profile);
628 blend_input = blend_input_temp;
629 if(input_locked)
630 {
632 input_locked = FALSE;
633 }
634 }
635
636 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT)
637 {
638 float *blend_output_temp
639 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_out.width * piece->roi_out.height * 4, pipe);
640 if(IS_NULL_PTR(blend_output_temp))
641 {
642 if(input_locked)
644 dt_pixelpipe_cache_free_align(blend_input_temp);
645 dt_pixelpipe_cache_free_align(module_input_temp);
646 goto error;
647 }
648
649 dt_colorspaces_apply_profile(module->op, module->multi_name, output, blend_output_temp, piece->roi_out.width,
650 piece->roi_out.height, blend_output_dsc.cst, blend_cst,
651 &blend_output_dsc.cst, work_profile);
652 blend_output = blend_output_temp;
653 }
654 }
655
656 dt_develop_blend_process(module, pipe, piece, blend_input, blend_output);
657 *pixelpipe_flow |= PIXELPIPE_FLOW_BLENDED_ON_CPU;
658 *pixelpipe_flow &= ~(PIXELPIPE_FLOW_BLENDED_ON_GPU);
659
660 if((blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT))
661 {
662 // A mask or channel preview is converted like any output: the preview is authored in the
663 // blending space precisely so this conversion lands it where the display expects it.
664 dt_colorspaces_apply_profile(module->op, module->multi_name, blend_output, output, piece->roi_out.width,
665 piece->roi_out.height, blend_output_dsc.cst, piece->dsc_out.cst,
666 &blend_output_dsc.cst, work_profile);
667 }
668
669 if(input_locked)
671 if(blend_output != output)
672 dt_pixelpipe_cache_free_align(blend_output);
673 dt_pixelpipe_cache_free_align(blend_input_temp);
674 dt_pixelpipe_cache_free_align(module_input_temp);
675 }
676 else
677 {
678 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] could not run module '%s' on gpu. falling back to cpu path\n",
679 module->name());
680 goto error;
681 }
682
684
685 if(locked_input_entry)
686 dt_dev_pixelpipe_cache_rdlock_entry(FALSE, locked_input_entry);
687
688 /* Borrowed vRAM inputs must stay protected until the current queue completed, otherwise
689 * another pipe can flush or recycle the shared device buffer while the queued kernels
690 * are still reading it. */
691 if(borrowed_cl_mem_input)
692 {
693 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, cl_mem_input);
694 cl_mem_input = NULL;
695 borrowed_cl_mem_input = FALSE;
696 }
697 else
698 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, input_entry, input,
700
701 /* The backend now owns the authoritative module output payload until publish time.
702 * When the output stayed GPU-only, the recursion no longer carries `cl_mem_output`
703 * back explicitly, so we must cache it here before returning. Otherwise
704 * the caller publishes a cacheline with metadata only and no recoverable payload. */
705 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_output, output_entry, output, TRUE);
706
707 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_blend_output_temp, NULL, NULL, FALSE);
708 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_blend_input_temp, NULL, NULL, FALSE);
709 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_process_input_temp, NULL, NULL, FALSE);
710
711 return 0;
712
713error:
714 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] %s couldn't process on GPU\n", module->name());
715
717
718 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_blend_output_temp, NULL, NULL, FALSE);
719 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_blend_input_temp, NULL, NULL, FALSE);
720 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_process_input_temp, NULL, NULL, FALSE);
721
722 if(locked_input_entry)
723 dt_dev_pixelpipe_cache_rdlock_entry(FALSE, locked_input_entry);
724
725 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_output, output_entry, NULL, FALSE);
726
727 // The pipe abandoned this run while the module was on the device: a module that checks the
728 // killswitch mid-process (doc/module-abandon.md) fails for it. Rerunning it on CPU would redo work
729 // nobody wants and tell the user OpenCL failed, so abort as the killswitch does.
731 {
732 if(borrowed_cl_mem_input)
733 {
734 dt_dev_pixelpipe_cache_return_cl_payload(cpu_input_entry, cl_mem_input);
735 cl_mem_input = NULL;
736 }
737 else
738 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, cpu_input_entry, input,
740 return 1;
741 }
742
743 if(module->flags() & IOP_FLAGS_TAKE_NO_INPUT)
744 {
745 /* Root modules build their own input from external storage. If the OpenCL pre-check
746 * rejects the required allocation, CPU fallback must keep the same no-input contract
747 * instead of looking for an upstream cacheline that cannot exist. */
748
749 /* The `error:` label is the catch-all for *every* GPU failure (kernel errors, driver
750 * allocation faults, colorspace/blend failures...), not only the memory pre-check.
751 * Only quote a memory limit when the pre-check actually rejected the buffer, and quote
752 * the limit that was really exceeded -- otherwise the numbers contradict the failure
753 * (e.g. "needs 100 MiB but device limit is 1991 MiB", issue #878). */
754 switch(fit_reason)
755 {
757 dt_pipeline_message(_("OpenCL failed for module `%s`: image buffer needs %" G_GSIZE_FORMAT
758 " MiB but the largest allocation the device allows is %" G_GSIZE_FORMAT
759 " MiB; falling back to CPU"),
760 module->name(), (size_t)(fit_needed / (1024 * 1024)),
761 (size_t)(fit_limit / (1024 * 1024)));
762 break;
764 dt_pipeline_message(_("OpenCL failed for module `%s`: image buffer needs %" G_GSIZE_FORMAT
765 " MiB but only %" G_GSIZE_FORMAT " MiB are free on the device; falling back to CPU"),
766 module->name(), (size_t)(fit_needed / (1024 * 1024)),
767 (size_t)(fit_limit / (1024 * 1024)));
768 break;
770 dt_pipeline_message(_("OpenCL failed for module `%s`: image dimensions %" G_GSIZE_FORMAT "x%" G_GSIZE_FORMAT
771 " exceed the device limits; falling back to CPU"),
772 module->name(), precheck_width, precheck_height);
773 break;
774 default: // DT_OPENCL_FIT_OK / UNINITED: the buffer fit, the GPU failed for another reason
775 dt_pipeline_message(_("OpenCL failed for module `%s`; falling back to CPU"), module->name());
776 break;
777 }
778 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
779 cache_output, cpu_input_entry, output_entry);
780 }
781
782 if(!IS_NULL_PTR(input))
783 {
785 "[dev_pixelpipe] %s GPU error fallback will reuse host input\n",
786 module->name());
787 }
788 else if(!IS_NULL_PTR(cl_mem_input))
789 {
790 if(_gpu_init_input(pipe, &input, &cl_mem_input, piece, tiling,
791 cpu_input_entry, output_entry))
792 {
793 if(borrowed_cl_mem_input)
794 {
795 dt_dev_pixelpipe_cache_return_cl_payload(cpu_input_entry, cl_mem_input);
796 cl_mem_input = NULL;
797 borrowed_cl_mem_input = FALSE;
798 }
799 else
800 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, cpu_input_entry, NULL,
802 return 1;
803 }
804 }
805 else if(IS_NULL_PTR(input))
806 {
808 "[dev_pixelpipe] %s CPU fallback has no input buffer (cache allocation failed?)\n",
809 module->name());
810 return 1;
811 }
812
813 if(borrowed_cl_mem_input)
814 {
815 dt_dev_pixelpipe_cache_return_cl_payload(cpu_input_entry, cl_mem_input);
816 cl_mem_input = NULL;
817 }
818 else
819 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, cpu_input_entry, input,
821
822 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
823 cache_output, cpu_input_entry, output_entry);
824}
825
826#else
827
829 const dt_dev_pixelpipe_iop_t *previous_piece,
831 dt_pixelpipe_flow_t *pixelpipe_flow,
832 gboolean *const cache_output,
833 dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
834{
835 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
836 cache_output, input_entry, output_entry);
837}
838
839#endif
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int dt_develop_blend_process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition blend.c:1174
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
int dt_develop_blend_process(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition blend.c:719
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
dt_iop_colorspace_type_t
@ IOP_CS_RAW
int dt_colorspaces_apply_profile_cl(const char *const op_name, const char *const instance_name, const int devid, cl_mem dev_img_in, cl_mem dev_img_out, const int width, const int height, const int cst_from, const int cst_to, int *converted_cst, const dt_iop_order_iccprofile_info_t *const profile_info)
OpenCL counterpart of dt_colorspaces_apply_profile(), same contract.
void dt_colorspaces_apply_profile(const char *const op_name, const char *const instance_name, const float *const image_in, float *const image_out, const int width, const int height, const int cst_from, const int cst_to, int *converted_cst, const dt_iop_order_iccprofile_info_t *const profile_info)
Convert a 4-channel float buffer between RGB and Lab through one profile.
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition format.h:75
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_TAKE_NO_INPUT
Definition imageop.h:195
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
@ DT_DEBUG_OPENCL
Definition logging.h:57
@ DT_DEBUG_PERF
Definition logging.h:55
@ DT_DEBUG_TILING
Definition logging.h:74
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.
#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
cl_ulong dt_opencl_get_device_available(const int devid)
Definition opencl.c:3203
dt_opencl_fit_reason_t dt_opencl_image_fits_device_reason(const int devid, const size_t width, const size_t height, const unsigned bpp, const float factor, const size_t overhead, size_t *needed, size_t *limit)
Definition opencl.c:3259
int dt_opencl_is_inited(void)
Definition opencl.c:3328
gboolean dt_opencl_finish(const int devid)
Definition opencl.c:1712
dt_opencl_fit_reason_t
Definition opencl.h:169
@ DT_OPENCL_FIT_ALLOC_LIMIT
Definition opencl.h:172
@ DT_OPENCL_FIT_AVAILABLE
Definition opencl.h:173
@ DT_OPENCL_FIT_DIMENSION
Definition opencl.h:171
@ DT_OPENCL_FIT_OK
Definition opencl.h:170
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
void dt_pipeline_message(const char *format,...)
Everything the pixel pipeline says to whoever is watching: messages for the user, and the busy banner...
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:44
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.
void * dt_dev_pixelpipe_cache_borrow_cl_payload(dt_pixel_cache_entry_t *entry, int devid, int width, int height, int bpp)
Borrow a cached OpenCL payload attached to a cache entry.
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_flush_clmem(const int devid)
Release cached OpenCL buffers for a single device.
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_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_get_cl_buffer(int devid, void *const host_ptr, const dt_iop_roi_t *roi, const size_t bpp, dt_iop_module_t *module, const char *message, dt_pixel_cache_entry_t *cache_entry, gboolean *out_reused, void *keep)
gboolean dt_dev_pixelpipe_cache_flush_host_pinned_image(void *host_ptr, dt_pixel_cache_entry_t *entry_hint, int devid)
Drop cached pinned OpenCL images associated with a given host buffer.
void * dt_pixel_cache_alloc(dt_pixel_cache_entry_t *cache_entry)
Actually allocate the memory buffer attached to the cache entry once you create it with dt_dev_pixelp...
void * dt_dev_pixelpipe_cache_alloc_cl_device_buffer(int devid, const dt_iop_roi_t *roi, const size_t bpp, const dt_iop_module_t *module, const char *message, void *keep)
void * dt_pixel_cache_entry_get_buffer(dt_pixel_cache_entry_t *entry)
The host buffer of a cache entry, whatever hash its content describes.
int dt_dev_pixelpipe_cache_prepare_cl_input(dt_dev_pixelpipe_t *pipe, dt_iop_module_t *module, float *input, void **cl_mem_input, const dt_iop_roi_t *roi_in, const size_t in_bpp, dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t **locked_input_entry, void *keep)
Prepare/obtain the OpenCL input image for a module.
int dt_dev_pixelpipe_cache_sync_cl_buffer(const int devid, void *host_ptr, void *cl_mem_buffer, const dt_iop_roi_t *roi, int cl_mode, size_t bpp, dt_iop_module_t *module, const char *message)
Synchronize between host memory and a pinned OpenCL image.
void dt_dev_pixelpipe_cache_return_cl_payload(dt_pixel_cache_entry_t *entry, void *mem)
Return a borrowed cached OpenCL payload to its cache 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.
float * dt_dev_pixelpipe_cache_restore_cl_buffer(dt_dev_pixelpipe_t *pipe, float *input, void *cl_mem_input, const dt_iop_roi_t *roi_in, dt_iop_module_t *module, const size_t in_bpp, dt_pixel_cache_entry_t *input_entry, const char *message)
Force device → host resynchronization of the pixelpipe input cache line.
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define dt_pixelpipe_cache_free_align(mem)
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
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)
static int _is_opencl_supported(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, dt_iop_module_t *module)
static int _gpu_early_cpu_fallback_if_unsupported(dt_dev_pixelpipe_t *pipe, float **input, void **cl_mem_input, gboolean *const borrowed_cl_mem_input, 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)
static int _gpu_init_input(dt_dev_pixelpipe_t *pipe, float **input, void **cl_mem_input, const dt_dev_pixelpipe_iop_t *piece, dt_develop_tiling_t *tiling, dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
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)
gboolean dt_dev_pixelpipe_cache_gpu_device_buffer(const dt_dev_pixelpipe_t *pipe, const dt_pixel_cache_entry_t *cache_entry)
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)
static gboolean dt_dev_pixelpipe_has_shutdown(const dt_dev_pixelpipe_t *pipe)
dt_pixelpipe_flow_t
@ PIXELPIPE_FLOW_PROCESSED_ON_CPU
@ PIXELPIPE_FLOW_PROCESSED_WITH_TILING
@ PIXELPIPE_FLOW_PROCESSED_ON_GPU
@ PIXELPIPE_FLOW_BLENDED_ON_CPU
@ PIXELPIPE_FLOW_BLENDED_ON_GPU
dt_pixelpipe_blend_transform_t
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_INPUT
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_NONE
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT
dt_iop_buffer_dsc_t dsc_out
dt_atomic_int realtime
dt_dev_pixelpipe_type_t type
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:225
dt_iop_buffer_dsc_t dsc
Definition image.h:428
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
#define MAX(a, b)
Definition thinplate.c:29