Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
chroma.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// Anisotropic (divergence-form) chrominance-coherence stage (CPU + OpenCL). (implementation; see chroma.h for the
20// public API.)
21
22#include "common/darktable.h"
23#include "develop/imageop.h"
25#include "iop/highlights/blur.h"
27#include "iop/highlights/pde.h"
28#include <string.h>
29
31void _aniso_tensor(const float *const restrict luminance, float *const restrict tensor_xx,
32 float *const restrict tensor_xy, float *const restrict tensor_yy, float *const restrict scratch,
33 const int region_w, const int region_h)
34{
35 const size_t region_pixels = (size_t)region_w * region_h;
36
37 // two 3x3 box passes ~ small gaussian on the luminance, into scratch
38 for(int pass = 0; pass < 2; pass++)
39 {
40 const float *const src = (pass == 0) ? luminance : tensor_xx;
41
42 __OMP_PARALLEL_FOR__(collapse(2))
43 for(int y = 0; y < region_h; y++)
44 for(int x = 0; x < region_w; x++)
45 {
46 double accum = 0.0;
47 int count = 0;
48
49 for(int offset_y = -1; offset_y <= 1; offset_y++)
50 for(int offset_x = -1; offset_x <= 1; offset_x++)
51 {
52 const int neighbour_y = CLAMP(y + offset_y, 0, region_h - 1);
53 const int neighbour_x = CLAMP(x + offset_x, 0, region_w - 1);
54 accum += src[(size_t)neighbour_y * region_w + neighbour_x];
55 count++;
56 }
57
58 ((pass == 0) ? tensor_xx : scratch)[(size_t)y * region_w + x] = (float)(accum / count);
59 }
60 }
61
62 // mean gradient magnitude of the blurred luminance = the anisotropy normalisation
63 double grad_sum = 0.0;
64
65 __OMP_PARALLEL_FOR__(collapse(2) reduction(+ : grad_sum))
66 for(int y = 0; y < region_h; y++)
67 for(int x = 0; x < region_w; x++)
68 {
69 const int x_lo = MAX(x - 1, 0), x_hi = MIN(x + 1, region_w - 1);
70 const int y_lo = MAX(y - 1, 0), y_hi = MIN(y + 1, region_h - 1);
71 const float grad_x = 0.5f * (scratch[(size_t)y * region_w + x_hi] - scratch[(size_t)y * region_w + x_lo]);
72 const float grad_y = 0.5f * (scratch[(size_t)y_hi * region_w + x] - scratch[(size_t)y_lo * region_w + x]);
73 tensor_xx[(size_t)y * region_w + x] = grad_x; // stash gradients temporarily
74 tensor_xy[(size_t)y * region_w + x] = grad_y;
75 grad_sum += dt_fast_hypotf(grad_x, grad_y);
76 }
77
78 const float grad_mean = fmaxf((float)(grad_sum / (double)region_pixels), 1e-9f);
79
80 // D = isophote outer product + damped gradient outer product (k = 4 mean-gradients crossover)
82 for(size_t i = 0; i < region_pixels; i++)
83 {
84 const float grad_x = tensor_xx[i];
85 const float grad_y = tensor_xy[i];
86 const float grad_mag = dt_fast_hypotf(grad_x, grad_y);
87 const float nonzero = (grad_mag > 1e-12f) ? 1.f : 0.f;
88 const float inv_mag = nonzero / (grad_mag + (1.f - nonzero));
89 const float grad_unit_x = grad_x * inv_mag + (1.f - nonzero); // cos(theta_grad)
90 const float grad_unit_y = grad_y * inv_mag; // sin(theta_grad)
91 const float cross_damp = expf(-grad_mag / (4.f * grad_mean)); // c2 = exp(-|grad L|/(4 <|grad L|>))
92 const float isophote_x = -grad_unit_y, isophote_y = grad_unit_x; // t = g rotated 90 deg (level line)
93
94 // D = t t^T + c2 * g g^T (isophote outer product + damped gradient outer product)
95 tensor_xx[i] = isophote_x * isophote_x + cross_damp * grad_unit_x * grad_unit_x;
96 tensor_xy[i] = isophote_x * isophote_y + cross_damp * grad_unit_x * grad_unit_y;
97 tensor_yy[i] = isophote_y * isophote_y + cross_damp * grad_unit_y * grad_unit_y;
98 }
99}
100
102void _aniso_iterate_obs(float *const restrict field, const float *const restrict obstacle,
103 const uint8_t *const restrict hole, const float *const restrict tensor_xx,
104 const float *const restrict tensor_xy, const float *const restrict tensor_yy,
105 float *const restrict tmp, const int region_w, const int region_h, const int iters,
106 const int box_x_lo, const int box_y_lo, const int box_x_hi, const int box_y_hi)
107{
108 // project the seed once so the first sweep already sees an admissible field
109 // (r <- max(r, obstacle), obstacle = c0/L in ratio space -- the saturation floor)
110 __OMP_PARALLEL_FOR__(collapse(2))
111 for(int y = box_y_lo; y <= box_y_hi; y++)
112 for(int x = box_x_lo; x <= box_x_hi; x++)
113 {
114 const size_t i = (size_t)y * region_w + x;
115 if(hole[i]) field[i] = fmaxf(field[i], obstacle[i]);
116 }
117
118 for(int iter = 0; iter < iters; iter++)
119 {
120 __OMP_PARALLEL_FOR__(collapse(2))
121 for(int y = box_y_lo; y <= box_y_hi; y++)
122 for(int x = box_x_lo; x <= box_x_hi; x++)
123 {
124 const size_t i = (size_t)y * region_w + x;
125
126 if(!hole[i])
127 {
128 tmp[i] = field[i];
129 continue;
130 }
131
132 const int x_lo = MAX(x - 1, 0), x_hi = MIN(x + 1, region_w - 1);
133 const int y_lo = MAX(y - 1, 0), y_hi = MIN(y + 1, region_h - 1);
134 const float center = field[i];
135 // second differences of r: d_xx r, d_yy r, and the mixed d_xy r (the Hessian of r)
136 const float d2_xx = field[(size_t)y * region_w + x_hi] - 2.f * center + field[(size_t)y * region_w + x_lo];
137 const float d2_yy = field[(size_t)y_hi * region_w + x] - 2.f * center + field[(size_t)y_lo * region_w + x];
138 const float d2_xy = 0.25f
139 * (field[(size_t)y_hi * region_w + x_hi] - field[(size_t)y_hi * region_w + x_lo]
140 - field[(size_t)y_lo * region_w + x_hi] + field[(size_t)y_lo * region_w + x_lo]);
141
142 // r <- max( r + 0.18*(D_xx d_xx r + 2 D_xy d_xy r + D_yy d_yy r), obstacle ): explicit
143 // trace-form step tr(D Hess r) then obstacle projection (article Step 8 update rule)
144 tmp[i] = fmaxf(center + 0.18f * (tensor_xx[i] * d2_xx + 2.f * tensor_xy[i] * d2_xy + tensor_yy[i] * d2_yy),
145 obstacle[i]);
146 }
147
149 for(int y = box_y_lo; y <= box_y_hi; y++)
150 memcpy(field + (size_t)y * region_w + box_x_lo, tmp + (size_t)y * region_w + box_x_lo,
151 (size_t)(box_x_hi - box_x_lo + 1) * sizeof(float));
152 }
153}
154
155int _aniso_div_solve(float *const restrict ratios, const float *const restrict valid,
156 const float *const restrict luminance, float *const restrict scratch_planes,
157 const int region_w, const int region_h, const dt_dev_pixelpipe_t *pipe)
158{
159 const size_t region_pixels = (size_t)region_w * region_h;
160 float *const restrict tensor_xx = scratch_planes;
161 float *const restrict tensor_xy = scratch_planes + region_pixels;
162 float *const restrict tensor_yy = scratch_planes + 2 * region_pixels;
163 float *const restrict tensor_scratch = scratch_planes + 3 * region_pixels;
164
165 // the three channels must share one hole (all-clip core); bail out otherwise
166 int n_unknowns = 0;
167 for(size_t i = 0; i < region_pixels; i++)
168 {
169 const int is_hole = (valid[i * 4 + 0] < 0.5f);
170 if(is_hole != (valid[i * 4 + 1] < 0.5f) || is_hole != (valid[i * 4 + 2] < 0.5f)) return 0;
171 n_unknowns += is_hole;
172 }
173 if(n_unknowns == 0) return 1;
174 if(n_unknowns > DT_HL_SPARSE_MAX) return 0;
175
176 _aniso_tensor(luminance, tensor_xx, tensor_xy, tensor_yy, tensor_scratch, region_w, region_h);
177
178 int *grid_to_unknown = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * region_pixels, pipe);
179 int *unknown_to_grid = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
180 int *unknown_x = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
181 int *unknown_y = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
182 int *permutation = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
183 int *inverse_perm = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
184 int *matrix_col_ptr = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * (n_unknowns + 1), pipe);
185 double *right_hand_side
186 = (double *)dt_pixelpipe_cache_alloc_align(sizeof(double) * (size_t)n_unknowns * 3, pipe);
187 int *matrix_row_index = NULL;
188 double *matrix_values = NULL;
189 int success = (grid_to_unknown && unknown_to_grid && unknown_x && unknown_y && permutation && inverse_perm
190 && matrix_col_ptr && right_hand_side);
191
192 if(success)
193 {
194 int unknown_index = 0;
195 for(size_t i = 0; i < region_pixels; i++)
196 {
197 const int is_hole = (valid[i * 4 + 0] < 0.5f);
198 grid_to_unknown[i] = is_hole ? unknown_index : -1;
199 if(is_hole)
200 {
201 unknown_to_grid[unknown_index] = (int)i;
202 unknown_y[unknown_index] = (int)(i / region_w);
203 unknown_x[unknown_index] = (int)(i - (size_t)unknown_y[unknown_index] * region_w);
204 unknown_index++;
205 }
206 }
207
208 for(int i = 0; i < n_unknowns; i++) permutation[i] = i;
209 _sp_nd_order(permutation, n_unknowns, unknown_x, unknown_y, 1);
210 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
211 inverse_perm[permutation[perm_index]] = perm_index;
212
213 static const int neighbour_dy[8] = { 0, 0, -1, 1, -1, 1, -1, 1 };
214 static const int neighbour_dx[8] = { -1, 1, 0, 0, -1, 1, 1, -1 };
215
216 for(int pass = 0; pass < 2 && success; pass++)
217 {
218 if(pass == 1)
219 {
220 int total = 0;
221 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
222 {
223 const int col_count = matrix_col_ptr[perm_index];
224 matrix_col_ptr[perm_index] = total;
225 total += col_count;
226 }
227 matrix_col_ptr[n_unknowns] = total;
228 matrix_row_index = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * total, pipe);
229 matrix_values = (double *)dt_pixelpipe_cache_alloc_align(sizeof(double) * total, pipe);
230 if(!matrix_row_index || !matrix_values)
231 success = 0;
232 else
233 memset(right_hand_side, 0, sizeof(double) * (size_t)n_unknowns * 3);
234 }
235
236 for(int perm_index = 0; perm_index < n_unknowns && success; perm_index++)
237 {
238 const int origin_grid = unknown_to_grid[permutation[perm_index]];
239 const int origin_y = origin_grid / region_w;
240 const int origin_x = origin_grid - origin_y * region_w;
241 double diagonal = 0.0;
242 int n_col_entries = 0;
243
244 for(int edge = 0; edge < 8; edge++)
245 {
246 const int neighbour_x = origin_x + neighbour_dx[edge];
247 const int neighbour_y = origin_y + neighbour_dy[edge];
248 // note: at the region border, missing neighbours simply drop out (no-flux boundary)
249 if(neighbour_x < 0 || neighbour_y < 0 || neighbour_x >= region_w || neighbour_y >= region_h)
250 continue; // Neumann at the region border
251 const size_t j = (size_t)neighbour_y * region_w + neighbour_x;
252 const float weight = _aniso_edge_w(tensor_xx, tensor_xy, tensor_yy, (size_t)origin_grid, j,
253 neighbour_dx[edge], neighbour_dy[edge]); // w_ij >= 0
254 if(weight <= 0.f) continue;
255 diagonal += weight; // diagonal = sum_j w_ij (graph-Laplacian row sum)
256
257 if(grid_to_unknown[j] >= 0)
258 {
259 const int target_row = inverse_perm[grid_to_unknown[j]];
260 if(target_row < perm_index)
261 {
262 if(pass == 1)
263 {
264 matrix_row_index[matrix_col_ptr[perm_index] + n_col_entries] = target_row;
265 matrix_values[matrix_col_ptr[perm_index] + n_col_entries]
266 = -(double)weight; // off-diagonal A_ij = -w_ij
267 }
268 n_col_entries++;
269 }
270 }
271 else if(pass == 1)
272 // Dirichlet neighbour (rim, not an unknown): its fixed r_valid moves to the RHS as
273 // +w_ij * r_valid_j, one per colour channel (same matrix, three right-hand sides)
274 for(int c = 0; c < 3; c++)
275 right_hand_side[(size_t)c * n_unknowns + perm_index] += (double)weight * ratios[j * 4 + c];
276 }
277
278 // diagonal last (any order works: columns need not be sorted)
279 if(pass == 1)
280 {
281 matrix_row_index[matrix_col_ptr[perm_index] + n_col_entries] = perm_index;
282 matrix_values[matrix_col_ptr[perm_index] + n_col_entries] = diagonal;
283 }
284 n_col_entries++;
285 if(pass == 0) matrix_col_ptr[perm_index] = n_col_entries;
286 }
287 }
288
289 if(success)
290 {
291 _sp_chol_t *factor = _sp_chol_factor(n_unknowns, matrix_col_ptr, matrix_row_index, matrix_values, pipe);
292 if(factor)
293 {
294 for(int c = 0; c < 3; c++)
295 {
296 _sp_chol_solve(factor, right_hand_side + (size_t)c * n_unknowns);
297 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
298 ratios[(size_t)unknown_to_grid[permutation[perm_index]] * 4 + c]
299 = (float)right_hand_side[(size_t)c * n_unknowns + perm_index];
300 }
302 }
303 else
304 success = 0;
305 }
306 }
307
308 dt_pixelpipe_cache_free_align(grid_to_unknown);
309 dt_pixelpipe_cache_free_align(unknown_to_grid);
313 dt_pixelpipe_cache_free_align(inverse_perm);
314 dt_pixelpipe_cache_free_align(matrix_col_ptr);
315 dt_pixelpipe_cache_free_align(matrix_row_index);
316 dt_pixelpipe_cache_free_align(matrix_values);
317 dt_pixelpipe_cache_free_align(right_hand_side);
318 return success;
319}
320
323{
324 const _hl_region_t *const region = ctx->region;
325 const dt_dev_pixelpipe_t *const pipe = ctx->pipe;
326 const int region_w = ctx->region_w;
327 const int region_h = ctx->region_h;
328 const size_t region_pixels = ctx->region_pixels;
329 const float epsilon = ctx->epsilon;
330 float *const restrict estimate = ctx->estimate;
331 float *const restrict prev_scale = ctx->prev_scale;
332 float *const restrict valid = ctx->valid;
333 float *const restrict blur_in = ctx->blur_in;
334 float *const restrict plane1 = ctx->plane1;
335 float *const restrict clip0 = ctx->clip0;
336 uint8_t *const restrict hole = ctx->hole;
337 float *const restrict solver_field = ctx->solver_field;
338 float *const restrict lum_accum = ctx->lum_accum;
339 float *const restrict reaction_weight = ctx->reaction_weight;
340 float *const restrict flat_target = ctx->flat_target;
341
342 // --- uncertainty-aware biharmonic seam regulariser (fix_prototype.py _weighted_solve) ---
343 // The steps above recover magnitude well but leave SEAMS where the method changes: the
344 // guide-flip on decorrelated content, and above all the all-clip-core <-> partial-clip
345 // handoff (a bright joint-core dome meeting an under-estimated single-guide reconstruction).
346 // No confidence weight can hide a discontinuity in the thing it weights, so iron the seams
347 // out afterwards: per channel solve (diag(Wd) + lambda*Delta^2) u = diag(Wd)*rec over the
348 // any-clip region, with the finished reconstruction as both the data target and the initial
349 // guess, and Wd = Wc^2 (= R^4) the fidelity weight. Where the recon is trustworthy (Wd high)
350 // u = rec is preserved; where it is not (Wd low: seams, decorrelated, all-clip core) the
351 // biharmonic prior flattens the seam's CURVATURE spike while preserving smooth domes and
352 // gradients (a harmonic prior would over-smooth them). Magnitude is preserved because the
353 // target is the recon itself. Full-res CG: the dome is already built, so the solve only has
354 // to relax the localised seams -- no dome-building stall. See the companion article.
355
356 // --- structure-steered chroma: diffuse the clipped channels' ratios est_c/L along the
357 // isophotes of the recovered luminance, coarse-to-fine (pyramid) so the whole hole is
358 // seeded before refinement. Magnitude (the norm L) is untouched: only direction changes.
359 //
360 // MATHS BRIDGE -- Step 8 chrominance coherence (article §"Chrominance coherence", the
361 // anisotropic chroma pass): minimize E_chrominance = int_Omega grad(r)^T D grad(r) dOmega
362 // subject to r_c >= c0/L_sum, Euler-Lagrange div(D grad r) = 0, D structure-steered. Restricted
363 // to the all-clip pixels; the coefficient-field results act as Dirichlet anchors. Solver picked
364 // by size: _aniso_div_solve (direct, small cores) or the coarse-to-fine _aniso_iterate_obs
365 // pyramid (large cores), then a full-res projected polish. Reassembly RGB = L_sum * r.
366 {
367 // the aniso pass must not rewrite the coefficient-field estimates: only the guide-less
368 // all-clip core diffuses, and the coefficient-field pixels act as valid anchors
369 // vld_an: all-clip pixels keep valid < 0.5 (they diffuse); every other pixel is promoted to
370 // an anchor (validity raised to >= 0.6), so div(D grad r)=0 sees them as fixed Dirichlet data
371 HL_PFOR()
372 for(size_t i = 0; i < region_pixels; i++)
373 {
374 const int allc = (valid[i * 4 + 0] < 0.5f && valid[i * 4 + 1] < 0.5f && valid[i * 4 + 2] < 0.5f);
375
376 for(int c = 0; c < 4; c++)
377 prev_scale[i * 4 + c] = allc ? valid[i * 4 + c] : fmaxf(valid[i * 4 + c], 0.6f);
378 }
379
380 const float *const restrict vld_an = prev_scale;
381
382 // fine-level luminance and per-channel ratios (ratio planes packed in s1's 4-ch layout)
383 // L_sum = R+G+B, r_c = est_c / L_sum: the split of magnitude from chrominance (step 8 diffuses
384 // only r; L_sum is left untouched and re-multiplied back at the reassembly)
386 for(size_t i = 0; i < region_pixels; i++)
387 {
388 const float lum_val = fmaxf(estimate[i * 4 + 0] + estimate[i * 4 + 1] + estimate[i * 4 + 2], epsilon);
389 lum_accum[i] = lum_val;
390
391 for(int c = 0; c < 3; c++)
392 plane1[i * 4 + c] = estimate[i * 4 + c] / lum_val;
393 }
394
395 // unknowns and their bounding box: the diffusion only ever writes pixels with
396 // vld_an < 0.5 (the all-clip core in coefficient-field mode)
397 size_t n_aniso = 0;
398 int abx0 = region_w, aby0 = region_h, abx1 = -1, aby1 = -1;
399 for(int y = 0; y < region_h; y++)
400 for(int x = 0; x < region_w; x++)
401 {
402 const size_t i = (size_t)y * region_w + x;
403 if(vld_an[i * 4 + 0] < 0.5f || vld_an[i * 4 + 1] < 0.5f || vld_an[i * 4 + 2] < 0.5f)
404 {
405 n_aniso++;
406 abx0 = MIN(abx0, x);
407 abx1 = MAX(abx1, x);
408 aby0 = MIN(aby0, y);
409 aby1 = MAX(aby1, y);
410 }
411 }
412
413 int aniso_done = 0;
414 if(n_aniso == 0) aniso_done = 1; // nothing to diffuse: skip the whole machinery
415
416 // primary Step-8 estimator: exact div(D grad r)=0 direct solve (returns 0 -> fall back to
417 // the coarse-to-fine pyramid below for cores too large for the sparse Cholesky)
418 if(!aniso_done) aniso_done = _aniso_div_solve(plane1, vld_an, lum_accum, blur_in, region_w, region_h, pipe);
419
420 // pyramid depth: halve until the deepest hole spans ~8 px at the coarsest level
421 int nlev = 1;
422
423 while(((int)region->radius >> (nlev - 1)) > 8 && nlev < 7) nlev++;
424
425 // coarse -> fine; each level diffuses each channel's ratio over ITS clipped mask, then the
426 // result seeds the next finer level's hole pixels. Explicit iterations travel only
427 // ~sqrt(iters) px, so the coarsest level fills the whole hole first (the "unreached interior
428 // stays magenta" fix) -- the multiscale seeding of the div(D grad r)=0 fill for large cores.
429 if(!aniso_done)
430 for(int level = nlev - 1; level >= 0; level--)
431 {
432 const int step = 1 << level;
433 const int down_w = (region_w + step - 1) / step;
434 const int down_h = (region_h + step - 1) / step;
435 const size_t down_pixels = (size_t)down_w * down_h;
436 float *const restrict dome_L = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
437 float *const restrict dome_ratio = dt_pixelpipe_cache_alloc_align_float(down_pixels * 3, pipe);
438 float *const restrict tensor_xx = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
439 float *const restrict tensor_xy = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
440 float *const restrict tensor_yy = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
441 float *const restrict tensor_scratch = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
442 float *const restrict dobs = dt_pixelpipe_cache_alloc_align_float(down_pixels * 3, pipe);
443 float *const restrict dobc = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
444 uint8_t *const restrict dhole
445 = (uint8_t *)dt_pixelpipe_cache_alloc_align(sizeof(uint8_t) * down_pixels * 3, ctx->pipe);
446 uint8_t *const restrict hplane
447 = (uint8_t *)dt_pixelpipe_cache_alloc_align(sizeof(uint8_t) * down_pixels, ctx->pipe);
448
449 if(!dome_L || !dome_ratio || !tensor_xx || !tensor_xy || !tensor_yy || !tensor_scratch || !dobs || !dobc
450 || !dhole || !hplane)
451 {
457 dt_pixelpipe_cache_free_align(tensor_scratch);
462 break;
463 }
464
465 // box-downsample: luminance = cell mean; ratio = mean over the cell (current estimate,
466 // already seeded by the coarser level); hole = majority of the cell clipped
467 __OMP_PARALLEL_FOR__(collapse(2))
468 for(int cell_y = 0; cell_y < down_h; cell_y++)
469 for(int cell_x = 0; cell_x < down_w; cell_x++)
470 {
471 double accL = 0.0;
472 double accr[3] = { 0.0, 0.0, 0.0 };
473 int n_unknowns[3] = { 0, 0, 0 };
474 int n_total = 0;
475
476 double accc[3] = { 0.0, 0.0, 0.0 };
477 for(int nb_y = cell_y * step; nb_y < MIN((cell_y + 1) * step, region_h); nb_y++)
478 for(int nb_x = cell_x * step; nb_x < MIN((cell_x + 1) * step, region_w); nb_x++)
479 {
480 const size_t fine_index = (size_t)nb_y * region_w + nb_x;
481 accL += lum_accum[fine_index];
482 n_total++;
483
484 for(int c = 0; c < 3; c++)
485 {
486 accr[c] += plane1[fine_index * 4 + c];
487 accc[c] += clip0[fine_index * 4 + c];
488 n_unknowns[c] += (vld_an[fine_index * 4 + c] < 0.5f);
489 }
490 }
491
492 const size_t cell_index = (size_t)cell_y * down_w + cell_x;
493 dome_L[cell_index] = (float)(accL / n_total);
494
495 for(int c = 0; c < 3; c++)
496 {
497 dome_ratio[cell_index * 3 + c] = (float)(accr[c] / n_total);
498 // per-cell obstacle: the saturation floor in ratio space, clip0_c / L
499 dobs[cell_index * 3 + c] = (float)(accc[c] / fmax(accL, 1e-9));
500 dhole[cell_index * 3 + c] = (2 * n_unknowns[c] > n_total) ? 1 : 0;
501 }
502 }
503
504 // structure tensor D of this level's luminance, then diffuse each channel's ratio plane
505 // under the obstacle (per-level projected relaxation of div(D grad r)=0, r >= c0/L)
506 _aniso_tensor(dome_L, tensor_xx, tensor_xy, tensor_yy, tensor_scratch, down_w, down_h);
507
508 const int box_x_lo = MAX(abx0 / step - 2, 0), box_y_lo = MAX(aby0 / step - 2, 0);
509 const int box_x_hi = MIN(abx1 / step + 2, down_w - 1), box_y_hi = MIN(aby1 / step + 2, down_h - 1);
510
511 for(int c = 0; c < 3; c++)
512 {
513 size_t n_channels = 0;
514 __OMP_PARALLEL_FOR__(reduction(+ : n_channels))
515 for(size_t cell_index = 0; cell_index < down_pixels; cell_index++)
516 {
517 dome_L[cell_index] = dome_ratio[cell_index * 3 + c]; // reuse dL as the working plane for channel c
518 dobc[cell_index] = dobs[cell_index * 3 + c];
519 hplane[cell_index] = dhole[cell_index * 3 + c];
520 n_channels += hplane[cell_index];
521 }
522
523 if(n_channels == 0) continue; // no hole cell at this level for this channel
524
525 _aniso_iterate_obs(dome_L, dobc, hplane, tensor_xx, tensor_xy, tensor_yy, tensor_scratch, down_w, down_h,
526 240, box_x_lo, box_y_lo, box_x_hi, box_y_hi);
527
529 for(size_t cell_index = 0; cell_index < down_pixels; cell_index++)
530 dome_ratio[cell_index * 3 + c] = dome_L[cell_index];
531 }
532
533 // splat this level's hole ratios back into the fine planes (bilinear prolongation),
534 // seeding the next finer level; valid fine pixels keep their true ratios (anchors)
535 __OMP_PARALLEL_FOR__(collapse(2))
536 for(int y = 0; y < region_h; y++)
537 for(int x = 0; x < region_w; x++)
538 {
539 const size_t fine_index = (size_t)y * region_w + x;
540 const float grad_x = ((float)x + 0.5f) / step - 0.5f;
541 const float grad_y = ((float)y + 0.5f) / step - 0.5f;
542 const int x_lo = CLAMP((int)floorf(grad_x), 0, down_w - 1);
543 const int y_lo = CLAMP((int)floorf(grad_y), 0, down_h - 1);
544 const int x_hi = MIN(x_lo + 1, down_w - 1);
545 const int y_hi = MIN(y_lo + 1, down_h - 1);
546 const float frac_x = CLAMP(grad_x - x_lo, 0.f, 1.f);
547 const float frac_y = CLAMP(grad_y - y_lo, 0.f, 1.f);
548
549 for(int c = 0; c < 3; c++)
550 {
551 if(vld_an[fine_index * 4 + c] >= 0.5f) continue;
552
553 const float interp_a = dome_ratio[((size_t)y_lo * down_w + x_lo) * 3 + c] * (1.f - frac_x)
554 + dome_ratio[((size_t)y_lo * down_w + x_hi) * 3 + c] * frac_x;
555 const float interp_b = dome_ratio[((size_t)y_hi * down_w + x_lo) * 3 + c] * (1.f - frac_x)
556 + dome_ratio[((size_t)y_hi * down_w + x_hi) * 3 + c] * frac_x;
557 plane1[fine_index * 4 + c] = interp_a * (1.f - frac_y) + interp_b * frac_y;
558 }
559 }
560
566 dt_pixelpipe_cache_free_align(tensor_scratch);
571 }
572
573 // Full-resolution projected polish, both solver paths (the direct solve cannot project
574 // mid-solve, and the pyramid's finest sweeps only correct locally): a short obstacle-
575 // projected relaxation at full resolution lets the field settle smoothly around the
576 // active set of the constraint.
577 if(n_aniso > 0)
578 {
579 HL_PFOR()
580 for(size_t i = 0; i < region_pixels; i++)
581 hole[i] = (vld_an[i * 4 + 0] < 0.5f && vld_an[i * 4 + 1] < 0.5f && vld_an[i * 4 + 2] < 0.5f);
582
583 // Activity gate: the polish exists to settle the field around the ACTIVE set of the
584 // obstacle. Where no all-clip pixel sits at (or below) its obstacle, the projection
585 // never fires and the 60 sweeps only re-run a diffusion the solvers already
586 // converged -- skip them. The 1.001 band catches pixels the pyramid projection left
587 // exactly ON the obstacle.
588 int act0 = 0, act1 = 0, act2 = 0;
589 HL_PFOR(reduction(| : act0, act1, act2))
590 for(size_t i = 0; i < region_pixels; i++)
591 {
592 if(!hole[i]) continue;
593 const float invL = 1.f / fmaxf(lum_accum[i], epsilon);
594 act0 |= (plane1[i * 4 + 0] <= clip0[i * 4 + 0] * invL * 1.001f);
595 act1 |= (plane1[i * 4 + 1] <= clip0[i * 4 + 1] * invL * 1.001f);
596 act2 |= (plane1[i * 4 + 2] <= clip0[i * 4 + 2] * invL * 1.001f);
597 }
598 const int active[3] = { act0, act1, act2 };
599
600 if(act0 | act1 | act2)
601 {
602 float *const restrict otxx = blur_in + 0 * region_pixels; // `in` (rn*4) is free scratch here
603 float *const restrict otxy = blur_in + 1 * region_pixels;
604 float *const restrict otyy = blur_in + 2 * region_pixels;
605 float *const restrict otsc = blur_in + 3 * region_pixels;
606 _aniso_tensor(lum_accum, otxx, otxy, otyy, otsc, region_w, region_h);
607
608 for(int c = 0; c < 3; c++)
609 {
610 if(!active[c]) continue;
611
612 HL_PFOR()
613 for(size_t i = 0; i < region_pixels; i++)
614 {
615 solver_field[i] = plane1[i * 4 + c];
616 reaction_weight[i] = clip0[i * 4 + c] / fmaxf(lum_accum[i], epsilon); // the obstacle
617 }
618
619 _aniso_iterate_obs(solver_field, reaction_weight, hole, otxx, otxy, otyy, flat_target, region_w,
620 region_h, 60, abx0, aby0, abx1, aby1);
621
622 HL_PFOR()
623 for(size_t i = 0; i < region_pixels; i++) plane1[i * 4 + c] = solver_field[i];
624 }
625 }
626 }
627
628 // reassemble. This pass only ever writes the all-clip core (vld_an flags every channel
629 // of a partially-valid pixel >= 0.6, so those pixels are anchors, settled by the
630 // coefficient-field stages): the magnitude is the dome luminance L split by the
631 // diffused ratios. (A ladder-era magnitude-transfer branch for partially-valid pixels
632 // used to live here; the anchor construction made it unreachable and it was removed.)
634 for(size_t i = 0; i < region_pixels; i++)
635 {
636 const float raccum = fmaxf(plane1[i * 4 + 0] + plane1[i * 4 + 1] + plane1[i * 4 + 2], epsilon); // sum_j r_j
637
638 for(int c = 0; c < 3; c++)
639 if(vld_an[i * 4 + c] < 0.5f)
640 {
641 const float ratio_c = fmaxf(plane1[i * 4 + c], 0.f);
642 const float value = lum_accum[i] * ratio_c / raccum; // recombine u_c = L_sum * r_c / sum_j r_j
643 // SOFT saturation floor (same rounding as the coefficient-field floor): the hard
644 // max() prints an exactly-flat shelf at the clip level plus a gradient kink
645 // wherever the magnitude transfer under-predicts a channel near its own rim
646 // inside the core (measured on DSC00078's sun: ~10 px flat at clip0_B, then a
647 // 2x-slope break).
648 // soft saturation floor u_c <- c0 + 0.5*((u-c0) + sqrt((u-c0)^2 + w^2)), w = 0.02*c0
649 // (article rule 3 / step 5 soft-max): a smooth max(u, c0) with no shelf-and-kink
650 const float clip_floor_c = clip0[i * 4 + c];
651 const float delta = value - clip_floor_c;
652 const float weight = 0.02f * fmaxf(clip_floor_c, 1e-6f);
653 estimate[i * 4 + c] = clip_floor_c + 0.5f * (delta + sqrtf(delta * delta + weight * weight));
654 }
655 }
656 }
657}
658
659// ============================ OpenCL ============================
660
661#if defined(HAVE_OPENCL) && DT_HL_SPARSE_SOLVE && (DT_HL_ANISO_SOLVER == 2)
662
663// Explicit coarse-to-fine structure-steered diffusion on the device, mirroring the CPU
664// pyramid (_aniso_tensor + _aniso_iterate in the DT_HL_ANISO_CHROMA block) that handles cores
665// beyond DT_HL_SPARSE_MAX unknowns: each level box-downsamples the brightness/ratios/holes,
666// rebuilds the structure tensor (local edge direction and strength), runs 240 damped stencil
667// steps per channel over the hole bounding box (ping-pong buffers instead of the CPU's
668// write-back copy), and bilinearly splats the ratios into the fine planes' clipped channels
669// to seed the next level. Any change here must be mirrored in the CPU pyramid and
670// re-validated with the HL_ANISOCL_TEST self-test (_aniso_stage_cl_selftest).
671//
672// MATHS BRIDGE -- Step 8 large-core path (article §"The update rules", the explicit trace-form
673// pyramid): the multiscale solver for min int grad(r)^T D grad(r) s.t. r >= c0/L when the core
674// exceeds DT_HL_SPARSE_MAX. Each level projects onto the obstacle then runs 240 explicit steps of
675// r <- max(r + 0.18*tr(D Hess r), c0/L) (kernel hl_aniso_iter[_block]); coarsest level first so
676// the whole hole is seeded before refinement. D = structure tensor of the recovered luminance.
677static cl_int _aniso_pyramid_cl(const int devid, void *gd_void, cl_mem ratios, cl_mem valid, cl_mem luminance,
678 cl_mem clip0, const int region_w, const int region_h, const float radius,
679 const int box_x_lo, const int box_y_lo, const int box_x_hi, const int box_y_hi,
680 const dt_dev_pixelpipe_t *pipe)
681{
683 cl_int cl_err = CL_SUCCESS;
684
685 int n_levels = 1;
686 while(((int)radius >> (n_levels - 1)) > 8 && n_levels < 7) n_levels++;
687
688 for(int level = n_levels - 1; level >= 0 && cl_err == CL_SUCCESS; level--)
689 {
690 const int step = 1 << level;
691 const int coarse_w = (region_w + step - 1) / step;
692 const int coarse_h = (region_h + step - 1) / step;
693 const size_t coarse_pixels = (size_t)coarse_w * coarse_h;
694 size_t size_coarse[3] = { ROUNDUPDWD(coarse_w, devid), ROUNDUPDHT(coarse_h, devid), 1 };
695 size_t size_full[3] = { ROUNDUPDWD(region_w, devid), ROUNDUPDHT(region_h, devid), 1 };
696
697 cl_mem coarse_lum = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
698 cl_mem coarse_ratios = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels * 3);
699 cl_mem coarse_obstacle = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels * 3);
700 cl_mem coarse_hole = dt_opencl_alloc_device_buffer(devid, coarse_pixels * 3);
701 cl_mem grad_x = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
702 cl_mem tensor_xx = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
703 cl_mem grad_y = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
704 cl_mem tensor_xy = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
705 cl_mem tensor_yy = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
706 cl_mem diffuse_a = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
707 cl_mem diffuse_b = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
708 cl_mem grad_partials = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 256);
709 if(!coarse_lum || !coarse_ratios || !coarse_obstacle || !coarse_hole || !grad_x || !tensor_xx || !grad_y
710 || !tensor_xy || !tensor_yy || !diffuse_a || !diffuse_b || !grad_partials)
712
713 if(cl_err == CL_SUCCESS)
714 {
715 const int kernel = global_data->kernel_hl_aniso_pyr_down;
716 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &luminance);
717 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &ratios);
718 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &valid);
719 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &clip0);
720 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &coarse_lum);
721 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &coarse_ratios);
722 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &coarse_obstacle);
723 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), &coarse_hole);
724 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &region_w);
725 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &region_h);
726 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &coarse_w);
727 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &coarse_h);
728 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &step);
729 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
730 }
731
732 // structure tensor of this level's luminance (box3 x2, gradient + mean magnitude, D)
733 if(cl_err == CL_SUCCESS)
734 {
735 const int kernel = global_data->kernel_hl_box3;
736 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &coarse_lum);
737 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &grad_x);
738 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &coarse_w);
739 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &coarse_h);
740 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
741 if(cl_err == CL_SUCCESS)
742 {
743 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &grad_x);
744 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &tensor_xx);
745 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
746 }
747 }
748 if(cl_err == CL_SUCCESS)
749 {
750 const int local_size = 64, n_groups = 256;
751 const int kernel = global_data->kernel_hl_grad_reduce;
752 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
753 size_t local[3] = { local_size, 1, 1 };
754 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &tensor_xx);
755 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &grad_x); // gx
756 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &grad_y); // gy
757 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &grad_partials);
758 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &coarse_w);
759 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &coarse_h);
760 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float) * local_size, NULL);
761 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
762 if(cl_err == CL_SUCCESS)
763 {
764 float partial_sums[256];
765 cl_err = dt_opencl_read_buffer_from_device(devid, partial_sums, grad_partials, 0, sizeof(float) * n_groups,
766 CL_TRUE);
767 if(cl_err == CL_SUCCESS)
768 {
769 double grad_sum = 0.0;
770 for(int group = 0; group < n_groups; group++) grad_sum += (double)partial_sums[group];
771 const float grad_mean = fmaxf((float)(grad_sum / (double)coarse_pixels), 1e-9f);
772 const int kernel_tensor = global_data->kernel_hl_aniso_tensor;
773 dt_opencl_set_kernel_arg(devid, kernel_tensor, 0, sizeof(cl_mem), &grad_x);
774 dt_opencl_set_kernel_arg(devid, kernel_tensor, 1, sizeof(cl_mem), &grad_y);
775 dt_opencl_set_kernel_arg(devid, kernel_tensor, 2, sizeof(cl_mem), &tensor_xx); // txx
776 dt_opencl_set_kernel_arg(devid, kernel_tensor, 3, sizeof(cl_mem), &tensor_xy); // txy
777 dt_opencl_set_kernel_arg(devid, kernel_tensor, 4, sizeof(cl_mem), &tensor_yy);
778 dt_opencl_set_kernel_arg(devid, kernel_tensor, 5, sizeof(int), &coarse_w);
779 dt_opencl_set_kernel_arg(devid, kernel_tensor, 6, sizeof(int), &coarse_h);
780 dt_opencl_set_kernel_arg(devid, kernel_tensor, 7, sizeof(float), &grad_mean);
781 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel_tensor, size_coarse);
782 }
783 }
784 }
785
786 // per-channel 240-step diffusion over the level's hole bbox
787 if(cl_err == CL_SUCCESS)
788 {
789 const int level_x_lo = MAX(box_x_lo / step - 2, 0), level_y_lo = MAX(box_y_lo / step - 2, 0);
790 const int level_x_hi = MIN(box_x_hi / step + 2, coarse_w - 1),
791 level_y_hi = MIN(box_y_hi / step + 2, coarse_h - 1);
792 size_t size_box[3]
793 = { ROUNDUPDWD(level_x_hi - level_x_lo + 1, devid), ROUNDUPDHT(level_y_hi - level_y_lo + 1, devid), 1 };
794
795 for(int c = 0; c < 3 && cl_err == CL_SUCCESS; c++)
796 {
797 {
798 const int kernel = global_data->kernel_hl_pyr_getc;
799 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &coarse_ratios);
800 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &diffuse_a);
801 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &coarse_w);
802 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &coarse_h);
803 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
804 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
805 }
806 if(cl_err == CL_SUCCESS)
807 {
808 // seed projection onto the obstacle (mirrors the CPU _aniso_iterate_obs entry clamp)
809 const int kernel = global_data->kernel_hl_pyr_project;
810 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &diffuse_a);
811 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &coarse_obstacle);
812 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &coarse_hole);
813 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &coarse_w);
814 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &coarse_h);
815 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &c);
816 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
817 }
818 if(cl_err == CL_SUCCESS)
819 cl_err = dt_opencl_enqueue_copy_buffer_to_buffer(devid, diffuse_a, diffuse_b, 0, 0,
820 sizeof(float) * coarse_pixels);
821
822 cl_mem current_buf = diffuse_a, other_buf = diffuse_b;
823 if((level_x_hi - level_x_lo + 1) * (level_y_hi - level_y_lo + 1) <= 4096)
824 {
825 // all 240 steps in one single-workgroup launch (bit-identical, see the fill)
826 const int kernel = global_data->kernel_hl_aniso_iter_block;
827 const int iters = 240;
828 size_t size_block[3] = { 256, 1, 1 };
829 size_t local_block[3] = { 256, 1, 1 };
830 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &diffuse_a);
831 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &diffuse_b);
832 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &coarse_hole);
833 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &tensor_xx);
834 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &tensor_xy);
835 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &tensor_yy);
836 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &coarse_obstacle);
837 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &coarse_w);
838 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &coarse_h);
839 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &c);
840 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &level_x_lo);
841 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &level_y_lo);
842 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &level_x_hi);
843 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(int), &level_y_hi);
844 dt_opencl_set_kernel_arg(devid, kernel, 14, sizeof(int), &iters);
845 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, size_block, local_block);
846 }
847 else
848 for(int iter = 0; iter < 240 && cl_err == CL_SUCCESS; iter++)
849 {
850 const int kernel = global_data->kernel_hl_aniso_iter;
851 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &current_buf);
852 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &other_buf);
853 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &coarse_hole);
854 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &tensor_xx);
855 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &tensor_xy);
856 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &tensor_yy);
857 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &coarse_obstacle);
858 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &coarse_w);
859 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &coarse_h);
860 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &c);
861 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &level_x_lo);
862 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &level_y_lo);
863 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &level_x_hi);
864 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(int), &level_y_hi);
865 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_box);
866 cl_mem swap_buf = current_buf;
867 current_buf = other_buf;
868 other_buf = swap_buf;
869 }
870 if(cl_err == CL_SUCCESS)
871 {
872 const int kernel = global_data->kernel_hl_pyr_putc;
873 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &current_buf);
874 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &coarse_ratios);
875 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &coarse_w);
876 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &coarse_h);
877 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
878 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
879 }
880 }
881 }
882
883 if(cl_err == CL_SUCCESS)
884 {
885 const int kernel = global_data->kernel_hl_aniso_splat;
886 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &ratios);
887 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
888 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &coarse_ratios);
889 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
890 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
891 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &coarse_w);
892 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &coarse_h);
893 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &step);
894 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_full);
895 }
896
898 dt_opencl_release_mem_object(coarse_ratios);
899 dt_opencl_release_mem_object(coarse_obstacle);
900 dt_opencl_release_mem_object(coarse_hole);
908 dt_opencl_release_mem_object(grad_partials);
909 }
910 return cl_err;
911}
912
913cl_int _aniso_stage_cl(const int devid, void *gd_void, cl_mem estimate, cl_mem valid, cl_mem clip0,
914 const int region_w, const int region_h, const float radius, const dt_dev_pixelpipe_t *pipe)
915{
917 const size_t region_pixels = (size_t)region_w * region_h;
918 cl_int cl_err = DT_OPENCL_DEFAULT_ERROR;
919 size_t size[3] = { ROUNDUPDWD(region_w, devid), ROUNDUPDHT(region_h, devid), 1 };
920 const float epsilon = 1e-6f;
921
922 if(global_data->kernel_hl_aniso_rhs < 0 || global_data->kernel_hl_aniso_scatter < 0) return cl_err; // no fp64
923
924 cl_mem valid_packed = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 4);
925 cl_mem luminance = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
926 cl_mem ratios = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 4);
927 cl_mem hole = dt_opencl_alloc_device_buffer(devid, region_pixels);
928 cl_mem scratch1 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
929 cl_mem scratch2 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
930 cl_mem tensor_xx = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
931 cl_mem tensor_xy = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
932 cl_mem tensor_yy = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
933 cl_mem partials = NULL, perm_grid_dev = NULL, edge_weights_dev = NULL, rhs_dev = NULL;
934 uint8_t *hole_mask = (uint8_t *)dt_pixelpipe_cache_alloc_align(region_pixels, pipe);
935 int *grid_to_unknown = NULL, *unknown_to_grid = NULL, *unknown_x = NULL, *unknown_y = NULL, *perm = NULL,
936 *inverse_perm = NULL;
937 int *matrix_col_ptr = NULL, *matrix_row_index = NULL, *perm_grid = NULL;
938 double *matrix_values = NULL;
939 float *edge_weights = NULL;
940 _sp_chol_cl_t *factor = NULL;
941 if(!valid_packed || !luminance || !ratios || !hole || !scratch1 || !scratch2 || !tensor_xx || !tensor_xy
942 || !tensor_yy || !hole_mask)
943 goto out;
944
945 // validity mask + luminance + ratio planes + all-clip hole in one sweep
946 {
947 const int kernel = global_data->kernel_hl_aniso_prep;
948 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
949 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
950 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &valid_packed);
951 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &luminance);
952 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &ratios);
953 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &hole);
954 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_w);
955 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_h);
956 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(float), &epsilon);
957 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
958 if(cl_err != CL_SUCCESS) goto out;
959 }
960
961 cl_err = dt_opencl_read_buffer_from_device(devid, hole_mask, hole, 0, region_pixels, CL_TRUE);
962 if(cl_err != CL_SUCCESS) goto out;
963
964 int n_unknowns = 0;
965 for(size_t i = 0; i < region_pixels; i++)
966 if(hole_mask[i]) n_unknowns++;
967 if(n_unknowns == 0)
968 {
969 cl_err = CL_SUCCESS; // nothing to diffuse
970 goto out;
971 }
972 int box_x_lo = region_w, box_y_lo = region_h, box_x_hi = -1, box_y_hi = -1;
973 for(int y = 0; y < region_h; y++)
974 for(int x = 0; x < region_w; x++)
975 if(hole_mask[(size_t)y * region_w + x])
976 {
977 box_x_lo = MIN(box_x_lo, x);
978 box_x_hi = MAX(box_x_hi, x);
979 box_y_lo = MIN(box_y_lo, y);
980 box_y_hi = MAX(box_y_hi, y);
981 }
982
983 if(n_unknowns > DT_HL_SPARSE_MAX)
984 {
985 // beyond the direct solve: the explicit coarse-to-fine pyramid, like the CPU
986 cl_err = _aniso_pyramid_cl(devid, gd_void, ratios, valid_packed, luminance, clip0, region_w, region_h, radius,
987 box_x_lo, box_y_lo, box_x_hi, box_y_hi, pipe);
988 if(cl_err != CL_SUCCESS) goto out;
989 goto reassemble;
990 }
991
992 // structure tensor of the recovered luminance
993 {
994 const int kernel = global_data->kernel_hl_box3;
995 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &luminance);
996 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch1);
997 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
998 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
999 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1000 if(cl_err != CL_SUCCESS) goto out;
1001 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch1);
1002 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch2);
1003 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1004 if(cl_err != CL_SUCCESS) goto out;
1005 }
1006 {
1007 const int local_size = 64, n_groups = 256;
1008 partials = dt_opencl_alloc_device_buffer(devid, sizeof(float) * n_groups);
1009 if(!partials)
1010 {
1011 cl_err = DT_OPENCL_DEFAULT_ERROR;
1012 goto out;
1013 }
1014 const int kernel = global_data->kernel_hl_grad_reduce;
1015 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
1016 size_t local[3] = { local_size, 1, 1 };
1017 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch2);
1018 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &tensor_xx); // gx stash
1019 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &tensor_xy); // grad_y stash
1020 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &partials);
1021 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
1022 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
1023 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float) * local_size, NULL);
1024 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
1025 if(cl_err != CL_SUCCESS) goto out;
1026
1027 float psum[256];
1028 cl_err = dt_opencl_read_buffer_from_device(devid, psum, partials, 0, sizeof(float) * n_groups, CL_TRUE);
1029 if(cl_err != CL_SUCCESS) goto out;
1030 double gsum = 0.0;
1031 for(int group_index = 0; group_index < n_groups; group_index++) gsum += (double)psum[group_index];
1032 const float gnorm = fmaxf((float)(gsum / (double)region_pixels), 1e-9f);
1033
1034 const int kernel_tensor = global_data->kernel_hl_aniso_tensor;
1035 dt_opencl_set_kernel_arg(devid, kernel_tensor, 0, sizeof(cl_mem), &tensor_xx);
1036 dt_opencl_set_kernel_arg(devid, kernel_tensor, 1, sizeof(cl_mem), &tensor_xy);
1037 dt_opencl_set_kernel_arg(devid, kernel_tensor, 2, sizeof(cl_mem), &scratch1); // tensor_xx out (reuse)
1038 dt_opencl_set_kernel_arg(devid, kernel_tensor, 3, sizeof(cl_mem), &scratch2); // tensor_xy out (reuse)
1039 dt_opencl_set_kernel_arg(devid, kernel_tensor, 4, sizeof(cl_mem), &tensor_yy);
1040 dt_opencl_set_kernel_arg(devid, kernel_tensor, 5, sizeof(int), &region_w);
1041 dt_opencl_set_kernel_arg(devid, kernel_tensor, 6, sizeof(int), &region_h);
1042 dt_opencl_set_kernel_arg(devid, kernel_tensor, 7, sizeof(float), &gnorm);
1043 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel_tensor, size);
1044 if(cl_err != CL_SUCCESS) goto out;
1045 }
1046 // tensor now lives in (scratch1, scratch2, tensor_yy) = (tensor_xx, tensor_xy, tensor_yy)
1047
1048 // host symbolic: unknown list + ND ordering (reach 1: 8-neighbour stencil)
1049 grid_to_unknown = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * region_pixels, pipe);
1050 unknown_to_grid = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1051 unknown_x = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1052 unknown_y = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1053 perm = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1054 inverse_perm = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1055 matrix_col_ptr = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * (n_unknowns + 1), pipe);
1056 perm_grid = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1057 edge_weights = (float *)dt_pixelpipe_cache_alloc_align(sizeof(float) * (size_t)n_unknowns * 8, pipe);
1058 if(!grid_to_unknown || !unknown_to_grid || !unknown_x || !unknown_y || !perm || !inverse_perm || !matrix_col_ptr
1059 || !perm_grid || !edge_weights)
1060 {
1061 cl_err = DT_OPENCL_DEFAULT_ERROR;
1062 goto out;
1063 }
1064 {
1065 int unknown_index = 0;
1066 for(size_t i = 0; i < region_pixels; i++)
1067 {
1068 grid_to_unknown[i] = hole_mask[i] ? unknown_index : -1;
1069 if(hole_mask[i])
1070 {
1071 unknown_to_grid[unknown_index] = (int)i;
1072 unknown_y[unknown_index] = (int)(i / region_w);
1073 unknown_x[unknown_index] = (int)(i - (size_t)unknown_y[unknown_index] * region_w);
1074 unknown_index++;
1075 }
1076 }
1077 for(int i = 0; i < n_unknowns; i++) perm[i] = i;
1078 _sp_nd_order(perm, n_unknowns, unknown_x, unknown_y, 1);
1079 for(int perm_index = 0; perm_index < n_unknowns; perm_index++) inverse_perm[perm[perm_index]] = perm_index;
1080 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
1081 perm_grid[perm_index] = unknown_to_grid[perm[perm_index]];
1082 }
1083
1084 // edge weights on the device (they steer the RHS kernels too), compact download for assembly
1085 perm_grid_dev = _sp_cl_upload(devid, perm_grid, sizeof(int) * n_unknowns);
1086 edge_weights_dev = dt_opencl_alloc_device_buffer(devid, sizeof(float) * (size_t)n_unknowns * 8);
1087 rhs_dev = dt_opencl_alloc_device_buffer(devid, sizeof(double) * n_unknowns);
1088 if(!perm_grid_dev || !edge_weights_dev || !rhs_dev)
1089 {
1090 cl_err = DT_OPENCL_DEFAULT_ERROR;
1091 goto out;
1092 }
1093 {
1094 const int kernel = global_data->kernel_hl_aniso_weights;
1095 size_t size_1d[3] = { ROUNDUP(n_unknowns, 64), 1, 1 };
1096 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch1);
1097 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch2);
1098 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &tensor_yy);
1099 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &perm_grid_dev);
1100 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &edge_weights_dev);
1101 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &n_unknowns);
1102 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_w);
1103 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_h);
1104 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_1d);
1105 if(cl_err != CL_SUCCESS) goto out;
1106 }
1107 cl_err = dt_opencl_read_buffer_from_device(devid, edge_weights, edge_weights_dev, 0,
1108 sizeof(float) * (size_t)n_unknowns * 8, CL_TRUE);
1109 if(cl_err != CL_SUCCESS) goto out;
1110
1111 // host assembly from the downloaded weights, exactly the CPU _aniso_div_solve pattern
1112 {
1113 static const int neighbour_dy[8] = { 0, 0, -1, 1, -1, 1, -1, 1 };
1114 static const int neighbour_dx[8] = { -1, 1, 0, 0, -1, 1, 1, -1 };
1115 int success = 1;
1116 for(int pass = 0; pass < 2 && success; pass++)
1117 {
1118 if(pass == 1)
1119 {
1120 int total = 0;
1121 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
1122 {
1123 const int c = matrix_col_ptr[perm_index];
1124 matrix_col_ptr[perm_index] = total;
1125 total += c;
1126 }
1127 matrix_col_ptr[n_unknowns] = total;
1128 matrix_row_index = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * total, pipe);
1129 matrix_values = (double *)dt_pixelpipe_cache_alloc_align(sizeof(double) * total, pipe);
1130 if(!matrix_row_index || !matrix_values) success = 0;
1131 }
1132
1133 for(int perm_index = 0; perm_index < n_unknowns && success; perm_index++)
1134 {
1135 const int origin_grid = perm_grid[perm_index];
1136 const int origin_y = origin_grid / region_w, origin_x = origin_grid - origin_y * region_w;
1137 double diag = 0.0;
1138 int n_col_entries = 0;
1139
1140 for(int edge = 0; edge < 8; edge++)
1141 {
1142 const float weight_value = edge_weights[(size_t)perm_index * 8 + edge];
1143 // NaN-safe: !(weight_value > 0) also skips NaN weights (NaN pixels survive the blurs), which
1144 // 'weight_value <= 0' would let through into a wildly out-of-bounds grid_to_unknown read below
1145 if(!(weight_value > 0.f)) continue; // outside the border, a zeroed diagonal, or NaN
1146 const int neighbour_x = origin_x + neighbour_dx[edge], neighbour_y = origin_y + neighbour_dy[edge];
1147 if(neighbour_x < 0 || neighbour_y < 0 || neighbour_x >= region_w || neighbour_y >= region_h)
1148 continue; // same guard as the CPU
1149 diag += weight_value;
1150 const size_t j = (size_t)neighbour_y * region_w + neighbour_x;
1151 if(grid_to_unknown[j] >= 0)
1152 {
1153 const int target_row = inverse_perm[grid_to_unknown[j]];
1154 if(target_row < perm_index)
1155 {
1156 if(pass == 1)
1157 {
1158 matrix_row_index[matrix_col_ptr[perm_index] + n_col_entries] = target_row;
1159 matrix_values[matrix_col_ptr[perm_index] + n_col_entries] = -(double)weight_value;
1160 }
1161 n_col_entries++;
1162 }
1163 }
1164 }
1165 if(pass == 1)
1166 {
1167 matrix_row_index[matrix_col_ptr[perm_index] + n_col_entries] = perm_index;
1168 matrix_values[matrix_col_ptr[perm_index] + n_col_entries] = diag;
1169 }
1170 n_col_entries++;
1171 if(pass == 0) matrix_col_ptr[perm_index] = n_col_entries;
1172 }
1173 }
1174 if(!success)
1175 {
1176 cl_err = DT_OPENCL_DEFAULT_ERROR;
1177 goto out;
1178 }
1179 }
1180
1181 factor = _sp_chol_factor_cl(devid, _hl_sp_chol_kernels(gd_void), n_unknowns, matrix_col_ptr, matrix_row_index,
1182 matrix_values);
1183 if(!factor)
1184 {
1185 cl_err = DT_OPENCL_DEFAULT_ERROR;
1186 goto out;
1187 }
1188
1189 for(int c = 0; c < 3; c++)
1190 {
1191 {
1192 const int kernel = global_data->kernel_hl_aniso_rhs;
1193 size_t size_1d[3] = { ROUNDUP(n_unknowns, 64), 1, 1 };
1194 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &edge_weights_dev);
1195 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid_packed);
1196 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &ratios);
1197 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &perm_grid_dev);
1198 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &rhs_dev);
1199 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &n_unknowns);
1200 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_w);
1201 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_h);
1202 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &c);
1203 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_1d);
1204 if(cl_err != CL_SUCCESS) goto out;
1205 }
1206 if(_sp_chol_solve_cl(factor, _hl_sp_chol_kernels(gd_void), rhs_dev))
1207 {
1208 cl_err = DT_OPENCL_DEFAULT_ERROR;
1209 goto out;
1210 }
1211 {
1212 const int kernel = global_data->kernel_hl_aniso_scatter;
1213 size_t size_1d[3] = { ROUNDUP(n_unknowns, 64), 1, 1 };
1214 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &rhs_dev);
1215 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &perm_grid_dev);
1216 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &ratios);
1217 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &n_unknowns);
1218 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
1219 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_1d);
1220 if(cl_err != CL_SUCCESS) goto out;
1221 }
1222 }
1223
1224reassemble:;
1225 // Full-resolution projected polish, both solver paths (mirrors the CPU block): the
1226 // saturation floors active as an obstacle inside a short structure-steered relaxation, so
1227 // the field settles smoothly around the constraint instead of being clamped pointwise
1228 // at the reassembly.
1229 {
1230 cl_mem grad_y = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1231 cl_mem dobs3 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 3);
1232 cl_mem dhole3 = dt_opencl_alloc_device_buffer(devid, region_pixels * 3);
1233 cl_mem diffuse_a = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1234 cl_mem diffuse_b = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1235 cl_mem ppart = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 256);
1236 cl_mem aflags = dt_opencl_alloc_device_buffer(devid, sizeof(int) * 3);
1237 if(!grad_y || !dobs3 || !dhole3 || !diffuse_a || !diffuse_b || !ppart || !aflags)
1238 cl_err = DT_OPENCL_DEFAULT_ERROR;
1239
1240 // full-res structure tensor of the recovered luminance (box3 x2, gradient, D)
1241 if(cl_err == CL_SUCCESS)
1242 {
1243 const int kernel = global_data->kernel_hl_box3;
1244 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &luminance);
1245 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch1);
1246 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
1247 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
1248 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1249 if(cl_err == CL_SUCCESS)
1250 {
1251 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch1);
1252 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch2);
1253 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1254 }
1255 }
1256 if(cl_err == CL_SUCCESS)
1257 {
1258 const int local_size = 64, n_groups = 256;
1259 const int kernel = global_data->kernel_hl_grad_reduce;
1260 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
1261 size_t local[3] = { local_size, 1, 1 };
1262 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch2);
1263 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch1); // gx
1264 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &grad_y); // grad_y
1265 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &ppart);
1266 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
1267 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
1268 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float) * local_size, NULL);
1269 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
1270 if(cl_err == CL_SUCCESS)
1271 {
1272 float partial_sums[256];
1273 cl_err = dt_opencl_read_buffer_from_device(devid, partial_sums, ppart, 0, sizeof(float) * 256, CL_TRUE);
1274 if(cl_err == CL_SUCCESS)
1275 {
1276 double gsum = 0.0;
1277 for(int group_index = 0; group_index < 256; group_index++) gsum += (double)partial_sums[group_index];
1278 const float gnorm = fmaxf((float)(gsum / (double)region_pixels), 1e-9f);
1279 const int kernel_tensor = global_data->kernel_hl_aniso_tensor;
1280 dt_opencl_set_kernel_arg(devid, kernel_tensor, 0, sizeof(cl_mem), &scratch1);
1281 dt_opencl_set_kernel_arg(devid, kernel_tensor, 1, sizeof(cl_mem), &grad_y);
1282 dt_opencl_set_kernel_arg(devid, kernel_tensor, 2, sizeof(cl_mem), &tensor_xx);
1283 dt_opencl_set_kernel_arg(devid, kernel_tensor, 3, sizeof(cl_mem), &tensor_xy);
1284 dt_opencl_set_kernel_arg(devid, kernel_tensor, 4, sizeof(cl_mem), &tensor_yy);
1285 dt_opencl_set_kernel_arg(devid, kernel_tensor, 5, sizeof(int), &region_w);
1286 dt_opencl_set_kernel_arg(devid, kernel_tensor, 6, sizeof(int), &region_h);
1287 dt_opencl_set_kernel_arg(devid, kernel_tensor, 7, sizeof(float), &gnorm);
1288 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel_tensor, size);
1289 }
1290 }
1291 }
1292 if(cl_err == CL_SUCCESS)
1293 {
1294 const int kernel = global_data->kernel_hl_aniso_obs_full;
1295 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &valid_packed);
1296 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &clip0);
1297 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &luminance);
1298 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &dobs3);
1299 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &dhole3);
1300 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_w);
1301 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_h);
1302 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), &epsilon);
1303 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1304 }
1305
1306 // Activity gate (mirrors the CPU block): a channel whose obstacle can never fire skips
1307 // its 60 full-res sweeps entirely -- the field is already settled by the solvers.
1308 int active[3] = { 0, 0, 0 };
1309 if(cl_err == CL_SUCCESS)
1310 {
1311 cl_err = dt_opencl_write_buffer_to_device(devid, active, aflags, 0, sizeof(int) * 3, CL_TRUE);
1312 if(cl_err == CL_SUCCESS)
1313 {
1314 const int kernel = global_data->kernel_hl_aniso_obs_flags;
1315 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &ratios);
1316 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &dobs3);
1317 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &dhole3);
1318 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &aflags);
1319 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
1320 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
1321 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1322 }
1323 if(cl_err == CL_SUCCESS)
1324 cl_err = dt_opencl_read_buffer_from_device(devid, active, aflags, 0, sizeof(int) * 3, CL_TRUE);
1325 }
1326
1327 size_t size_box[3]
1328 = { ROUNDUPDWD(box_x_hi - box_x_lo + 1, devid), ROUNDUPDHT(box_y_hi - box_y_lo + 1, devid), 1 };
1329 for(int c = 0; c < 3 && cl_err == CL_SUCCESS; c++)
1330 {
1331 if(!active[c]) continue;
1332 {
1333 const int kernel = global_data->kernel_hl_pyr_getc4;
1334 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &ratios);
1335 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &diffuse_a);
1336 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
1337 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
1338 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
1339 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1340 }
1341 if(cl_err == CL_SUCCESS)
1342 {
1343 const int kernel = global_data->kernel_hl_pyr_project;
1344 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &diffuse_a);
1345 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &dobs3);
1346 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &dhole3);
1347 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
1348 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
1349 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &c);
1350 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1351 }
1352 if(cl_err == CL_SUCCESS)
1353 cl_err = dt_opencl_enqueue_copy_buffer_to_buffer(devid, diffuse_a, diffuse_b, 0, 0,
1354 sizeof(float) * region_pixels);
1355
1356 cl_mem current_buf = diffuse_a, other_buf = diffuse_b;
1357 for(int iter = 0; iter < 60 && cl_err == CL_SUCCESS; iter++)
1358 {
1359 const int kernel = global_data->kernel_hl_aniso_iter;
1360 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &current_buf);
1361 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &other_buf);
1362 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &dhole3);
1363 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &tensor_xx);
1364 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &tensor_xy);
1365 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &tensor_yy);
1366 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &dobs3);
1367 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_w);
1368 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &region_h);
1369 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &c);
1370 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &box_x_lo);
1371 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &box_y_lo);
1372 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &box_x_hi);
1373 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(int), &box_y_hi);
1374 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_box);
1375 cl_mem swap_buf = current_buf;
1376 current_buf = other_buf;
1377 other_buf = swap_buf;
1378 }
1379 if(cl_err == CL_SUCCESS)
1380 {
1381 const int kernel = global_data->kernel_hl_pyr_putc4;
1382 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &current_buf);
1383 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &ratios);
1384 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
1385 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
1386 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
1387 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1388 }
1389 }
1390
1398 if(cl_err != CL_SUCCESS) goto out;
1399 }
1400
1401 {
1402 const int kernel = global_data->kernel_hl_aniso_reassemble;
1403 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
1404 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid_packed);
1405 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &luminance);
1406 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &ratios);
1407 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &clip0);
1408 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_w);
1409 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_h);
1410 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), &epsilon);
1411 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1412 }
1413
1414out:
1415 dt_opencl_release_mem_object(valid_packed);
1425 dt_opencl_release_mem_object(perm_grid_dev);
1426 dt_opencl_release_mem_object(edge_weights_dev);
1429 dt_pixelpipe_cache_free_align(grid_to_unknown);
1430 dt_pixelpipe_cache_free_align(unknown_to_grid);
1434 dt_pixelpipe_cache_free_align(inverse_perm);
1435 dt_pixelpipe_cache_free_align(matrix_col_ptr);
1436 dt_pixelpipe_cache_free_align(matrix_row_index);
1437 dt_pixelpipe_cache_free_align(matrix_values);
1439 dt_pixelpipe_cache_free_align(edge_weights);
1441 return cl_err;
1442}
1443
1444#endif // HAVE_OPENCL && DT_HL_SPARSE_SOLVE && ANISO_SOLVER 2
int _aniso_div_solve(float *const restrict ratios, const float *const restrict valid, const float *const restrict luminance, float *const restrict scratch_planes, const int region_w, const int region_h, const dt_dev_pixelpipe_t *pipe)
Definition chroma.c:155
__DT_CLONE_TARGETS__ void _aniso_iterate_obs(float *const restrict field, const float *const restrict obstacle, const uint8_t *const restrict hole, const float *const restrict tensor_xx, const float *const restrict tensor_xy, const float *const restrict tensor_yy, float *const restrict tmp, const int region_w, const int region_h, const int iters, const int box_x_lo, const int box_y_lo, const int box_x_hi, const int box_y_hi)
Definition chroma.c:102
__DT_CLONE_TARGETS__ void _aniso_tensor(const float *const restrict luminance, float *const restrict tensor_xx, float *const restrict tensor_xy, float *const restrict tensor_yy, float *const restrict scratch, const int region_w, const int region_h)
Definition chroma.c:31
__DT_CLONE_TARGETS__ void _aniso_chroma(_hl_region_ctx_t *const ctx)
Definition chroma.c:322
static float _aniso_edge_w(const float *const restrict tensor_xx, const float *const restrict tensor_xy, const float *const restrict tensor_yy, const size_t i, const size_t j, const int offset_x, const int offset_y)
Definition chroma.h:54
const dt_colormatrix_t dt_aligned_pixel_t out
const float delta
#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 __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
Definition eaw.c:30
static int perm[512]
Definition grain.c:174
static int permutation[]
Definition grain.c:160
static float kernel(const float *x, const float *y)
static const float x
float *const restrict luminance
size_t size
Definition mipmap_cache.c:3
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
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
int dt_opencl_enqueue_copy_buffer_to_buffer(const int devid, cl_mem src_buffer, cl_mem dst_buffer, size_t srcoffset, size_t dstoffset, size_t size)
Definition opencl.c:2324
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2415
#define DT_OPENCL_DEFAULT_ERROR
Definition opencl.h:57
#define ROUNDUP(a, n)
Definition opencl.h:78
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
static _sp_chol_cl_kernels_t _hl_sp_chol_kernels(void *gd_void)
Definition pde.h:107
const float factor
Definition pdf.h:90
static void _sp_nd_order(int *const restrict unknown_ids, const int count, const int *const restrict unknown_x, const int *const restrict unknown_y, const int reach)
static _sp_chol_t * _sp_chol_factor(const int dimension, const int *const restrict matrix_col_ptr, const int *const restrict matrix_row_index, const double *const restrict matrix_values, const dt_dev_pixelpipe_t *pipe)
static void _sp_chol_free(_sp_chol_t *factor)
static void _sp_chol_solve(const _sp_chol_t *const factor, double *const restrict rhs)
static void _sp_chol_cl_free(_sp_chol_cl_t *factor)
static _sp_chol_cl_t * _sp_chol_factor_cl(const int devid, const _sp_chol_cl_kernels_t kernels, const int dimension, const int *const restrict matrix_col_ptr, const int *const restrict matrix_row_index, const double *const restrict matrix_values)
static int _sp_chol_solve_cl(const _sp_chol_cl_t *const factor, const _sp_chol_cl_kernels_t kernels, cl_mem rhs)
static cl_mem _sp_cl_upload(const int devid, const void *data, const size_t bytes)
#define DT_HL_SPARSE_MAX
#define HL_PFOR(...)
const _hl_region_t * region
const dt_dev_pixelpipe_t * pipe
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29