Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
region.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19// Per-region gather/composite + the region reconstruction driver (CPU + OpenCL). (implementation; see region.h for
20// the public API.)
21
22#include "common/darktable.h"
23#include "common/gaussian.h"
24#include "develop/imageop.h"
27#include "iop/highlights/blur.h"
30#include "iop/highlights/core.h"
32#include <math.h>
33#include <string.h>
34
36static void _region_gather(_hl_region_ctx_t *const ctx)
37{
38 float *const restrict interp = ctx->interp;
39 const float *const restrict mask = ctx->mask;
40 const float *const restrict depth = ctx->depth;
41 const int width = ctx->width;
42 const _hl_region_t *const region = ctx->region;
43 const int region_w = ctx->region_w;
44 const int region_h = ctx->region_h;
45 const size_t region_pixels = ctx->region_pixels;
46 float *const restrict estimate = ctx->estimate;
47 float *const restrict valid = ctx->valid;
48 float *const restrict clip_depth = ctx->clip_depth;
49 float *const restrict clip0 = ctx->clip0;
50
51 // gather region into contiguous buffers
52 HL_PFOR(collapse(2))
53 for(int y = 0; y < region_h; y++)
54 for(int x = 0; x < region_w; x++)
55 {
56 const size_t pixel_index = (size_t)(region->ry0 + y) * width + (region->rx0 + x);
57 const size_t src_offset = pixel_index * 4;
58 const size_t dst_offset = ((size_t)y * region_w + x) * 4;
59 clip_depth[(size_t)y * region_w + x] = depth[pixel_index];
60 for(int k = 0; k < 4; k++)
61 {
62 estimate[dst_offset + k] = interp[src_offset + k];
63 clip0[dst_offset + k] = interp[src_offset + k]; // saturated value, physical floor for clipped ch.
64 valid[dst_offset + k] = fmaxf(1.f - mask[src_offset + k], 0.f); // per-channel validity
65 }
66 }
67
68 // A clipped channel saturated, so its true value is >= its clip level: floor the reconstruction at
69 // the saturated value so a low-guide fit cannot push it below saturation (the amber -> magenta
70 // collapse). Monotone (only raises), so no overshoot and no per-pixel switching. Applied before the
71 // joint core, so the all-clip dome and chroma diffusion are fed the corrected (brighter) rim.
72 HL_PFOR()
73 for(size_t i = 0; i < region_pixels; i++)
74 for(int c = 0; c < 3; c++)
75 if(valid[i * 4 + c] < 0.5f) estimate[i * 4 + c] = fmaxf(estimate[i * 4 + c], clip0[i * 4 + c]);
76}
77
78// Stage 9 -- optional Poissonian grain on the reconstructed channels, then scatter the
79// padded-window estimate back into the full-res interp buffer at the region's offset.
81static void _region_composite(_hl_region_ctx_t *const ctx)
82{
83 float *const restrict interp = ctx->interp;
84 const float *const restrict mask = ctx->mask;
85 const int width = ctx->width;
86 const _hl_region_t *const region = ctx->region;
87 const int region_w = ctx->region_w;
88 const int region_h = ctx->region_h;
89 const float noise_level = ctx->noise_level;
90 float *const restrict estimate = ctx->estimate;
91 float *const restrict valid = ctx->valid;
92
93 // Optional grain: reconstructed highlights are very smooth, so break them up with Poissonian noise
94 // whose amplitude scales with the local value (the "noise level" user parameter). Only clipped
95 // channels get it; valid channels keep their real data. Matches the legacy last-scale noise.
96 if(noise_level > 0.f)
97 {
98 HL_PFOR(collapse(2))
99 for(int y = 0; y < region_h; y++)
100 {
101 for(int x = 0; x < region_w; x++)
102 {
103 const size_t i = ((size_t)y * region_w + x) * 4;
104
105 // per-pixel RNG, deterministic in region coordinates so the render is reproducible
106 uint32_t DT_ALIGNED_ARRAY state[4]
107 = { splitmix32(x + 1), splitmix32((y + 1) * (x + 3)), splitmix32(1337), splitmix32(666) };
112
113 // per-channel noise standard deviation = value * noise_level
114 dt_aligned_pixel_t current = { estimate[i], estimate[i + 1], estimate[i + 2], estimate[i + 3] };
115 dt_aligned_pixel_t nsigma = { current[0] * noise_level, current[1] * noise_level, current[2] * noise_level,
116 current[3] * noise_level };
117 const int DT_ALIGNED_ARRAY flip[4] = { TRUE, FALSE, TRUE, FALSE };
118 dt_aligned_pixel_t noise = { 0.f };
120
121 // one-sided (brightening) grain, only on the reconstructed (clipped) channels
122 for(int c = 0; c < 3; c++)
123 if(valid[i + c] < 0.5f) estimate[i + c] = fmaxf(current[c] + fabsf(noise[c] - current[c]), 0.f);
124 }
125 }
126 }
127
128 // FLOW: final per-region composite (article §"The algorithm", the flowchart's remosaic-feeding step).
129 // Scatter the reconstructed clipped channels from the padded window back into the full-res interp
130 // buffer at the region's absolute offset (region->rx0/ry0). Only the channels that were ACTUALLY
131 // clipped (mask > 0.5) are overwritten -- valid channels keep their measured values untouched -- and
132 // the write is floored at 0 (no negative radiance). Unclipped pixels outside every region are never
133 // visited, so the reconstruction only ever edits the holes.
134 HL_PFOR(collapse(2))
135 for(int y = 0; y < region_h; y++)
136 {
137 for(int x = 0; x < region_w; x++)
138 {
139 const size_t src_offset = ((size_t)y * region_w + x) * 4;
140 const size_t dst_offset = ((size_t)(region->ry0 + y) * width + (region->rx0 + x)) * 4;
141
142 // only overwrite the channels that were actually clipped
143 for(int c = 0; c < 3; c++)
144 if(mask[dst_offset + c] > 0.5f) interp[dst_offset + c] = fmaxf(estimate[src_offset + c], 0.f);
145 }
146 }
147}
148
149void _region_guided_filter(float *const restrict interp, const float *const restrict mask,
150 const float *const restrict depth, const int width, const _hl_region_t *const region,
151 const dt_dev_pixelpipe_t *pipe, const float solid_color, const int max_iter,
152 const float noise_level)
153{
154 const int region_w = region->rx1 - region->rx0 + 1;
155 const int region_h = region->ry1 - region->ry0 + 1;
156 if(region_w < 2 || region_h < 2) return;
157 const size_t region_pixels = (size_t)region_w * region_h;
158 // Sanity guard only (the pipe-cache arena handles memory): skip a pathologically huge region.
159 // Normal clipped regions in a full raw stay well under this; keep it high so nothing is missed.
160 if(region_pixels > (size_t)64 * 1024 * 1024) return;
161
162 float *const restrict estimate
163 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // running estimate (RGB+norm)
164 float *const restrict prev_scale
165 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // snapshot at scale start
166 float *const restrict valid
167 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // per-channel validity (0..1)
168 float *const restrict blur_in
169 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // blur scratch (in)
170 float *const restrict plane1
171 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // per-channel fit accumulator
172 float *const restrict plane2
173 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // blur scratch (out)
174 float *const restrict plane3
175 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // blur scratch (out)
176 float *const restrict valid_variance
177 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // per-channel valid variance
178 float *const restrict guide_score
179 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // per-channel best guide score
180 float *const restrict clip_depth
181 = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe); // per-pixel clip-to-valid depth
182 float *const restrict clip0
183 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 4, pipe); // saturated (clipped) value per channel
184 if(!estimate || !prev_scale || !valid || !blur_in || !plane1 || !plane2 || !plane3 || !valid_variance
185 || !guide_score || !clip_depth || !clip0)
186 {
194 dt_pixelpipe_cache_free_align(valid_variance);
198 return;
199 }
200
201 const int extent = MAX(region->x1 - region->x0, region->y1 - region->y0) + 1;
202 const float epsilon = 1e-6f;
203 const int max_cg_iter = CLAMP(2 * extent, 200, 2000);
204 // The prototype solves the seam regulariser with a direct sparse solve (exact). C has no sparse
205 // direct solver, so run the FULL CG budget instead of capping at the user "iterations" param:
206 // an under-converged biharmonic CG stops each channel at a different point -> per-channel
207 // inconsistency -> chroma drift. maxit (not max_iter) is the honest best-effort here.
208 (void)max_iter;
209
210 uint8_t *const restrict hole = (uint8_t *)dt_pixelpipe_cache_alloc_align(sizeof(uint8_t) * region_pixels, pipe);
211 float *const restrict solver_field
212 = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe); // solver working field
213 float *const restrict fill_planes
214 = dt_pixelpipe_cache_alloc_align_float(region_pixels * 3, pipe); // fused-fill planes
215 float *const restrict dome_lum = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe); // luminance dome
216 float *const restrict lum_accum
217 = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe); // luminance accum (chroma denom)
218 float *const restrict reaction_weight
219 = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe); // chroma reaction weight
220 float *const restrict flat_target
221 = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe); // chroma flat target
222 float *const restrict cg_residual = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe); // CG scratch
223 float *const restrict cg_dir = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe);
224 float *const restrict cg_operator = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe);
225 float *const restrict cg_tmp1 = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe);
226 float *const restrict cg_tmp2 = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe);
227
228 _hl_region_ctx_t ctx = {
229 .interp = interp,
230 .mask = mask,
231 .depth = depth,
232 .width = width,
233 .region = region,
234 .pipe = pipe,
235 .region_w = region_w,
236 .region_h = region_h,
237 .region_pixels = region_pixels,
238 .extent = extent,
239 .epsilon = epsilon,
240 .max_cg_iter = max_cg_iter,
241 .solid_color = solid_color,
242 .noise_level = noise_level,
243 .estimate = estimate,
244 .prev_scale = prev_scale,
245 .valid = valid,
246 .blur_in = blur_in,
247 .plane1 = plane1,
248 .plane2 = plane2,
249 .plane3 = plane3,
250 .valid_variance = valid_variance,
251 .guide_score = guide_score,
252 .clip_depth = clip_depth,
253 .clip0 = clip0,
254 .hole = hole,
255 .solver_field = solver_field,
256 .fill_planes = fill_planes,
257 .dome_lum = dome_lum,
258 .lum_accum = lum_accum,
259 .reaction_weight = reaction_weight,
260 .flat_target = flat_target,
261 .cg_residual = cg_residual,
262 .cg_dir = cg_dir,
263 .cg_operator = cg_operator,
264 .cg_tmp1 = cg_tmp1,
265 .cg_tmp2 = cg_tmp2,
266 };
267
268 _region_gather(&ctx);
269
270 if(hole && solver_field && fill_planes && dome_lum && lum_accum && reaction_weight && flat_target && cg_residual
271 && cg_dir && cg_operator && cg_tmp1 && cg_tmp2)
272 {
273 _cf_reconstruct(&ctx);
274 _selfdome(&ctx);
275 _joint_core(&ctx);
276 _aniso_chroma(&ctx);
277 // Per-region timing breakdown. Only for regions big enough to matter (small ones are noise) and
278 }
280 dt_pixelpipe_cache_free_align(solver_field);
284 dt_pixelpipe_cache_free_align(reaction_weight);
291
292 _region_composite(&ctx);
293
301 dt_pixelpipe_cache_free_align(valid_variance);
305}
306
307// ---------------------------------------------------------------------------------------------
308// R9 sensor-rolloff (knee) estimation + inversion. See the DT_HL_KNEE macro comment for the why.
309// All values are handled in CLIP-NORMALIZED units: x = value / (clip level), so the detection
310// threshold sits at DT_HL_KNEE_DET (the clips[] passed around equal 0.995 * clip level) and the
311// band under estimation is [DT_HL_KNEE_LO, DT_HL_KNEE_DET).
312// ---------------------------------------------------------------------------------------------
313
314// ============================ OpenCL ============================
315
316#if defined(HAVE_OPENCL) && DT_HL_COEFF_FIELD && DT_HL_SPARSE_SOLVE && (DT_HL_ANISO_SOLVER == 2)
317// Device counterpart (per-region GPU orchestrator) of _region_guided_filter: gathers the
318// padded region window, derives the stage parameters from one on-device reduction
319// (union-hole plateau brightness -> cf_binv, per-channel clip counts -> deep channel, union
320// count -> shared dome grid), then chains the proven stages -- coefficient field
321// (_cf_stage_cl), high-frequency detail hybrid (_hf_stage_cl), floors + gated self-dome
322// (_selfdome_stage_cl), all-clip joint core (_joint_core_stage_cl), divergence-form
323// anisotropic chroma (_aniso_stage_cl) -- and scatters the clipped channels back. Everything
324// stays on the device except the reduction partials. Caller must handle noise_level > 0 on
325// the CPU (the grain epilogue is not ported).
326// Any change here must be mirrored in _region_guided_filter (CPU) and re-validated with the
327// HL_REGCL_TEST self-test (_region_guided_filter_cl_selftest).
328// Regions below this pixel count are reconstructed on the CPU even when the pipe runs on the
329// GPU: a device region pays ~1000 kernel launches (iterative stages, per-level sparse solves)
330
331cl_int _region_cpu_offload_cl(const int devid, void *gd_void, cl_mem interp, cl_mem mask, cl_mem depth,
332 const int width, const _hl_region_t *const region, const dt_dev_pixelpipe_t *pipe,
333 const float solid_color, const int max_iter, const float noise_level)
334{
336 const int region_w = region->rx1 - region->rx0 + 1;
337 const int region_h = region->ry1 - region->ry0 + 1;
338 if(region_w < 2 || region_h < 2) return CL_SUCCESS;
339 const size_t region_pixels = (size_t)region_w * region_h;
340
341 cl_int cl_err = DT_OPENCL_DEFAULT_ERROR;
342 size_t work_size[3] = { ROUNDUPDWD(region_w, devid), ROUNDUPDHT(region_h, devid), 1 };
343
344 cl_mem staging = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 9);
345 float *host = dt_pixelpipe_cache_alloc_align_float(region_pixels * 9, pipe);
346 if(!staging || IS_NULL_PTR(host)) goto out;
347
348 {
349 const int kernel = global_data->kernel_hl_window_pack;
350 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &interp);
351 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &mask);
352 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &depth);
353 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &staging);
354 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &width);
355 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region->rx0);
356 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region->ry0);
357 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_w);
358 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &region_h);
359 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, work_size);
360 if(cl_err != CL_SUCCESS) goto out;
361 }
362
363 cl_err = dt_opencl_read_buffer_from_device(devid, host, staging, 0, sizeof(float) * region_pixels * 9, CL_TRUE);
364 if(cl_err != CL_SUCCESS) goto out;
365
366 {
367 float *const hw_interp = host;
368 const float *const hw_mask = host + region_pixels * 4;
369 const float *const hw_depth = host + region_pixels * 8;
370
371 _hl_region_t translated_region = *region;
372 translated_region.x0 -= region->rx0;
373 translated_region.x1 -= region->rx0;
374 translated_region.y0 -= region->ry0;
375 translated_region.y1 -= region->ry0;
376 translated_region.rx1 -= region->rx0;
377 translated_region.ry1 -= region->ry0;
378 translated_region.rx0 = 0;
379 translated_region.ry0 = 0;
380
381 _region_guided_filter(hw_interp, hw_mask, hw_depth, region_w, &translated_region, pipe, solid_color, max_iter,
382 noise_level);
383 }
384
385 cl_err = dt_opencl_write_buffer_to_device(devid, host, staging, 0, sizeof(float) * region_pixels * 4, CL_TRUE);
386 if(cl_err != CL_SUCCESS) goto out;
387
388 {
389 const int kernel = global_data->kernel_hl_window_unpack;
390 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &staging);
391 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &interp);
392 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &width);
393 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region->rx0);
394 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region->ry0);
395 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_w);
396 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_h);
397 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, work_size);
398 }
399
400out:
403 return cl_err;
404}
405
406cl_int _region_guided_filter_cl(const int devid, void *gd_void, cl_mem interp, cl_mem mask, cl_mem depth,
407 const int width, const _hl_region_t *const region, const dt_dev_pixelpipe_t *pipe,
408 const float solid_color)
409{
411 const int region_w = region->rx1 - region->rx0 + 1;
412 const int region_h = region->ry1 - region->ry0 + 1;
413 if(region_w < 2 || region_h < 2) return CL_SUCCESS;
414 const size_t region_pixels = (size_t)region_w * region_h;
415 if(region_pixels > (size_t)64 * 1024 * 1024) return CL_SUCCESS;
416
417 cl_int cl_err = DT_OPENCL_DEFAULT_ERROR;
418 size_t work_size[3] = { ROUNDUPDWD(region_w, devid), ROUNDUPDHT(region_h, devid), 1 };
419 dt_gaussian_cl_t *cf_gaussian = NULL;
420
421 cl_mem estimate = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 4);
422 cl_mem valid = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 4);
423 cl_mem clip0 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 4);
424 cl_mem model_quality = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 4);
425 cl_mem clip_depth = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
426 cl_mem lsb0 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels); // pre-ladder luminance
427 cl_mem partials = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 8 * 256);
428 cl_mem steer = NULL; // coefficient-fill steering plane (guide structure)
429 if(!estimate || !valid || !clip0 || !model_quality || !clip_depth || !lsb0 || !partials) goto out;
430
431 // gather the padded region window into contiguous device buffers (est/clip0/vld/dep/lsb0)
432 {
433 const int kernel = global_data->kernel_hl_region_gather;
434 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &interp);
435 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &mask);
436 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &depth);
437 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &estimate);
438 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &clip0);
439 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &valid);
440 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &clip_depth);
441 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), &model_quality);
442 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(cl_mem), &lsb0);
443 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &width);
444 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &region->rx0);
445 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &region->ry0);
446 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &region_w);
447 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(int), &region_h);
448 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, work_size);
449 if(cl_err != CL_SUCCESS) goto out;
450 }
451
452 // ladder parameters from the pre-ladder statistics
453 const float cf_sigma = CLAMP(region->radius / 6.f, 8.f, 64.f);
454 const float cf_fmin = 0.05f;
455 float cf_binv;
456 float channel_means[3] = { 0.f, 0.f, 0.f }; // per-channel valid means (moment-pack centering)
457 int cdeep, ds_shared;
458 {
459 const int local_size = 64, n_groups = 256;
460 const int pixel_count = (int)region_pixels;
461 const int kernel = global_data->kernel_hl_region_stats;
462 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
463 size_t local[3] = { local_size, 1, 1 };
464 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
465 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
466 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &partials);
467 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &pixel_count);
468 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(float) * 8 * local_size, NULL);
469 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
470 if(cl_err != CL_SUCCESS) goto out;
471
472 float partials_host[8 * 256];
473 cl_err = dt_opencl_read_buffer_from_device(devid, partials_host, partials, 0, sizeof(float) * 8 * n_groups,
474 CL_TRUE);
475 if(cl_err != CL_SUCCESS) goto out;
476 double lsum = 0.0, lcnt = 0.0, clip_count_r = 0.0, clip_count_g = 0.0, clip_count_b = 0.0;
477 double msum[3] = { 0.0, 0.0, 0.0 };
478 for(int group = 0; group < n_groups; group++)
479 {
480 lsum += (double)partials_host[8 * group + 0];
481 lcnt += (double)partials_host[8 * group + 1];
482 clip_count_r += (double)partials_host[8 * group + 2];
483 clip_count_g += (double)partials_host[8 * group + 3];
484 clip_count_b += (double)partials_host[8 * group + 4];
485 msum[0] += (double)partials_host[8 * group + 5];
486 msum[1] += (double)partials_host[8 * group + 6];
487 msum[2] += (double)partials_host[8 * group + 7];
488 }
489 if(lcnt <= 0.0)
490 {
491 cl_err = CL_SUCCESS; // no clipped pixel in this window: nothing to do
492 goto out;
493 }
494 const float cf_lref = (float)(lsum / lcnt);
495 cf_binv = (cf_lref > 1e-9f) ? 1.f / (0.35f * cf_lref) : 0.f;
496 cdeep = (clip_count_r >= clip_count_g && clip_count_r >= clip_count_b)
497 ? 0
498 : ((clip_count_g >= clip_count_b) ? 1 : 2);
499 ds_shared = MAX(1, (int)ceilf(sqrtf((float)lcnt / (float)DT_HL_DOME_NMAX_SPARSE)));
500 // per-channel means of the VALID values: the moment packs are centered on them (see the
501 // CPU counterpart for the cancellation rationale)
502 const double valid_count_r = (double)region_pixels - clip_count_r,
503 valid_count_g = (double)region_pixels - clip_count_g,
504 valid_count_b = (double)region_pixels - clip_count_b;
505 channel_means[0] = valid_count_r > 0.5 ? (float)(msum[0] / valid_count_r) : 0.f;
506 channel_means[1] = valid_count_g > 0.5 ? (float)(msum[1] / valid_count_g) : 0.f;
507 channel_means[2] = valid_count_b > 0.5 ? (float)(msum[2] / valid_count_b) : 0.f;
508 }
509
510 // one gaussian handle serves every cf_sigma blur of the region (each init allocates two
511 // region-sized temp buffers -- 13+ per-blur re-allocations were pure churn)
512 cf_gaussian = _region_blur_handle(devid, region_w, region_h, cf_sigma);
513
514 // Steering plane for the coefficient fills = the measured guide structure, built ONCE here
515 // (same est state as the CPU: after the saturation floor) and shared by the coefficient-field
516 // and HF stages, exactly like the CPU path.
517 {
518 steer = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
519 if(steer)
520 {
521 const int kernel = global_data->kernel_hl_cfa_steer;
522 const int pixel_count = (int)region_pixels;
523 size_t work_size_1d[3] = { ROUNDUPDWD(pixel_count, devid), 1, 1 };
524 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
525 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
526 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &steer);
527 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &pixel_count);
528 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, work_size_1d);
529 if(cl_err != CL_SUCCESS) goto out;
530 }
531 }
532
533 cl_err = _cf_stage_cl(devid, gd_void, estimate, valid, model_quality, lsb0, steer, channel_means, cf_gaussian,
534 region_w, region_h, cf_sigma, cf_fmin, cf_binv, cdeep);
535 if(cl_err == CL_SUCCESS) dt_opencl_finish(devid);
536 if(cl_err != CL_SUCCESS) goto out;
537 cl_err = _hf_stage_cl(devid, gd_void, estimate, valid, model_quality, lsb0, steer, cf_gaussian, region_w,
538 region_h, cf_sigma, cf_fmin, cf_binv);
539 if(cl_err == CL_SUCCESS) dt_opencl_finish(devid);
540 if(cl_err != CL_SUCCESS) goto out;
541
542 // gated self-dome: the soft floor is unconditional (production applies it right after the
543 // HF hybrid); the dome + blend + hard floor only run where a clipped channel with a
544 // surviving guide sits on a weak colour-line
545 int need_self = 0;
546 {
547 const int local_size = 64, n_groups = 256;
548 const int pixel_count = (int)region_pixels;
549 const int kernel = global_data->kernel_hl_need_self;
550 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
551 size_t local[3] = { local_size, 1, 1 };
552 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &valid);
553 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &model_quality);
554 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &clip_depth);
555 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &partials);
556 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &pixel_count);
557 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), &cf_sigma);
558 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float) * local_size, NULL);
559 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
560 if(cl_err != CL_SUCCESS) goto out;
561 float partials_host[256];
562 cl_err
563 = dt_opencl_read_buffer_from_device(devid, partials_host, partials, 0, sizeof(float) * n_groups, CL_TRUE);
564 if(cl_err != CL_SUCCESS) goto out;
565 for(int group = 0; group < n_groups; group++)
566 if(partials_host[group] > 0.f) need_self = 1;
567 }
568
569 if(need_self)
570 {
571 cl_err = _selfdome_stage_cl(devid, gd_void, estimate, valid, model_quality, clip0, clip_depth, region_w,
572 region_h, cf_sigma, region->radius, ds_shared, pipe);
573 if(cl_err != CL_SUCCESS) goto out;
574 }
575 else
576 {
577 const int kernel = global_data->kernel_hl_soft_floor;
578 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
579 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
580 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &clip0);
581 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
582 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
583 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, work_size);
584 if(cl_err != CL_SUCCESS) goto out;
585 }
586
587 {
588 // Step 7: all-clip joint core (shared biharmonic dome x screened-Poisson diffused chroma)
589 const int extent = MAX(region->x1 - region->x0, region->y1 - region->y0) + 1;
590 cl_err = _joint_core_stage_cl(devid, gd_void, estimate, valid, clip0, region_w, region_h, solid_color,
591 region->radius, extent, pipe);
592 if(cl_err == CL_SUCCESS) dt_opencl_finish(devid);
593 }
594 if(cl_err != CL_SUCCESS) goto out;
595 // Step 8: structure-steered chrominance coherence (div(D grad r)=0 under the obstacle r >= c0/L)
596 cl_err = _aniso_stage_cl(devid, gd_void, estimate, valid, clip0, region_w, region_h, region->radius, pipe);
597 if(cl_err == CL_SUCCESS) dt_opencl_finish(devid);
598 if(cl_err != CL_SUCCESS) goto out;
599
600 // scatter the reconstructed clipped channels back into the full-res buffer
601 {
602 const int kernel = global_data->kernel_hl_region_scatter;
603 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &interp);
604 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &mask);
605 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &estimate);
606 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &width);
607 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region->rx0);
608 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region->ry0);
609 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_w);
610 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_h);
611 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, work_size);
612 }
613
614out:
615 dt_gaussian_free_cl(cf_gaussian);
619 dt_opencl_release_mem_object(model_quality);
624 return cl_err;
625}
626
627#endif // HAVE_OPENCL && DT_HL_COEFF_FIELD && DT_HL_SPARSE_SOLVE && ANISO_SOLVER 2
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
dt_gaussian_cl_t * _region_blur_handle(const int devid, const int region_w, const int region_h, const float sigma)
Definition blur.c:69
__DT_CLONE_TARGETS__ void _aniso_chroma(_hl_region_ctx_t *const ctx)
Definition chroma.c:322
cl_int _cf_stage_cl(const int devid, void *gd_void, cl_mem estimate, cl_mem valid, cl_mem model_quality, cl_mem luminance, cl_mem steer, const float *const restrict channel_means, dt_gaussian_cl_t *gaussian, const int region_w, const int region_h, const float cf_sigma, const float cf_fmin, const float cf_binv, const int cdeep)
__DT_CLONE_TARGETS__ void _cf_reconstruct(_hl_region_ctx_t *const ctx)
cl_int _hf_stage_cl(const int devid, void *gd_void, cl_mem estimate, cl_mem valid, cl_mem model_quality, cl_mem luminance, cl_mem steer, dt_gaussian_cl_t *gaussian, const int region_w, const int region_h, const float cf_sigma, const float cf_fmin, const float cf_binv)
const dt_colormatrix_t dt_aligned_pixel_t out
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
__DT_CLONE_TARGETS__ void _joint_core(_hl_region_ctx_t *const ctx)
Definition core.c:151
__DT_CLONE_TARGETS__ void _selfdome(_hl_region_ctx_t *const ctx)
Definition core.c:37
#define DT_ALIGNED_ARRAY
Definition darktable.h:400
#define dt_pixelpipe_cache_alloc_align(size, pipe)
Definition darktable.h:449
#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 __DT_CLONE_TARGETS__
Definition darktable.h:379
#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
static float4 dt_noise_generator_simd(const dt_noise_distribution_t distribution, const float4 mu, const float4 param, uint state[4])
static unsigned int splitmix32(const unsigned long seed)
static float xoshiro128plus(uint state[4])
void dt_gaussian_free_cl(dt_gaussian_cl_t *g)
Definition gaussian.c:353
static float kernel(const float *x, const float *y)
static const float x
float *const restrict const size_t k
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2580
int dt_opencl_write_buffer_to_device(const int devid, void *host, void *device, const size_t offset, const size_t size, const int blocking)
Definition opencl.c:2348
int dt_opencl_read_buffer_from_device(const int devid, void *host, void *device, const size_t offset, const size_t size, const int blocking)
Definition opencl.c:2337
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2155
gboolean dt_opencl_finish(const int devid)
Definition opencl.c:1375
int dt_opencl_enqueue_kernel_2d_with_local(const int dev, const int kernel, const size_t *sizes, const size_t *local)
Definition opencl.c:2170
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2415
#define DT_OPENCL_DEFAULT_ERROR
Definition opencl.h:57
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
static __DT_CLONE_TARGETS__ void _region_composite(_hl_region_ctx_t *const ctx)
Definition region.c:81
static __DT_CLONE_TARGETS__ void _region_gather(_hl_region_ctx_t *const ctx)
Definition region.c:36
void _region_guided_filter(float *const restrict interp, const float *const restrict mask, const float *const restrict depth, const int width, const _hl_region_t *const region, const dt_dev_pixelpipe_t *pipe, const float solid_color, const int max_iter, const float noise_level)
Definition region.c:149
const float uint32_t state[4]
const float const int flip
const float noise
#define DT_HL_DOME_NMAX_SPARSE
#define HL_PFOR(...)
const _hl_region_t * region
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MAX(a, b)
Definition thinplate.c:29