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
5#include "common/darktable.h"
6#include "common/iop_order.h"
7#include "common/opencl.h"
8#include "develop/blend.h"
12
13#include <math.h>
14#include <stdio.h>
15
17 dt_pixel_cache_entry_t *cache_entry, const char *reason)
18{
19#ifdef HAVE_OPENCL
20 if(pipe && !pipe->realtime && pipe->devid >= 0 && host_ptr && cache_entry)
21 {
22 /* Non-realtime host writes invalidate reusable pinned images bound to the previous ROI/hash.
23 * Realtime keeps its pinned reuse untouched to avoid stalling the live draw path. */
25 pipe->devid))
26 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] flushed pinned OpenCL images after %s\n",
27 reason ? reason : "host write");
28 }
29#else
30 (void)pipe;
31 (void)host_ptr;
32 (void)cache_entry;
33 (void)reason;
34#endif
35}
36
37#ifdef HAVE_OPENCL
38
40{
41 return dt_opencl_is_inited() && piece->process_cl_ready && module->process_cl;
42}
43
45 float **input, void **cl_mem_input,
47 dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
48{
49 dt_iop_module_t *module = piece->module;
50
51 if(IS_NULL_PTR(*input))
52 {
54 *input = dt_pixel_cache_alloc(darktable.pixelpipe_cache, input_entry);
56 }
57
58 if(IS_NULL_PTR(*input))
59 {
61 "[dev_pixelpipe] %s CPU fallback has no input buffer (cache allocation failed?)\n",
62 module->name());
63 return 1;
64 }
65
67 const int fail = dt_dev_pixelpipe_cache_sync_cl_buffer(pipe->devid, *input, *cl_mem_input, &piece->roi_in, CL_MAP_READ,
68 piece->dsc_in.bpp, module,
69 "cpu fallback input copy to cache");
71
72 if(fail)
73 {
75 "[dev_pixelpipe] %s couldn't resync GPU input to cache for CPU fallback\n",
76 module->name());
77 return 1;
78 }
79 return 0;
80}
81
83 void **cl_mem_input,
84 gboolean *const borrowed_cl_mem_input,
85 const dt_dev_pixelpipe_iop_t *piece,
86 const dt_dev_pixelpipe_iop_t *previous_piece,
88 dt_pixelpipe_flow_t *pixelpipe_flow,
89 gboolean *const cache_output,
90 dt_pixel_cache_entry_t *input_entry,
91 dt_pixel_cache_entry_t *output_entry)
92{
93 dt_iop_module_t *module = piece->module;
94
95 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] %s will run directly on CPU\n", module->name());
96
102 if(module->flags() & IOP_FLAGS_TAKE_NO_INPUT)
103 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
104 cache_output, input_entry, output_entry);
105
106 /* CPU fallback only needs a valid host buffer. If `input` already exists here, the upstream
107 * hand-off has already materialized authoritative RAM and re-reading the same pixels back out
108 * of the cached OpenCL image is redundant. */
109 if(input && !IS_NULL_PTR(*input))
110 {
112 "[dev_pixelpipe] %s CPU fallback will reuse host input\n",
113 module->name());
114 }
115 else if(cl_mem_input && !IS_NULL_PTR(*cl_mem_input))
116 {
117 if(input && IS_NULL_PTR(*input))
118 {
120 *input = dt_pixel_cache_alloc(darktable.pixelpipe_cache, input_entry);
122 }
123
124 if(IS_NULL_PTR(input) || IS_NULL_PTR(*input))
125 {
127 "[dev_pixelpipe] %s CPU fallback has no input buffer (cache allocation failed?)\n",
128 module->name());
129 if(borrowed_cl_mem_input && *borrowed_cl_mem_input)
130 {
131 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, *cl_mem_input);
132 *cl_mem_input = NULL;
133 *borrowed_cl_mem_input = FALSE;
134 }
135 else
136 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_input, input_entry, NULL,
138 return 1;
139 }
140
141 *input = dt_dev_pixelpipe_cache_restore_cl_buffer(pipe, *input, *cl_mem_input, &piece->roi_in, module,
142 piece->dsc_in.bpp, input_entry,
143 "cpu fallback input copy to cache");
144 if(IS_NULL_PTR(*input))
145 {
147 "[dev_pixelpipe] %s couldn't resync GPU input to cache for CPU fallback\n",
148 module->name());
149 if(borrowed_cl_mem_input && *borrowed_cl_mem_input)
150 {
151 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, *cl_mem_input);
152 *cl_mem_input = NULL;
153 *borrowed_cl_mem_input = FALSE;
154 }
155 else
156 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_input, input_entry, NULL,
158 return 1;
159 }
160 }
161 else if(!input || IS_NULL_PTR(*input))
162 {
164 "[dev_pixelpipe] %s CPU fallback has no input buffer (cache allocation failed?)\n",
165 module->name());
166 return 1;
167 }
168
169 if(borrowed_cl_mem_input && *borrowed_cl_mem_input)
170 {
171 /* Device-only inputs borrowed from the cache stay owned by the cache entry.
172 * CPU fallback only needs to drop the temporary borrow after the device->host
173 * sync, otherwise releasing the cl_mem here leaves a stale cache-side pointer
174 * that later thumbnail runs may reopen as corrupted input. */
175 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, *cl_mem_input);
176 *cl_mem_input = NULL;
177 *borrowed_cl_mem_input = FALSE;
178 }
179 else
180 dt_dev_pixelpipe_cache_release_cl_buffer(cl_mem_input, input_entry, *input,
182
183 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
184 cache_output, input_entry, output_entry);
185}
186
188 const dt_dev_pixelpipe_iop_t *previous_piece,
190 dt_pixelpipe_flow_t *pixelpipe_flow,
191 gboolean *const cache_output,
192 dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
193{
194 dt_iop_module_t *module = piece->module;
195 float *input = input_entry ? dt_pixel_cache_entry_get_data(input_entry) : NULL;
196 void *output = dt_pixel_cache_entry_get_data(output_entry);
197 void *cl_mem_input = NULL;
198 void *cl_mem_output = NULL;
199 void *cl_mem_process_input = NULL;
200 void *cl_mem_blend_input = NULL;
201 void *cl_mem_blend_output = NULL;
202 void *cl_mem_process_input_temp = NULL;
203 void *cl_mem_blend_input_temp = NULL;
204 void *cl_mem_blend_output_temp = NULL;
205 dt_pixel_cache_entry_t *cpu_input_entry = input_entry;
206 dt_pixel_cache_entry_t *locked_input_entry = NULL;
207 gboolean borrowed_cl_mem_input = FALSE;
208 const dt_iop_buffer_dsc_t actual_input_dsc = previous_piece ? previous_piece->dsc_out : pipe->dev->image_storage.dsc;
209 dt_iop_buffer_dsc_t process_input_dsc = actual_input_dsc;
210 dt_iop_buffer_dsc_t blend_input_dsc = actual_input_dsc;
211 dt_iop_buffer_dsc_t blend_output_dsc = piece->dsc_out;
212
213 // Try to reuse the cached vRAM buffer for the input entry if available
214 // except for basebuffer module which takes no input
215 if(!(piece->module->flags() & IOP_FLAGS_TAKE_NO_INPUT))
216 {
217 cl_mem_input = dt_dev_pixelpipe_cache_borrow_cl_payload(input_entry, pipe->devid,
218 piece->roi_in.width, piece->roi_in.height,
219 actual_input_dsc.bpp);
220 borrowed_cl_mem_input = (!IS_NULL_PTR(cl_mem_input));
221 if(IS_NULL_PTR(cl_mem_input))
222 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] %s could not get a cached vRAM input buffer.\n", module->name());
223
224 // Note: if that fails, we will attempt resync from RAM cache later
225
226 if(IS_NULL_PTR(input) && IS_NULL_PTR(cl_mem_input))
227 {
228 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] %s has no RAM nor vRAM input... aborting.\n", module->name());
229 return 1;
230 }
231 }
232
233 if(!_is_opencl_supported(pipe, piece, module) || !pipe->opencl_enabled || !(pipe->devid >= 0))
234 {
235 return _gpu_early_cpu_fallback_if_unsupported(pipe, &input, &cl_mem_input,
236 &borrowed_cl_mem_input, piece, previous_piece, tiling,
237 pixelpipe_flow, cache_output,
238 input_entry, output_entry);
239 }
240
241 const dt_iop_order_iccprofile_info_t *const work_profile
242 = (process_input_dsc.cst != IOP_CS_RAW || piece->dsc_in.cst != IOP_CS_RAW)
244 : NULL;
245
246 const float required_factor_cl
247 = fmaxf(1.0f, (!IS_NULL_PTR(cl_mem_input)) ? tiling->factor_cl - 1.0f : tiling->factor_cl);
248
249 const size_t precheck_width = ROUNDUPDWD(MAX(piece->roi_in.width, piece->roi_out.width), pipe->devid);
250 const size_t precheck_height = ROUNDUPDHT(MAX(piece->roi_in.height, piece->roi_out.height), pipe->devid);
251 // Remember *why* the pre-check (dis)allowed OpenCL so the CPU-fallback message on the
252 // error path can quote the limit that was actually exceeded instead of a fixed one.
253 size_t fit_needed = 0, fit_limit = 0;
254 dt_opencl_fit_reason_t fit_reason
255 = dt_opencl_image_fits_device_reason(pipe->devid, precheck_width, precheck_height,
256 MAX(piece->dsc_in.bpp, piece->dsc_out.bpp),
257 required_factor_cl, tiling->overhead, &fit_needed, &fit_limit);
258 gboolean fits_on_device = (fit_reason == DT_OPENCL_FIT_OK);
259 if(!fits_on_device)
260 {
262 "[dev_pixelpipe] %s pre-check didn't fit on device, flushing cached pinned buffers and retrying\n",
263 module->name());
265 fit_reason = dt_opencl_image_fits_device_reason(pipe->devid, precheck_width, precheck_height,
266 MAX(piece->dsc_in.bpp, piece->dsc_out.bpp),
267 required_factor_cl, tiling->overhead, &fit_needed, &fit_limit);
268 fits_on_device = (fit_reason == DT_OPENCL_FIT_OK);
269 }
270
271 gboolean possible_cl = !(pipe->type == DT_DEV_PIXELPIPE_PREVIEW
272 && (module->flags() & IOP_FLAGS_PREVIEW_NON_OPENCL))
273 && (fits_on_device || piece->process_tiling_ready);
274
275 if(!possible_cl || !fits_on_device) *cache_output = TRUE;
276 if(*cache_output && IS_NULL_PTR(output))
277 {
278 output = dt_pixel_cache_alloc(darktable.pixelpipe_cache, output_entry);
279 if(IS_NULL_PTR(output)) goto error;
280 }
281
282 if(possible_cl && !fits_on_device)
283 {
284 // Prepare the input buffer for tiling
285 const float cl_px = dt_opencl_get_device_available(pipe->devid)
286 / (sizeof(float) * MAX(piece->dsc_in.bpp, piece->dsc_out.bpp)
287 * ceilf(required_factor_cl));
288 const float dx = MAX(piece->roi_in.width, piece->roi_out.width);
289 const float dy = MAX(piece->roi_in.height, piece->roi_out.height);
290 const float border = tiling->overlap + 1;
291 const gboolean possible = (cl_px > dx * border) || (cl_px > dy * border) || (cl_px > border * border);
292 if(!possible)
293 {
295 "[dt_dev_pixelpipe_process_rec] CL: tiling impossible in module `%s'. avail=%.1fM, requ=%.1fM (%ix%i). overlap=%i\n",
296 module->name(), cl_px / 1e6f, dx * dy / 1e6f, (int)dx, (int)dy, (int)tiling->overlap);
297 goto error;
298 }
299
300 // Ensure the input image is present on RAM cache,
301 // tiling on OpenCL will only copy tiles from it to GPU.
302 if(_gpu_init_input(pipe, &input, &cl_mem_input, piece, tiling,
303 input_entry, output_entry))
304 goto error;
305 }
306
307 if(!possible_cl) goto error;
308
309 if(fits_on_device)
310 {
311 // Alloc input GPU buffer if we didn't already borrow it
312 if(!(piece->module->flags() & IOP_FLAGS_TAKE_NO_INPUT))
313 if(dt_dev_pixelpipe_cache_prepare_cl_input(pipe, module, input, &cl_mem_input,
314 &piece->roi_in, piece->dsc_in.bpp, input_entry,
315 &locked_input_entry, NULL))
316 goto error;
317
318 cl_mem_process_input = cl_mem_input;
319
320 // Alloc output GPU buffer - non-optional
321 cl_mem_output = dt_dev_pixelpipe_cache_get_cl_buffer(pipe->devid, output, &piece->roi_out, piece->dsc_out.bpp, module,
322 "output", output_entry,
323 NULL, cl_mem_input);
324 if(IS_NULL_PTR(cl_mem_output)) goto error;
325
326 const int cst_before_cl = process_input_dsc.cst;
327 if(process_input_dsc.cst != piece->dsc_in.cst
328 && !(dt_iop_colorspace_is_rgb(process_input_dsc.cst) && dt_iop_colorspace_is_rgb(piece->dsc_in.cst)))
329 {
330 cl_mem_process_input_temp = dt_dev_pixelpipe_cache_alloc_cl_device_buffer(pipe->devid, &piece->roi_in, piece->dsc_in.bpp,
331 module, "module input colorspace temp",
332 cl_mem_input);
333 if(IS_NULL_PTR(cl_mem_process_input_temp))
334 goto error;
335
336 if(!dt_ioppr_transform_image_colorspace_cl(module, pipe->devid, cl_mem_input, cl_mem_process_input_temp,
337 piece->roi_in.width, piece->roi_in.height,
338 process_input_dsc.cst, piece->dsc_in.cst,
339 &process_input_dsc.cst, work_profile))
340 goto error;
341 cl_mem_process_input = cl_mem_process_input_temp;
342 }
343 else if(process_input_dsc.cst != piece->dsc_in.cst)
344 {
345 process_input_dsc.cst = piece->dsc_in.cst;
346 }
347 const int cst_after_cl = process_input_dsc.cst;
348
349 dt_dev_pixelpipe_debug_dump_module_io(pipe, module, "pre", TRUE, &piece->dsc_in, &piece->dsc_out,
350 &piece->roi_in, &piece->roi_out,
351 process_input_dsc.bpp, piece->dsc_out.bpp,
352 cst_before_cl, cst_after_cl);
353
354 if(!module->process_cl(module, pipe, piece, cl_mem_process_input, cl_mem_output))
355 goto error;
356
357 *pixelpipe_flow |= PIXELPIPE_FLOW_PROCESSED_ON_GPU;
359
360 if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)
361 {
362 const dt_dev_pixelpipe_display_mask_t request_mask_display
363 = (module->dev->gui_attached && (module == module->dev->gui_module) && (pipe == module->dev->pipe))
364 ? module->request_mask_display
365 : DT_DEV_PIXELPIPE_DISPLAY_NONE;
366 const dt_pixelpipe_blend_transform_t blend_transforms
367 = dt_dev_pixelpipe_transform_for_blend(module, piece, &piece->dsc_out);
368 cl_mem_blend_input = cl_mem_process_input;
369 cl_mem_blend_output = cl_mem_output;
370 blend_input_dsc = process_input_dsc;
371 blend_output_dsc = piece->dsc_out;
372 if(blend_transforms != DT_DEV_PIXELPIPE_BLEND_TRANSFORM_NONE)
373 {
375 int success = 1;
376 const int blend_in_before = blend_input_dsc.cst;
377 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_INPUT)
378 {
379 cl_mem_blend_input_temp = dt_dev_pixelpipe_cache_alloc_cl_device_buffer(pipe->devid, &piece->roi_in, piece->dsc_in.bpp,
380 module, "blend input colorspace temp",
381 cl_mem_process_input);
382 if(IS_NULL_PTR(cl_mem_blend_input_temp))
383 goto error;
384
385 success &= dt_ioppr_transform_image_colorspace_cl(module, pipe->devid,
386 cl_mem_process_input, cl_mem_blend_input_temp,
387 piece->roi_in.width, piece->roi_in.height,
388 blend_input_dsc.cst, blend_cst,
389 &blend_input_dsc.cst, work_profile);
390 cl_mem_blend_input = cl_mem_blend_input_temp;
391 }
392 const int blend_in_after = blend_input_dsc.cst;
393 dt_dev_pixelpipe_debug_dump_module_io(pipe, module, "blend-in", TRUE,
394 &process_input_dsc, &blend_input_dsc,
395 &piece->roi_in, &piece->roi_in,
396 process_input_dsc.bpp, blend_input_dsc.bpp,
397 blend_in_before, blend_in_after);
398 const int blend_out_before = blend_output_dsc.cst;
399 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT)
400 {
401 cl_mem_blend_output_temp = dt_dev_pixelpipe_cache_alloc_cl_device_buffer(pipe->devid, &piece->roi_out,
402 piece->dsc_out.bpp, module,
403 "blend output colorspace temp", cl_mem_output);
404 if(IS_NULL_PTR(cl_mem_blend_output_temp))
405 goto error;
406
407 success &= dt_ioppr_transform_image_colorspace_cl(module, pipe->devid, cl_mem_output,
408 cl_mem_blend_output_temp, piece->roi_out.width,
409 piece->roi_out.height, blend_output_dsc.cst, blend_cst,
410 &blend_output_dsc.cst, work_profile);
411 cl_mem_blend_output = cl_mem_blend_output_temp;
412 }
413 const int blend_out_after = blend_output_dsc.cst;
414 dt_dev_pixelpipe_debug_dump_module_io(pipe, module, "blend-out", TRUE,
415 &piece->dsc_out, &blend_output_dsc,
416 &piece->roi_out, &piece->roi_out,
417 piece->dsc_out.bpp, blend_output_dsc.bpp,
418 blend_out_before, blend_out_after);
419
420 if(!success)
421 {
422 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] couldn't transform blending colorspace for module %s\n",
423 module->name());
424 goto error;
425 }
426 }
427
428 if(dt_develop_blend_process_cl(module, pipe, piece, cl_mem_blend_input, cl_mem_blend_output))
429 goto error;
430
431 if((blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT)
432 && request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY)
433 {
434 size_t origin[] = { 0, 0, 0 };
435 size_t region[] = { piece->roi_out.width, piece->roi_out.height, 1 };
436 if(dt_opencl_enqueue_copy_image(pipe->devid, cl_mem_blend_output, cl_mem_output, origin, origin,
437 region) != CL_SUCCESS)
438 goto error;
439 }
440 else if((blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT)
441 && !dt_ioppr_transform_image_colorspace_cl(module, pipe->devid, cl_mem_blend_output,
442 cl_mem_output, piece->roi_out.width,
443 piece->roi_out.height, blend_output_dsc.cst,
444 piece->dsc_out.cst, &blend_output_dsc.cst,
445 work_profile))
446 goto error;
447
448 *pixelpipe_flow |= PIXELPIPE_FLOW_BLENDED_ON_GPU;
449 *pixelpipe_flow &= ~(PIXELPIPE_FLOW_BLENDED_ON_CPU);
450 }
451
452 if(*cache_output)
453 {
454 if(dt_dev_pixelpipe_cache_sync_cl_buffer(pipe->devid, output, cl_mem_output, &piece->roi_out, CL_MAP_READ,
455 piece->dsc_out.bpp, module,
456 "output to cache"))
457 goto error;
458 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] output memory was copied to cache for %s\n", module->name());
459 }
460 }
461 else if(piece->process_tiling_ready && !IS_NULL_PTR(input))
462 {
463 // FIXME: we don't cover the case (piece->module->flags() & IOP_FLAGS_TAKE_NO_INPUT)
464 // in tiling path
465 const float *module_input = input;
466 const float *blend_input = input;
467 float *module_input_temp = NULL;
468 float *blend_input_temp = NULL;
469 gboolean input_locked = FALSE;
470
471 if(borrowed_cl_mem_input)
472 {
473 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, cl_mem_input);
474 cl_mem_input = NULL;
475 borrowed_cl_mem_input = FALSE;
476 }
477 else
478 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, input_entry, input,
480
481 if(process_input_dsc.cst != piece->dsc_in.cst
482 && !(dt_iop_colorspace_is_rgb(process_input_dsc.cst) && dt_iop_colorspace_is_rgb(piece->dsc_in.cst)))
483 {
484 module_input_temp
485 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_in.width * piece->roi_in.height * 4, pipe);
486 if(IS_NULL_PTR(module_input_temp))
487 goto error;
488
490 input_locked = TRUE;
491 dt_ioppr_transform_image_colorspace(module, input, module_input_temp, piece->roi_in.width,
492 piece->roi_in.height, process_input_dsc.cst, piece->dsc_in.cst,
493 &process_input_dsc.cst, work_profile);
495 input_locked = FALSE;
496 module_input = module_input_temp;
497 }
498 else if(process_input_dsc.cst != piece->dsc_in.cst)
499 {
500 process_input_dsc.cst = piece->dsc_in.cst;
502 input_locked = TRUE;
503 }
504 else
505 {
507 input_locked = TRUE;
508 }
509
510 int fail = !module->process_tiling_cl(module, pipe, piece, module_input, output, piece->dsc_in.bpp);
511 dt_opencl_finish(pipe->devid);
512
513 if(fail)
514 {
515 if(input_locked)
517 dt_pixelpipe_cache_free_align(module_input_temp);
518 goto error;
519 }
520
522 *pixelpipe_flow &= ~(PIXELPIPE_FLOW_PROCESSED_ON_CPU);
523
524 blend_input = module_input;
525 blend_input_dsc = process_input_dsc;
526 void *blend_output = output;
527 blend_output_dsc = piece->dsc_out;
528
529 const dt_dev_pixelpipe_display_mask_t request_mask_display
530 = (module->dev->gui_attached && (module == module->dev->gui_module) && (pipe == module->dev->pipe))
531 ? module->request_mask_display
532 : DT_DEV_PIXELPIPE_DISPLAY_NONE;
533 const dt_pixelpipe_blend_transform_t blend_transforms
534 = dt_dev_pixelpipe_transform_for_blend(module, piece, &piece->dsc_out);
535 if(blend_transforms != DT_DEV_PIXELPIPE_BLEND_TRANSFORM_NONE)
536 {
538
539 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_INPUT)
540 {
541 blend_input_temp
542 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_in.width * piece->roi_in.height * 4, pipe);
543 if(IS_NULL_PTR(blend_input_temp))
544 {
545 if(input_locked)
547 dt_pixelpipe_cache_free_align(module_input_temp);
548 goto error;
549 }
550
551 dt_ioppr_transform_image_colorspace(module, module_input, blend_input_temp, piece->roi_in.width,
552 piece->roi_in.height, blend_input_dsc.cst, blend_cst,
553 &blend_input_dsc.cst, work_profile);
554 blend_input = blend_input_temp;
555 if(input_locked)
556 {
558 input_locked = FALSE;
559 }
560 }
561
562 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT)
563 {
564 float *blend_output_temp
565 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_out.width * piece->roi_out.height * 4, pipe);
566 if(IS_NULL_PTR(blend_output_temp))
567 {
568 if(input_locked)
570 dt_pixelpipe_cache_free_align(blend_input_temp);
571 dt_pixelpipe_cache_free_align(module_input_temp);
572 goto error;
573 }
574
575 dt_ioppr_transform_image_colorspace(module, output, blend_output_temp, piece->roi_out.width,
576 piece->roi_out.height, blend_output_dsc.cst, blend_cst,
577 &blend_output_dsc.cst, work_profile);
578 blend_output = blend_output_temp;
579 }
580 }
581
582 dt_develop_blend_process(module, pipe, piece, blend_input, blend_output);
583 *pixelpipe_flow |= PIXELPIPE_FLOW_BLENDED_ON_CPU;
584 *pixelpipe_flow &= ~(PIXELPIPE_FLOW_BLENDED_ON_GPU);
585
586 if((blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT))
587 {
588 if(request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY)
589 {
590 memcpy(output, blend_output,
591 (size_t)piece->roi_out.width * piece->roi_out.height * piece->dsc_out.bpp);
592 }
593 else
594 {
595 dt_ioppr_transform_image_colorspace(module, blend_output, output, piece->roi_out.width,
596 piece->roi_out.height, blend_output_dsc.cst, piece->dsc_out.cst,
597 &blend_output_dsc.cst, work_profile);
598 }
599 }
600
601 if(input_locked)
603 if(blend_output != output)
604 dt_pixelpipe_cache_free_align(blend_output);
605 dt_pixelpipe_cache_free_align(blend_input_temp);
606 dt_pixelpipe_cache_free_align(module_input_temp);
607 }
608 else
609 {
610 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] could not run module '%s' on gpu. falling back to cpu path\n",
611 module->name());
612 goto error;
613 }
614
615 dt_opencl_finish(pipe->devid);
616
617 if(locked_input_entry)
619
620 /* Borrowed vRAM inputs must stay protected until the current queue completed, otherwise
621 * another pipe can flush or recycle the shared device buffer while the queued kernels
622 * are still reading it. */
623 if(borrowed_cl_mem_input)
624 {
625 dt_dev_pixelpipe_cache_return_cl_payload(input_entry, cl_mem_input);
626 cl_mem_input = NULL;
627 borrowed_cl_mem_input = FALSE;
628 }
629 else
630 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, input_entry, input,
632
633 /* The backend now owns the authoritative module output payload until publish time.
634 * When the output stayed GPU-only, the recursion no longer carries `cl_mem_output`
635 * back explicitly, so we must cache it here before returning. Otherwise
636 * the caller publishes a cacheline with metadata only and no recoverable payload. */
637 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_output, output_entry, output, TRUE);
638
639 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_blend_output_temp, NULL, NULL, FALSE);
640 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_blend_input_temp, NULL, NULL, FALSE);
641 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_process_input_temp, NULL, NULL, FALSE);
642
643 return 0;
644
645error:
646 dt_print(DT_DEBUG_OPENCL, "[dev_pixelpipe] %s couldn't process on GPU\n", module->name());
647
648 dt_opencl_finish(pipe->devid);
649
650 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_blend_output_temp, NULL, NULL, FALSE);
651 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_blend_input_temp, NULL, NULL, FALSE);
652 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_process_input_temp, NULL, NULL, FALSE);
653
654 if(locked_input_entry)
656
657 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_output, output_entry, NULL, FALSE);
658
659 if(module->flags() & IOP_FLAGS_TAKE_NO_INPUT)
660 {
661 /* Root modules build their own input from external storage. If the OpenCL pre-check
662 * rejects the required allocation, CPU fallback must keep the same no-input contract
663 * instead of looking for an upstream cacheline that cannot exist. */
664
665 /* The `error:` label is the catch-all for *every* GPU failure (kernel errors, driver
666 * allocation faults, colorspace/blend failures...), not only the memory pre-check.
667 * Only quote a memory limit when the pre-check actually rejected the buffer, and quote
668 * the limit that was really exceeded -- otherwise the numbers contradict the failure
669 * (e.g. "needs 100 MiB but device limit is 1991 MiB", issue #878). */
670 switch(fit_reason)
671 {
673 dt_control_log(_("OpenCL failed for module `%s`: image buffer needs %" G_GSIZE_FORMAT
674 " MiB but the largest allocation the device allows is %" G_GSIZE_FORMAT
675 " MiB; falling back to CPU"),
676 module->name(), (size_t)(fit_needed / (1024 * 1024)),
677 (size_t)(fit_limit / (1024 * 1024)));
678 break;
680 dt_control_log(_("OpenCL failed for module `%s`: image buffer needs %" G_GSIZE_FORMAT
681 " MiB but only %" G_GSIZE_FORMAT " MiB are free on the device; falling back to CPU"),
682 module->name(), (size_t)(fit_needed / (1024 * 1024)),
683 (size_t)(fit_limit / (1024 * 1024)));
684 break;
686 dt_control_log(_("OpenCL failed for module `%s`: image dimensions %" G_GSIZE_FORMAT "x%" G_GSIZE_FORMAT
687 " exceed the device limits; falling back to CPU"),
688 module->name(), precheck_width, precheck_height);
689 break;
690 default: // DT_OPENCL_FIT_OK / UNINITED: the buffer fit, the GPU failed for another reason
691 dt_control_log(_("OpenCL failed for module `%s`; falling back to CPU"), module->name());
692 break;
693 }
694 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
695 cache_output, cpu_input_entry, output_entry);
696 }
697
698 if(!IS_NULL_PTR(input))
699 {
701 "[dev_pixelpipe] %s GPU error fallback will reuse host input\n",
702 module->name());
703 }
704 else if(!IS_NULL_PTR(cl_mem_input))
705 {
706 if(_gpu_init_input(pipe, &input, &cl_mem_input, piece, tiling,
707 cpu_input_entry, output_entry))
708 {
709 if(borrowed_cl_mem_input)
710 {
711 dt_dev_pixelpipe_cache_return_cl_payload(cpu_input_entry, cl_mem_input);
712 cl_mem_input = NULL;
713 borrowed_cl_mem_input = FALSE;
714 }
715 else
716 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, cpu_input_entry, NULL,
717 dt_dev_pixelpipe_cache_gpu_device_buffer(pipe, cpu_input_entry));
718 return 1;
719 }
720 }
721 else if(IS_NULL_PTR(input))
722 {
724 "[dev_pixelpipe] %s CPU fallback has no input buffer (cache allocation failed?)\n",
725 module->name());
726 return 1;
727 }
728
729 if(borrowed_cl_mem_input)
730 {
731 dt_dev_pixelpipe_cache_return_cl_payload(cpu_input_entry, cl_mem_input);
732 cl_mem_input = NULL;
733 }
734 else
735 dt_dev_pixelpipe_cache_release_cl_buffer(&cl_mem_input, cpu_input_entry, input,
736 dt_dev_pixelpipe_cache_gpu_device_buffer(pipe, cpu_input_entry));
737
738 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
739 cache_output, cpu_input_entry, output_entry);
740}
741
742#else
743
745 const dt_dev_pixelpipe_iop_t *previous_piece,
747 dt_pixelpipe_flow_t *pixelpipe_flow,
748 gboolean *const cache_output,
749 dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
750{
751 return pixelpipe_process_on_CPU(pipe, piece, previous_piece, tiling, pixelpipe_flow,
752 cache_output, input_entry, output_entry);
753}
754
755#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:1098
dt_iop_colorspace_type_t dt_develop_blend_colorspace(const dt_dev_pixelpipe_iop_t *const piece, dt_iop_colorspace_type_t cst)
Definition blend.c:180
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:642
dt_iop_colorspace_type_t
@ IOP_CS_RAW
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_control_log(const char *msg,...)
Definition control.c:777
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
@ DT_DEBUG_OPENCL
Definition darktable.h:744
@ DT_DEBUG_TILING
Definition darktable.h:761
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:475
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
Definition darktable.h:454
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
dt_dev_pixelpipe_display_mask_t
Definition develop.h:116
@ DT_DEV_PIXELPIPE_DISPLAY_ANY
Definition develop.h:138
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:243
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_FLAGS_TAKE_NO_INPUT
Definition imageop.h:206
void dt_ioppr_transform_image_colorspace(struct dt_iop_module_t *self, 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)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
int dt_ioppr_transform_image_colorspace_cl(struct dt_iop_module_t *self, 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)
cl_ulong dt_opencl_get_device_available(const int devid)
Definition opencl.c:2740
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:2759
int dt_opencl_is_inited(void)
Definition opencl.c:2828
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
Definition opencl.c:2289
gboolean dt_opencl_finish(const int devid)
Definition opencl.c:1375
dt_opencl_fit_reason_t
Definition opencl.h:123
@ DT_OPENCL_FIT_ALLOC_LIMIT
Definition opencl.h:126
@ DT_OPENCL_FIT_AVAILABLE
Definition opencl.h:127
@ DT_OPENCL_FIT_DIMENSION
Definition opencl.h:125
@ DT_OPENCL_FIT_OK
Definition opencl.h:124
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:40
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)
void * dt_pixel_cache_alloc(dt_dev_pixelpipe_cache_t *cache, 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_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)
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)
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.
void dt_dev_pixelpipe_cache_wrlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the write lock on the entry.
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.
gboolean dt_dev_pixelpipe_cache_flush_host_pinned_image(dt_dev_pixelpipe_cache_t *cache, 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_dev_pixelpipe_cache_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
void dt_dev_pixelpipe_cache_flush_clmem(dt_dev_pixelpipe_cache_t *cache, const int devid)
Release cached OpenCL buffers for a single device.
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.
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)
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
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:818
dt_iop_buffer_dsc_t dsc_out
dt_iop_buffer_dsc_t dsc_in
dt_atomic_int realtime
dt_dev_pixelpipe_type_t type
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:259
dt_iop_buffer_dsc_t dsc
Definition image.h:337
#define MAX(a, b)
Definition thinplate.c:29