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 "system/openmp.h"
23#include "system/simd.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 const float react, const float react_target)
108{
109 // project the seed once so the first sweep already sees an admissible field
110 // (r <- max(r, obstacle), obstacle = c0/L in ratio space -- the saturation floor)
111 __OMP_PARALLEL_FOR__(collapse(2))
112 for(int y = box_y_lo; y <= box_y_hi; y++)
113 for(int x = box_x_lo; x <= box_x_hi; x++)
114 {
115 const size_t i = (size_t)y * region_w + x;
116 if(hole[i]) field[i] = fmaxf(field[i], obstacle[i]);
117 }
118
119 for(int iter = 0; iter < iters; iter++)
120 {
121 __OMP_PARALLEL_FOR__(collapse(2))
122 for(int y = box_y_lo; y <= box_y_hi; y++)
123 for(int x = box_x_lo; x <= box_x_hi; x++)
124 {
125 const size_t i = (size_t)y * region_w + x;
126
127 if(!hole[i])
128 {
129 tmp[i] = field[i];
130 continue;
131 }
132
133 const int x_lo = MAX(x - 1, 0), x_hi = MIN(x + 1, region_w - 1);
134 const int y_lo = MAX(y - 1, 0), y_hi = MIN(y + 1, region_h - 1);
135 const float center = field[i];
136 // second differences of r: d_xx r, d_yy r, and the mixed d_xy r (the Hessian of r)
137 const float d2_xx = field[(size_t)y * region_w + x_hi] - 2.f * center + field[(size_t)y * region_w + x_lo];
138 const float d2_yy = field[(size_t)y_hi * region_w + x] - 2.f * center + field[(size_t)y_lo * region_w + x];
139 const float d2_xy = 0.25f
140 * (field[(size_t)y_hi * region_w + x_hi] - field[(size_t)y_hi * region_w + x_lo]
141 - field[(size_t)y_lo * region_w + x_hi] + field[(size_t)y_lo * region_w + x_lo]);
142
143 // r <- max( r + 0.18*(D_xx d_xx r + 2 D_xy d_xy r + D_yy d_yy r) - 0.18*react*(r - target),
144 // obstacle ): explicit trace-form step tr(D Hess r), the screened reaction of the
145 // "inpaint a flat color" user parameter (lambda_solid pulls the core chroma toward the
146 // mean valid colour -- same semantics as the joint core's screened-Poisson solve, applied
147 // here because THIS stage owns the final all-clip chroma), then the obstacle projection
148 // (article Step 8 update rule).
149 tmp[i] = fmaxf(center + 0.18f * (tensor_xx[i] * d2_xx + 2.f * tensor_xy[i] * d2_xy + tensor_yy[i] * d2_yy)
150 - 0.18f * react * (center - react_target),
151 obstacle[i]);
152 }
153
155 for(int y = box_y_lo; y <= box_y_hi; y++)
156 memcpy(field + (size_t)y * region_w + box_x_lo, tmp + (size_t)y * region_w + box_x_lo,
157 (size_t)(box_x_hi - box_x_lo + 1) * sizeof(float));
158 }
159}
160
161int _aniso_div_solve(float *const restrict ratios, const float *const restrict valid,
162 const float *const restrict luminance, float *const restrict scratch_planes,
163 const int region_w, const int region_h, const float react,
164 const dt_aligned_pixel_t react_target, const dt_dev_pixelpipe_t *pipe)
165{
166 const size_t region_pixels = (size_t)region_w * region_h;
167 float *const restrict tensor_xx = scratch_planes;
168 float *const restrict tensor_xy = scratch_planes + region_pixels;
169 float *const restrict tensor_yy = scratch_planes + 2 * region_pixels;
170 float *const restrict tensor_scratch = scratch_planes + 3 * region_pixels;
171
172 // the three channels must share one hole (all-clip core); bail out otherwise
173 int n_unknowns = 0;
174 for(size_t i = 0; i < region_pixels; i++)
175 {
176 const int is_hole = (valid[i * 4 + 0] < 0.5f);
177 if(is_hole != (valid[i * 4 + 1] < 0.5f) || is_hole != (valid[i * 4 + 2] < 0.5f)) return 0;
178 n_unknowns += is_hole;
179 }
180 if(n_unknowns == 0) return 1;
181 if(n_unknowns > DT_HL_SPARSE_MAX) return 0;
182
183 _aniso_tensor(luminance, tensor_xx, tensor_xy, tensor_yy, tensor_scratch, region_w, region_h);
184
185 int *grid_to_unknown = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * region_pixels, pipe);
186 int *unknown_to_grid = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
187 int *unknown_x = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
188 int *unknown_y = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
189 int *permutation = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
190 int *inverse_perm = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
191 int *matrix_col_ptr = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * (n_unknowns + 1), pipe);
192 double *right_hand_side
193 = (double *)dt_pixelpipe_cache_alloc_align(sizeof(double) * (size_t)n_unknowns * 3, pipe);
194 int *matrix_row_index = NULL;
195 double *matrix_values = NULL;
196 int success = (grid_to_unknown && unknown_to_grid && unknown_x && unknown_y && permutation && inverse_perm
197 && matrix_col_ptr && right_hand_side);
198
199 if(success)
200 {
201 int unknown_index = 0;
202 for(size_t i = 0; i < region_pixels; i++)
203 {
204 const int is_hole = (valid[i * 4 + 0] < 0.5f);
205 grid_to_unknown[i] = is_hole ? unknown_index : -1;
206 if(is_hole)
207 {
208 unknown_to_grid[unknown_index] = (int)i;
209 unknown_y[unknown_index] = (int)(i / region_w);
210 unknown_x[unknown_index] = (int)(i - (size_t)unknown_y[unknown_index] * region_w);
211 unknown_index++;
212 }
213 }
214
215 for(int i = 0; i < n_unknowns; i++) permutation[i] = i;
216 _sp_nd_order(permutation, n_unknowns, unknown_x, unknown_y, 1);
217 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
218 inverse_perm[permutation[perm_index]] = perm_index;
219
220 static const int neighbour_dy[8] = { 0, 0, -1, 1, -1, 1, -1, 1 };
221 static const int neighbour_dx[8] = { -1, 1, 0, 0, -1, 1, 1, -1 };
222
223 for(int pass = 0; pass < 2 && success; pass++)
224 {
225 if(pass == 1)
226 {
227 int total = 0;
228 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
229 {
230 const int col_count = matrix_col_ptr[perm_index];
231 matrix_col_ptr[perm_index] = total;
232 total += col_count;
233 }
234 matrix_col_ptr[n_unknowns] = total;
235 matrix_row_index = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * total, pipe);
236 matrix_values = (double *)dt_pixelpipe_cache_alloc_align(sizeof(double) * total, pipe);
237 if(!matrix_row_index || !matrix_values)
238 success = 0;
239 else
240 memset(right_hand_side, 0, sizeof(double) * (size_t)n_unknowns * 3);
241 }
242
243 for(int perm_index = 0; perm_index < n_unknowns && success; perm_index++)
244 {
245 const int origin_grid = unknown_to_grid[permutation[perm_index]];
246 const int origin_y = origin_grid / region_w;
247 const int origin_x = origin_grid - origin_y * region_w;
248 double diagonal = 0.0;
249 int n_col_entries = 0;
250
251 for(int edge = 0; edge < 8; edge++)
252 {
253 const int neighbour_x = origin_x + neighbour_dx[edge];
254 const int neighbour_y = origin_y + neighbour_dy[edge];
255 // note: at the region border, missing neighbours simply drop out (no-flux boundary)
256 if(neighbour_x < 0 || neighbour_y < 0 || neighbour_x >= region_w || neighbour_y >= region_h)
257 continue; // Neumann at the region border
258 const size_t j = (size_t)neighbour_y * region_w + neighbour_x;
259 const float weight = _aniso_edge_w(tensor_xx, tensor_xy, tensor_yy, (size_t)origin_grid, j,
260 neighbour_dx[edge], neighbour_dy[edge]); // w_ij >= 0
261 if(weight <= 0.f) continue;
262 diagonal += weight; // diagonal = sum_j w_ij (graph-Laplacian row sum)
263
264 if(grid_to_unknown[j] >= 0)
265 {
266 const int target_row = inverse_perm[grid_to_unknown[j]];
267 if(target_row < perm_index)
268 {
269 if(pass == 1)
270 {
271 matrix_row_index[matrix_col_ptr[perm_index] + n_col_entries] = target_row;
272 matrix_values[matrix_col_ptr[perm_index] + n_col_entries]
273 = -(double)weight; // off-diagonal A_ij = -w_ij
274 }
275 n_col_entries++;
276 }
277 }
278 else if(pass == 1)
279 // Dirichlet neighbour (rim, not an unknown): its fixed r_valid moves to the RHS as
280 // +w_ij * r_valid_j, one per colour channel (same matrix, three right-hand sides)
281 for(int c = 0; c < 3; c++)
282 right_hand_side[(size_t)c * n_unknowns + perm_index] += (double)weight * ratios[j * 4 + c];
283 }
284
285 // diagonal last (any order works: columns need not be sorted). The screened reaction of
286 // the "inpaint a flat color" user parameter adds lambda_solid to the diagonal (and
287 // lambda_solid * target to each channel's RHS below): (lambda I + Op) r = lambda target
288 // + boundary terms -- the same semantics as the joint core's screened-Poisson solve,
289 // applied here because THIS stage owns the final all-clip chroma.
290 if(pass == 1)
291 {
292 matrix_row_index[matrix_col_ptr[perm_index] + n_col_entries] = perm_index;
293 matrix_values[matrix_col_ptr[perm_index] + n_col_entries] = diagonal + (double)react;
294 if(react > 0.f)
295 for(int c = 0; c < 3; c++)
296 right_hand_side[(size_t)c * n_unknowns + perm_index] += (double)react * react_target[c];
297 }
298 n_col_entries++;
299 if(pass == 0) matrix_col_ptr[perm_index] = n_col_entries;
300 }
301 }
302
303 if(success)
304 {
305 _sp_chol_t *factor = _sp_chol_factor(n_unknowns, matrix_col_ptr, matrix_row_index, matrix_values, pipe->type);
306 if(factor)
307 {
308 for(int c = 0; c < 3; c++)
309 {
310 _sp_chol_solve(factor, right_hand_side + (size_t)c * n_unknowns);
311 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
312 ratios[(size_t)unknown_to_grid[permutation[perm_index]] * 4 + c]
313 = (float)right_hand_side[(size_t)c * n_unknowns + perm_index];
314 }
316 }
317 else
318 success = 0;
319 }
320 }
321
322 dt_pixelpipe_cache_free_align(grid_to_unknown);
323 dt_pixelpipe_cache_free_align(unknown_to_grid);
327 dt_pixelpipe_cache_free_align(inverse_perm);
328 dt_pixelpipe_cache_free_align(matrix_col_ptr);
329 dt_pixelpipe_cache_free_align(matrix_row_index);
330 dt_pixelpipe_cache_free_align(matrix_values);
331 dt_pixelpipe_cache_free_align(right_hand_side);
332 return success;
333}
334
337{
338 const _hl_region_t *const region = ctx->region;
339 const dt_dev_pixelpipe_t *const pipe = ctx->pipe;
340 const int region_w = ctx->region_w;
341 const int region_h = ctx->region_h;
342 const size_t region_pixels = ctx->region_pixels;
343 const float epsilon = ctx->epsilon;
344 float *const restrict estimate = ctx->estimate;
345 float *const restrict prev_scale = ctx->prev_scale;
346 float *const restrict valid = ctx->valid;
347 float *const restrict blur_in = ctx->blur_in;
348 float *const restrict plane1 = ctx->plane1;
349 float *const restrict clip0 = ctx->clip0;
350 uint8_t *const restrict hole = ctx->hole;
351 float *const restrict solver_field = ctx->solver_field;
352 float *const restrict lum_accum = ctx->lum_accum;
353 float *const restrict reaction_weight = ctx->reaction_weight;
354 float *const restrict flat_target = ctx->flat_target;
355
356 // --- uncertainty-aware biharmonic seam regulariser (fix_prototype.py _weighted_solve) ---
357 // The steps above recover magnitude well but leave SEAMS where the method changes: the
358 // guide-flip on decorrelated content, and above all the all-clip-core <-> partial-clip
359 // handoff (a bright joint-core dome meeting an under-estimated single-guide reconstruction).
360 // No confidence weight can hide a discontinuity in the thing it weights, so iron the seams
361 // out afterwards: per channel solve (diag(Wd) + lambda*Delta^2) u = diag(Wd)*rec over the
362 // any-clip region, with the finished reconstruction as both the data target and the initial
363 // guess, and Wd = Wc^2 (= R^4) the fidelity weight. Where the recon is trustworthy (Wd high)
364 // u = rec is preserved; where it is not (Wd low: seams, decorrelated, all-clip core) the
365 // biharmonic prior flattens the seam's CURVATURE spike while preserving smooth domes and
366 // gradients (a harmonic prior would over-smooth them). Magnitude is preserved because the
367 // target is the recon itself. Full-res CG: the dome is already built, so the solve only has
368 // to relax the localised seams -- no dome-building stall. See the companion article.
369
370 // --- structure-steered chroma: diffuse the clipped channels' ratios est_c/L along the
371 // isophotes of the recovered luminance, coarse-to-fine (pyramid) so the whole hole is
372 // seeded before refinement. Magnitude (the norm L) is untouched: only direction changes.
373 //
374 // MATHS BRIDGE -- Step 8 chrominance coherence (article §"Chrominance coherence", the
375 // anisotropic chroma pass): minimize E_chrominance = int_Omega grad(r)^T D grad(r) dOmega
376 // subject to r_c >= c0/L_sum, Euler-Lagrange div(D grad r) = 0, D structure-steered. Restricted
377 // to the all-clip pixels; the coefficient-field results act as Dirichlet anchors. Solver picked
378 // by size: _aniso_div_solve (direct, small cores) or the coarse-to-fine _aniso_iterate_obs
379 // pyramid (large cores), then a full-res projected polish. Reassembly RGB = L_sum * r.
380 {
381 // the aniso pass must not rewrite the coefficient-field estimates: only the guide-less
382 // all-clip core diffuses, and the coefficient-field pixels act as valid anchors
383 // vld_an: all-clip pixels keep valid < 0.5 (they diffuse); every other pixel is promoted to
384 // an anchor (validity raised to >= 0.6), so div(D grad r)=0 sees them as fixed Dirichlet data
385 HL_PFOR()
386 for(size_t i = 0; i < region_pixels; i++)
387 {
388 const int allc = (valid[i * 4 + 0] < 0.5f && valid[i * 4 + 1] < 0.5f && valid[i * 4 + 2] < 0.5f);
389
390 for(int c = 0; c < 4; c++)
391 prev_scale[i * 4 + c] = allc ? valid[i * 4 + c] : fmaxf(valid[i * 4 + c], 0.6f);
392 }
393
394 const float *const restrict vld_an = prev_scale;
395
396 // fine-level luminance and per-channel ratios (ratio planes packed in s1's 4-ch layout)
397 // L_sum = R+G+B, r_c = est_c / L_sum: the split of magnitude from chrominance (step 8 diffuses
398 // only r; L_sum is left untouched and re-multiplied back at the reassembly)
400 for(size_t i = 0; i < region_pixels; i++)
401 {
402 const float lum_val = fmaxf(estimate[i * 4 + 0] + estimate[i * 4 + 1] + estimate[i * 4 + 2], epsilon);
403 lum_accum[i] = lum_val;
404
405 for(int c = 0; c < 3; c++)
406 plane1[i * 4 + c] = estimate[i * 4 + c] / lum_val;
407 }
408
409 // unknowns and their bounding box: the diffusion only ever writes pixels with
410 // vld_an < 0.5 (the all-clip core in coefficient-field mode)
411 size_t n_aniso = 0;
412 int abx0 = region_w, aby0 = region_h, abx1 = -1, aby1 = -1;
413 for(int y = 0; y < region_h; y++)
414 for(int x = 0; x < region_w; x++)
415 {
416 const size_t i = (size_t)y * region_w + x;
417 if(vld_an[i * 4 + 0] < 0.5f || vld_an[i * 4 + 1] < 0.5f || vld_an[i * 4 + 2] < 0.5f)
418 {
419 n_aniso++;
420 abx0 = MIN(abx0, x);
421 abx1 = MAX(abx1, x);
422 aby0 = MIN(aby0, y);
423 aby1 = MAX(aby1, y);
424 }
425 }
426
427 int aniso_done = 0;
428 if(n_aniso == 0) aniso_done = 1; // nothing to diffuse: skip the whole machinery
429
430 // "inpaint a flat color": the screened reaction lambda_solid = solid_color^2 * 4 pulls the
431 // all-clip chroma toward the mean valid chromaticity. It must live in THIS stage's solves:
432 // the joint core applies the same reaction, but this stage re-solves the all-clip interior
433 // afterwards (direct solve or pyramid, both anchor-determined), so a reaction applied only
434 // there never reaches the output -- the user parameter was dead from release until this fix.
435 const float react = ctx->solid_color * ctx->solid_color * 4.f;
436 dt_aligned_pixel_t react_target = { 0.f, 0.f, 0.f, 0.f };
437 if(react > 0.f && !aniso_done)
438 {
439 double target_accum[3] = { 0.0, 0.0, 0.0 };
440 double target_count = 0.0;
441 for(size_t i = 0; i < region_pixels; i++)
442 {
443 if(!(valid[i * 4 + 0] >= 0.5f && valid[i * 4 + 1] >= 0.5f && valid[i * 4 + 2] >= 0.5f)) continue;
444 for(int c = 0; c < 3; c++) target_accum[c] += (double)plane1[i * 4 + c];
445 target_count += 1.0;
446 }
447 if(target_count > 0.0)
448 for(int c = 0; c < 3; c++) react_target[c] = (float)(target_accum[c] / target_count);
449 }
450
451 // primary Step-8 estimator: exact div(D grad r)=0 direct solve (returns 0 -> fall back to
452 // the coarse-to-fine pyramid below for cores too large for the sparse Cholesky)
453 if(!aniso_done)
454 aniso_done = _aniso_div_solve(plane1, vld_an, lum_accum, blur_in, region_w, region_h, react,
455 react_target, pipe);
456
457 // pyramid depth: halve until the deepest hole spans ~8 px at the coarsest level
458 int nlev = 1;
459
460 while(((int)region->radius >> (nlev - 1)) > 8 && nlev < 7) nlev++;
461
462 // coarse -> fine; each level diffuses each channel's ratio over ITS clipped mask, then the
463 // result seeds the next finer level's hole pixels. Explicit iterations travel only
464 // ~sqrt(iters) px, so the coarsest level fills the whole hole first (the "unreached interior
465 // stays magenta" fix) -- the multiscale seeding of the div(D grad r)=0 fill for large cores.
466 if(!aniso_done)
467 for(int level = nlev - 1; level >= 0; level--)
468 {
469 const int step = 1 << level;
470 const int down_w = (region_w + step - 1) / step;
471 const int down_h = (region_h + step - 1) / step;
472 const size_t down_pixels = (size_t)down_w * down_h;
473 float *const restrict dome_L = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
474 float *const restrict dome_ratio = dt_pixelpipe_cache_alloc_align_float(down_pixels * 3, pipe);
475 float *const restrict tensor_xx = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
476 float *const restrict tensor_xy = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
477 float *const restrict tensor_yy = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
478 float *const restrict tensor_scratch = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
479 float *const restrict dobs = dt_pixelpipe_cache_alloc_align_float(down_pixels * 3, pipe);
480 float *const restrict dobc = dt_pixelpipe_cache_alloc_align_float(down_pixels, pipe);
481 uint8_t *const restrict dhole
482 = (uint8_t *)dt_pixelpipe_cache_alloc_align(sizeof(uint8_t) * down_pixels * 3, ctx->pipe);
483 uint8_t *const restrict hplane
484 = (uint8_t *)dt_pixelpipe_cache_alloc_align(sizeof(uint8_t) * down_pixels, ctx->pipe);
485
486 if(!dome_L || !dome_ratio || !tensor_xx || !tensor_xy || !tensor_yy || !tensor_scratch || !dobs || !dobc
487 || !dhole || !hplane)
488 {
494 dt_pixelpipe_cache_free_align(tensor_scratch);
499 break;
500 }
501
502 // box-downsample: luminance = cell mean; ratio = mean over the cell (current estimate,
503 // already seeded by the coarser level); hole = majority of the cell clipped
504 __OMP_PARALLEL_FOR__(collapse(2))
505 for(int cell_y = 0; cell_y < down_h; cell_y++)
506 for(int cell_x = 0; cell_x < down_w; cell_x++)
507 {
508 double accL = 0.0;
509 double accr[3] = { 0.0, 0.0, 0.0 };
510 int n_unknowns[3] = { 0, 0, 0 };
511 int n_total = 0;
512
513 double accc[3] = { 0.0, 0.0, 0.0 };
514 for(int nb_y = cell_y * step; nb_y < MIN((cell_y + 1) * step, region_h); nb_y++)
515 for(int nb_x = cell_x * step; nb_x < MIN((cell_x + 1) * step, region_w); nb_x++)
516 {
517 const size_t fine_index = (size_t)nb_y * region_w + nb_x;
518 accL += lum_accum[fine_index];
519 n_total++;
520
521 for(int c = 0; c < 3; c++)
522 {
523 accr[c] += plane1[fine_index * 4 + c];
524 accc[c] += clip0[fine_index * 4 + c];
525 n_unknowns[c] += (vld_an[fine_index * 4 + c] < 0.5f);
526 }
527 }
528
529 const size_t cell_index = (size_t)cell_y * down_w + cell_x;
530 dome_L[cell_index] = (float)(accL / n_total);
531
532 for(int c = 0; c < 3; c++)
533 {
534 dome_ratio[cell_index * 3 + c] = (float)(accr[c] / n_total);
535 // per-cell obstacle: the saturation floor in ratio space, clip0_c / L
536 dobs[cell_index * 3 + c] = (float)(accc[c] / fmax(accL, 1e-9));
537 dhole[cell_index * 3 + c] = (2 * n_unknowns[c] > n_total) ? 1 : 0;
538 }
539 }
540
541 // structure tensor D of this level's luminance, then diffuse each channel's ratio plane
542 // under the obstacle (per-level projected relaxation of div(D grad r)=0, r >= c0/L)
543 _aniso_tensor(dome_L, tensor_xx, tensor_xy, tensor_yy, tensor_scratch, down_w, down_h);
544
545 const int box_x_lo = MAX(abx0 / step - 2, 0), box_y_lo = MAX(aby0 / step - 2, 0);
546 const int box_x_hi = MIN(abx1 / step + 2, down_w - 1), box_y_hi = MIN(aby1 / step + 2, down_h - 1);
547
548 for(int c = 0; c < 3; c++)
549 {
550 size_t n_channels = 0;
551 __OMP_PARALLEL_FOR__(reduction(+ : n_channels))
552 for(size_t cell_index = 0; cell_index < down_pixels; cell_index++)
553 {
554 dome_L[cell_index] = dome_ratio[cell_index * 3 + c]; // reuse dL as the working plane for channel c
555 dobc[cell_index] = dobs[cell_index * 3 + c];
556 hplane[cell_index] = dhole[cell_index * 3 + c];
557 n_channels += hplane[cell_index];
558 }
559
560 if(n_channels == 0) continue; // no hole cell at this level for this channel
561
562 _aniso_iterate_obs(dome_L, dobc, hplane, tensor_xx, tensor_xy, tensor_yy, tensor_scratch, down_w, down_h,
563 240, box_x_lo, box_y_lo, box_x_hi, box_y_hi, 0.f, 0.f);
564
566 for(size_t cell_index = 0; cell_index < down_pixels; cell_index++)
567 dome_ratio[cell_index * 3 + c] = dome_L[cell_index];
568 }
569
570 // splat this level's hole ratios back into the fine planes (bilinear prolongation),
571 // seeding the next finer level; valid fine pixels keep their true ratios (anchors)
572 __OMP_PARALLEL_FOR__(collapse(2))
573 for(int y = 0; y < region_h; y++)
574 for(int x = 0; x < region_w; x++)
575 {
576 const size_t fine_index = (size_t)y * region_w + x;
577 const float grad_x = ((float)x + 0.5f) / step - 0.5f;
578 const float grad_y = ((float)y + 0.5f) / step - 0.5f;
579 const int x_lo = CLAMP((int)floorf(grad_x), 0, down_w - 1);
580 const int y_lo = CLAMP((int)floorf(grad_y), 0, down_h - 1);
581 const int x_hi = MIN(x_lo + 1, down_w - 1);
582 const int y_hi = MIN(y_lo + 1, down_h - 1);
583 const float frac_x = CLAMP(grad_x - x_lo, 0.f, 1.f);
584 const float frac_y = CLAMP(grad_y - y_lo, 0.f, 1.f);
585
586 for(int c = 0; c < 3; c++)
587 {
588 if(vld_an[fine_index * 4 + c] >= 0.5f) continue;
589
590 const float interp_a = dome_ratio[((size_t)y_lo * down_w + x_lo) * 3 + c] * (1.f - frac_x)
591 + dome_ratio[((size_t)y_lo * down_w + x_hi) * 3 + c] * frac_x;
592 const float interp_b = dome_ratio[((size_t)y_hi * down_w + x_lo) * 3 + c] * (1.f - frac_x)
593 + dome_ratio[((size_t)y_hi * down_w + x_hi) * 3 + c] * frac_x;
594 plane1[fine_index * 4 + c] = interp_a * (1.f - frac_y) + interp_b * frac_y;
595 }
596 }
597
603 dt_pixelpipe_cache_free_align(tensor_scratch);
608 }
609
610 // Full-resolution projected polish, both solver paths (the direct solve cannot project
611 // mid-solve, and the pyramid's finest sweeps only correct locally): a short obstacle-
612 // projected relaxation at full resolution lets the field settle smoothly around the
613 // active set of the constraint.
614 if(n_aniso > 0)
615 {
616 HL_PFOR()
617 for(size_t i = 0; i < region_pixels; i++)
618 hole[i] = (vld_an[i * 4 + 0] < 0.5f && vld_an[i * 4 + 1] < 0.5f && vld_an[i * 4 + 2] < 0.5f);
619
620 // Activity gate: the polish exists to settle the field around the ACTIVE set of the
621 // obstacle. Where no all-clip pixel sits at (or below) its obstacle, the projection
622 // never fires and the 60 sweeps only re-run a diffusion the solvers already
623 // converged -- skip them. The 1.001 band catches pixels the pyramid projection left
624 // exactly ON the obstacle.
625 int act0 = 0, act1 = 0, act2 = 0;
626 HL_PFOR(reduction(| : act0, act1, act2))
627 for(size_t i = 0; i < region_pixels; i++)
628 {
629 if(!hole[i]) continue;
630 const float invL = 1.f / fmaxf(lum_accum[i], epsilon);
631 act0 |= (plane1[i * 4 + 0] <= clip0[i * 4 + 0] * invL * 1.001f);
632 act1 |= (plane1[i * 4 + 1] <= clip0[i * 4 + 1] * invL * 1.001f);
633 act2 |= (plane1[i * 4 + 2] <= clip0[i * 4 + 2] * invL * 1.001f);
634 }
635 // the reaction changes the fixed point everywhere in the core, not just at the active
636 // set of the obstacle: with lambda_solid > 0 the polish must always run
637 const int react_on = (react > 0.f);
638 const int active[3] = { act0 | react_on, act1 | react_on, act2 | react_on };
639
640 if(act0 | act1 | act2 | react_on)
641 {
642 float *const restrict otxx = blur_in + 0 * region_pixels; // `in` (rn*4) is free scratch here
643 float *const restrict otxy = blur_in + 1 * region_pixels;
644 float *const restrict otyy = blur_in + 2 * region_pixels;
645 float *const restrict otsc = blur_in + 3 * region_pixels;
646 _aniso_tensor(lum_accum, otxx, otxy, otyy, otsc, region_w, region_h);
647
648 for(int c = 0; c < 3; c++)
649 {
650 if(!active[c]) continue;
651
652 HL_PFOR()
653 for(size_t i = 0; i < region_pixels; i++)
654 {
655 solver_field[i] = plane1[i * 4 + c];
656 reaction_weight[i] = clip0[i * 4 + c] / fmaxf(lum_accum[i], epsilon); // the obstacle
657 }
658
659 _aniso_iterate_obs(solver_field, reaction_weight, hole, otxx, otxy, otyy, flat_target, region_w,
660 region_h, 60, abx0, aby0, abx1, aby1, react, react_target[c]);
661
662 HL_PFOR()
663 for(size_t i = 0; i < region_pixels; i++) plane1[i * 4 + c] = solver_field[i];
664 }
665 }
666 }
667
668 // reassemble. This pass only ever writes the all-clip core (vld_an flags every channel
669 // of a partially-valid pixel >= 0.6, so those pixels are anchors, settled by the
670 // coefficient-field stages): the magnitude is the dome luminance L split by the
671 // diffused ratios. (A ladder-era magnitude-transfer branch for partially-valid pixels
672 // used to live here; the anchor construction made it unreachable and it was removed.)
673 // SOFT saturation floor on the way out (same rounding as the coefficient-field floor): the
674 // hard max() prints an exactly-flat shelf at the clip level plus a gradient kink wherever the
675 // magnitude transfer under-predicts a channel near its own rim inside the core (measured on
676 // DSC00078's sun: ~10 px flat at clip0_B, then a 2x-slope break). JOINT variant blended by the
677 // clip-asymmetry gate ctx->floor_gate (see the cf Step-5 floor for the rationale): one scalar
678 // lift of the clipped subset preserves the diffused chromaticity; per-channel at gate 0.
679 const float floor_gate = ctx->floor_gate;
681 for(size_t i = 0; i < region_pixels; i++)
682 {
683 const float raccum = fmaxf(plane1[i * 4 + 0] + plane1[i * 4 + 1] + plane1[i * 4 + 2], epsilon); // sum_j r_j
684
685 float lift = 1.f;
686 if(floor_gate > 1e-6f)
687 for(int c = 0; c < 3; c++)
688 if(vld_an[i * 4 + c] < 0.5f)
689 {
690 const float ratio_c = fmaxf(plane1[i * 4 + c], 0.f);
691 const float value = fmaxf(lum_accum[i] * ratio_c / raccum, 1e-6f);
692 const float clip_floor_c = clip0[i * 4 + c];
693 const float delta = value - clip_floor_c;
694 const float weight = 0.02f * fmaxf(clip_floor_c, 1e-6f);
695 const float target = clip_floor_c + 0.5f * (delta + sqrtf(delta * delta + weight * weight));
696 lift = fmaxf(lift, fminf(target / value, 8.f));
697 }
698
699 for(int c = 0; c < 3; c++)
700 if(vld_an[i * 4 + c] < 0.5f)
701 {
702 const float ratio_c = fmaxf(plane1[i * 4 + c], 0.f);
703 const float value = lum_accum[i] * ratio_c / raccum; // recombine u_c = L_sum * r_c / sum_j r_j
704 // soft saturation floor u_c <- c0 + 0.5*((u-c0) + sqrt((u-c0)^2 + w^2)), w = 0.02*c0
705 // (article rule 3 / step 5 soft-max): a smooth max(u, c0) with no shelf-and-kink
706 const float clip_floor_c = clip0[i * 4 + c];
707 const float weight = 0.02f * fmaxf(clip_floor_c, 1e-6f);
708 const float delta = value - clip_floor_c;
709 const float per_chan = clip_floor_c + 0.5f * (delta + sqrtf(delta * delta + weight * weight));
710 if(floor_gate <= 1e-6f)
711 {
712 estimate[i * 4 + c] = per_chan; // bit-exact approved path
713 continue;
714 }
715 const float lifted = fmaxf(value, 1e-6f) * lift;
716 const float delta_joint = lifted - clip_floor_c;
717 const float joint
718 = clip_floor_c + 0.5f * (delta_joint + sqrtf(delta_joint * delta_joint + weight * weight));
719 estimate[i * 4 + c] = floor_gate * joint + (1.f - floor_gate) * per_chan;
720 }
721 }
722 }
723}
724
725// ============================ OpenCL ============================
726
727#if defined(HAVE_OPENCL) && DT_HL_SPARSE_SOLVE && (DT_HL_ANISO_SOLVER == 2)
728
729// Explicit coarse-to-fine structure-steered diffusion on the device, mirroring the CPU
730// pyramid (_aniso_tensor + _aniso_iterate in the DT_HL_ANISO_CHROMA block) that handles cores
731// beyond DT_HL_SPARSE_MAX unknowns: each level box-downsamples the brightness/ratios/holes,
732// rebuilds the structure tensor (local edge direction and strength), runs 240 damped stencil
733// steps per channel over the hole bounding box (ping-pong buffers instead of the CPU's
734// write-back copy), and bilinearly splats the ratios into the fine planes' clipped channels
735// to seed the next level. Any change here must be mirrored in the CPU pyramid and
736// re-validated with the HL_ANISOCL_TEST self-test (_aniso_stage_cl_selftest).
737//
738// MATHS BRIDGE -- Step 8 large-core path (article §"The update rules", the explicit trace-form
739// pyramid): the multiscale solver for min int grad(r)^T D grad(r) s.t. r >= c0/L when the core
740// exceeds DT_HL_SPARSE_MAX. Each level projects onto the obstacle then runs 240 explicit steps of
741// r <- max(r + 0.18*tr(D Hess r), c0/L) (kernel hl_aniso_iter[_block]); coarsest level first so
742// the whole hole is seeded before refinement. D = structure tensor of the recovered luminance.
743static cl_int _aniso_pyramid_cl(const int devid, void *gd_void, cl_mem ratios, cl_mem valid, cl_mem luminance,
744 cl_mem clip0, const int region_w, const int region_h, const float radius,
745 const int box_x_lo, const int box_y_lo, const int box_x_hi, const int box_y_hi,
746 const dt_dev_pixelpipe_t *pipe)
747{
749 cl_int cl_err = CL_SUCCESS;
750
751 // The stage-2 reduction finalizers this needs are compiled only where the fp64 extension
752 // is (data/kernels/highlights_harmonic.cl). Without them the caller falls back to the CPU
753 // twin, the same way the sparse solver and the PDE/aniso stages already do.
754 if(global_data->kernel_hl_reduce_finalize < 0) return DT_OPENCL_DEFAULT_ERROR; // no fp64 device
755
756 cl_mem gnorm_dev = dt_opencl_alloc_device_buffer(devid, sizeof(float)); // gradient-mean normaliser, device-resident
757 // the pyramid levels run reaction-free; the "inpaint a flat color" pull is applied by the
758 // direct solve and the full-resolution polish only, like the CPU twin
759 const float no_react = 0.f;
760
761 int n_levels = 1;
762 while(((int)radius >> (n_levels - 1)) > 8 && n_levels < 7) n_levels++;
763
764 for(int level = n_levels - 1; level >= 0 && cl_err == CL_SUCCESS; level--)
765 {
766 const int step = 1 << level;
767 const int coarse_w = (region_w + step - 1) / step;
768 const int coarse_h = (region_h + step - 1) / step;
769 const size_t coarse_pixels = (size_t)coarse_w * coarse_h;
770 size_t size_coarse[3] = { ROUNDUPDWD(coarse_w, devid), ROUNDUPDHT(coarse_h, devid), 1 };
771 size_t size_full[3] = { ROUNDUPDWD(region_w, devid), ROUNDUPDHT(region_h, devid), 1 };
772
773 cl_mem coarse_lum = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
774 cl_mem coarse_ratios = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels * 3);
775 cl_mem coarse_obstacle = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels * 3);
776 cl_mem coarse_hole = dt_opencl_alloc_device_buffer(devid, coarse_pixels * 3);
777 cl_mem grad_x = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
778 cl_mem tensor_xx = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
779 cl_mem grad_y = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
780 cl_mem tensor_xy = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
781 cl_mem tensor_yy = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
782 cl_mem diffuse_a = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
783 cl_mem diffuse_b = dt_opencl_alloc_device_buffer(devid, sizeof(float) * coarse_pixels);
784 cl_mem grad_partials = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 256);
785 if(!coarse_lum || !coarse_ratios || !coarse_obstacle || !coarse_hole || !grad_x || !tensor_xx || !grad_y
786 || !tensor_xy || !tensor_yy || !diffuse_a || !diffuse_b || !grad_partials)
788
789 if(cl_err == CL_SUCCESS)
790 {
791 const int kernel = global_data->kernel_hl_aniso_pyr_down;
792 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &luminance);
793 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &ratios);
794 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &valid);
795 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &clip0);
796 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &coarse_lum);
797 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &coarse_ratios);
798 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &coarse_obstacle);
799 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), &coarse_hole);
800 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &region_w);
801 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &region_h);
802 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &coarse_w);
803 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &coarse_h);
804 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &step);
805 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
806 }
807
808 // structure tensor of this level's luminance (box3 x2, gradient + mean magnitude, D)
809 if(cl_err == CL_SUCCESS)
810 {
811 const int kernel = global_data->kernel_hl_box3;
812 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &coarse_lum);
813 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &grad_x);
814 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &coarse_w);
815 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &coarse_h);
816 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
817 if(cl_err == CL_SUCCESS)
818 {
819 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &grad_x);
820 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &tensor_xx);
821 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
822 }
823 }
824 if(cl_err == CL_SUCCESS)
825 {
826 const int local_size = 64, n_groups = 256;
827 const int kernel = global_data->kernel_hl_grad_reduce;
828 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
829 size_t local[3] = { local_size, 1, 1 };
830 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &tensor_xx);
831 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &grad_x); // gx
832 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &grad_y); // gy
833 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &grad_partials);
834 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &coarse_w);
835 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &coarse_h);
836 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float) * local_size, NULL);
837 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
838 if(cl_err == CL_SUCCESS)
839 {
840 {
841 // fold the gradient sum into the normaliser ON DEVICE (hl_reduce_finalize mode 2:
842 // scale x sum, floored) -- hl_aniso_tensor reads the scalar, nothing crosses the bus
843 const int gfin = global_data->kernel_hl_reduce_finalize;
844 const int gstride = 1, gmode = 2;
845 const float gscale = 1.f / (float)coarse_pixels;
846 size_t gone[3] = { 1, 1, 1 };
847 dt_opencl_set_kernel_arg(devid, gfin, 0, sizeof(cl_mem), &grad_partials);
848 dt_opencl_set_kernel_arg(devid, gfin, 1, sizeof(cl_mem), &gnorm_dev);
849 dt_opencl_set_kernel_arg(devid, gfin, 2, sizeof(int), &n_groups);
850 dt_opencl_set_kernel_arg(devid, gfin, 3, sizeof(int), &gstride);
851 dt_opencl_set_kernel_arg(devid, gfin, 4, sizeof(int), &gmode);
852 dt_opencl_set_kernel_arg(devid, gfin, 5, sizeof(float), &gscale);
853 cl_err = dt_opencl_enqueue_kernel_2d(devid, gfin, gone);
854 const int kernel_tensor = global_data->kernel_hl_aniso_tensor;
855 dt_opencl_set_kernel_arg(devid, kernel_tensor, 0, sizeof(cl_mem), &grad_x);
856 dt_opencl_set_kernel_arg(devid, kernel_tensor, 1, sizeof(cl_mem), &grad_y);
857 dt_opencl_set_kernel_arg(devid, kernel_tensor, 2, sizeof(cl_mem), &tensor_xx); // txx
858 dt_opencl_set_kernel_arg(devid, kernel_tensor, 3, sizeof(cl_mem), &tensor_xy); // txy
859 dt_opencl_set_kernel_arg(devid, kernel_tensor, 4, sizeof(cl_mem), &tensor_yy);
860 dt_opencl_set_kernel_arg(devid, kernel_tensor, 5, sizeof(int), &coarse_w);
861 dt_opencl_set_kernel_arg(devid, kernel_tensor, 6, sizeof(int), &coarse_h);
862 dt_opencl_set_kernel_arg(devid, kernel_tensor, 7, sizeof(cl_mem), &gnorm_dev);
863 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel_tensor, size_coarse);
864 }
865 }
866 }
867
868 // per-channel 240-step diffusion over the level's hole bbox
869 if(cl_err == CL_SUCCESS)
870 {
871 const int level_x_lo = MAX(box_x_lo / step - 2, 0), level_y_lo = MAX(box_y_lo / step - 2, 0);
872 const int level_x_hi = MIN(box_x_hi / step + 2, coarse_w - 1),
873 level_y_hi = MIN(box_y_hi / step + 2, coarse_h - 1);
874 size_t size_box[3]
875 = { ROUNDUPDWD(level_x_hi - level_x_lo + 1, devid), ROUNDUPDHT(level_y_hi - level_y_lo + 1, devid), 1 };
876
877 for(int c = 0; c < 3 && cl_err == CL_SUCCESS; c++)
878 {
879 {
880 const int kernel = global_data->kernel_hl_pyr_getc;
881 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &coarse_ratios);
882 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &diffuse_a);
883 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &coarse_w);
884 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &coarse_h);
885 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
886 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
887 }
888 if(cl_err == CL_SUCCESS)
889 {
890 // seed projection onto the obstacle (mirrors the CPU _aniso_iterate_obs entry clamp)
891 const int kernel = global_data->kernel_hl_pyr_project;
892 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &diffuse_a);
893 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &coarse_obstacle);
894 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &coarse_hole);
895 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &coarse_w);
896 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &coarse_h);
897 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &c);
898 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
899 }
900 if(cl_err == CL_SUCCESS)
901 cl_err = dt_opencl_enqueue_copy_buffer_to_buffer(devid, diffuse_a, diffuse_b, 0, 0,
902 sizeof(float) * coarse_pixels);
903
904 cl_mem current_buf = diffuse_a, other_buf = diffuse_b;
905 if((level_x_hi - level_x_lo + 1) * (level_y_hi - level_y_lo + 1) <= 4096)
906 {
907 // all 240 steps in one single-workgroup launch (bit-identical, see the fill)
908 const int kernel = global_data->kernel_hl_aniso_iter_block;
909 const int iters = 240;
910 size_t size_block[3] = { 256, 1, 1 };
911 size_t local_block[3] = { 256, 1, 1 };
912 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &diffuse_a);
913 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &diffuse_b);
914 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &coarse_hole);
915 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &tensor_xx);
916 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &tensor_xy);
917 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &tensor_yy);
918 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &coarse_obstacle);
919 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &coarse_w);
920 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &coarse_h);
921 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &c);
922 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &level_x_lo);
923 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &level_y_lo);
924 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &level_x_hi);
925 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(int), &level_y_hi);
926 dt_opencl_set_kernel_arg(devid, kernel, 14, sizeof(int), &iters);
927 dt_opencl_set_kernel_arg(devid, kernel, 15, sizeof(float), &no_react);
928 dt_opencl_set_kernel_arg(devid, kernel, 16, sizeof(float), &no_react);
929 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, size_block, local_block);
930 }
931 else
932 for(int iter = 0; iter < 240 && cl_err == CL_SUCCESS; iter++)
933 {
934 const int kernel = global_data->kernel_hl_aniso_iter;
935 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &current_buf);
936 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &other_buf);
937 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &coarse_hole);
938 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &tensor_xx);
939 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &tensor_xy);
940 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &tensor_yy);
941 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &coarse_obstacle);
942 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &coarse_w);
943 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &coarse_h);
944 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &c);
945 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &level_x_lo);
946 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &level_y_lo);
947 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &level_x_hi);
948 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(int), &level_y_hi);
949 dt_opencl_set_kernel_arg(devid, kernel, 14, sizeof(float), &no_react);
950 dt_opencl_set_kernel_arg(devid, kernel, 15, sizeof(float), &no_react);
951 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_box);
952 cl_mem swap_buf = current_buf;
953 current_buf = other_buf;
954 other_buf = swap_buf;
955 }
956 if(cl_err == CL_SUCCESS)
957 {
958 const int kernel = global_data->kernel_hl_pyr_putc;
959 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &current_buf);
960 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &coarse_ratios);
961 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &coarse_w);
962 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &coarse_h);
963 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
964 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_coarse);
965 }
966 }
967 }
968
969 if(cl_err == CL_SUCCESS)
970 {
971 const int kernel = global_data->kernel_hl_aniso_splat;
972 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &ratios);
973 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
974 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &coarse_ratios);
975 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
976 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
977 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &coarse_w);
978 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &coarse_h);
979 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &step);
980 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_full);
981 }
982
984 dt_opencl_release_mem_object(coarse_ratios);
985 dt_opencl_release_mem_object(coarse_obstacle);
986 dt_opencl_release_mem_object(coarse_hole);
994 dt_opencl_release_mem_object(grad_partials);
995 }
997 return cl_err;
998}
999
1000cl_int _aniso_stage_cl(const int devid, void *gd_void, cl_mem estimate, cl_mem valid, cl_mem clip0,
1001 const int region_w, const int region_h, const float radius, const float floor_gate, const float solid_color, const dt_dev_pixelpipe_t *pipe)
1002{
1003 cl_mem gnorm_dev = dt_opencl_alloc_device_buffer(devid, sizeof(float)); // gradient-mean normaliser, device-resident
1005 const size_t region_pixels = (size_t)region_w * region_h;
1006 cl_int cl_err = DT_OPENCL_DEFAULT_ERROR;
1007 size_t size[3] = { ROUNDUPDWD(region_w, devid), ROUNDUPDHT(region_h, devid), 1 };
1008 const float epsilon = 1e-6f;
1009
1010 // "inpaint a flat color": lambda_solid = solid_color^2 * 4 pulls the all-clip chroma toward
1011 // the mean valid chromaticity; it lives in THIS stage's solves (see the CPU twin -- the joint
1012 // core's reaction is re-solved away by this stage, so applying it only there leaves the user
1013 // parameter dead). The target is reduced on the device once and reused by the direct RHS and
1014 // the full-resolution polish.
1015 const float react = solid_color * solid_color * 4.f;
1016 float react_target[3] = { 0.f, 0.f, 0.f };
1017
1018 if(global_data->kernel_hl_aniso_rhs < 0 || global_data->kernel_hl_aniso_scatter < 0) return cl_err; // no fp64
1019
1020 cl_mem valid_packed = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 4);
1021 cl_mem luminance = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1022 cl_mem ratios = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 4);
1023 cl_mem hole = dt_opencl_alloc_device_buffer(devid, region_pixels);
1024 cl_mem scratch1 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1025 cl_mem scratch2 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1026 cl_mem tensor_xx = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1027 cl_mem tensor_xy = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1028 cl_mem tensor_yy = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1029 cl_mem partials = NULL, perm_grid_dev = NULL, edge_weights_dev = NULL, rhs_dev = NULL;
1030 uint8_t *hole_mask = (uint8_t *)dt_pixelpipe_cache_alloc_align(region_pixels, pipe);
1031 int *grid_to_unknown = NULL, *unknown_to_grid = NULL, *unknown_x = NULL, *unknown_y = NULL, *perm = NULL,
1032 *inverse_perm = NULL;
1033 int *matrix_col_ptr = NULL, *matrix_row_index = NULL, *perm_grid = NULL;
1034 double *matrix_values = NULL;
1035 float *edge_weights = NULL;
1036 _sp_chol_cl_t *factor = NULL;
1037 if(!valid_packed || !luminance || !ratios || !hole || !scratch1 || !scratch2 || !tensor_xx || !tensor_xy
1038 || !tensor_yy || !hole_mask)
1039 goto out;
1040
1041 // validity mask + luminance + ratio planes + all-clip hole in one sweep
1042 {
1043 const int kernel = global_data->kernel_hl_aniso_prep;
1044 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
1045 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
1046 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &valid_packed);
1047 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &luminance);
1048 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &ratios);
1049 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &hole);
1050 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_w);
1051 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_h);
1052 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(float), &epsilon);
1053 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1054 if(cl_err != CL_SUCCESS) goto out;
1055 }
1056
1057 cl_err = dt_opencl_read_buffer_from_device(devid, hole_mask, hole, 0, region_pixels, CL_TRUE);
1058 if(cl_err != CL_SUCCESS) goto out;
1059
1060 int n_unknowns = 0;
1061 for(size_t i = 0; i < region_pixels; i++)
1062 if(hole_mask[i]) n_unknowns++;
1063 if(n_unknowns == 0)
1064 {
1065 cl_err = CL_SUCCESS; // nothing to diffuse
1066 goto out;
1067 }
1068 int box_x_lo = region_w, box_y_lo = region_h, box_x_hi = -1, box_y_hi = -1;
1069 for(int y = 0; y < region_h; y++)
1070 for(int x = 0; x < region_w; x++)
1071 if(hole_mask[(size_t)y * region_w + x])
1072 {
1073 box_x_lo = MIN(box_x_lo, x);
1074 box_x_hi = MAX(box_x_hi, x);
1075 box_y_lo = MIN(box_y_lo, y);
1076 box_y_hi = MAX(box_y_hi, y);
1077 }
1078
1079 if(react > 0.f)
1080 {
1081 // all-valid mean chromaticity (mirrors the CPU double accumulation over plane1): reuse the
1082 // cmean reduction with lum_min = 0 -- estimate/max(luminance, epsilon) IS the ratios plane
1083 const int local_size = 64, n_groups = 256;
1084 const int n_pixels = (int)region_pixels;
1085 // lum_min = 0 published into a device scalar (hl_cmean_reduce now takes it by pointer);
1086 // the constant rides in the kernel argument, nothing is copied.
1087 cl_mem lum_min_zero = dt_opencl_alloc_device_buffer(devid, sizeof(float));
1088 if(!lum_min_zero)
1089 {
1090 cl_err = DT_OPENCL_DEFAULT_ERROR;
1091 goto out;
1092 }
1093 {
1094 const int setk = global_data->kernel_hl_set_scalar;
1095 const float zero = 0.f;
1096 dt_opencl_set_kernel_arg(devid, setk, 0, sizeof(cl_mem), &lum_min_zero);
1097 dt_opencl_set_kernel_arg(devid, setk, 1, sizeof(float), &zero);
1098 size_t one[3] = { 1, 1, 1 };
1099 cl_err = dt_opencl_enqueue_kernel_2d(devid, setk, one);
1100 if(cl_err != CL_SUCCESS)
1101 {
1102 dt_opencl_release_mem_object(lum_min_zero);
1103 goto out;
1104 }
1105 }
1106 cl_mem target_partials = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * n_groups);
1107 if(!target_partials)
1108 {
1109 cl_err = DT_OPENCL_DEFAULT_ERROR;
1110 goto out;
1111 }
1112 const int kernel = global_data->kernel_hl_cmean_reduce;
1113 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
1114 size_t local[3] = { local_size, 1, 1 };
1115 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
1116 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
1117 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &luminance);
1118 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &target_partials);
1119 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &n_pixels);
1120 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), &epsilon);
1121 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &lum_min_zero);
1122 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float) * 4 * local_size, NULL);
1123 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
1124 float partial_host[4 * 256];
1125 if(cl_err == CL_SUCCESS)
1126 cl_err = dt_opencl_read_buffer_from_device(devid, partial_host, target_partials, 0,
1127 sizeof(float) * 4 * n_groups, CL_TRUE);
1128 dt_opencl_release_mem_object(target_partials);
1129 dt_opencl_release_mem_object(lum_min_zero);
1130 if(cl_err != CL_SUCCESS) goto out;
1131 double accum[4] = { 0.0, 0.0, 0.0, 0.0 };
1132 for(int group = 0; group < n_groups; group++)
1133 for(int k = 0; k < 4; k++) accum[k] += (double)partial_host[group * 4 + k];
1134 if(accum[3] > 0.0)
1135 for(int c = 0; c < 3; c++) react_target[c] = (float)(accum[c] / accum[3]);
1136 }
1137
1138 if(n_unknowns > DT_HL_SPARSE_MAX)
1139 {
1140 // beyond the direct solve: the explicit coarse-to-fine pyramid, like the CPU
1141 cl_err = _aniso_pyramid_cl(devid, gd_void, ratios, valid_packed, luminance, clip0, region_w, region_h, radius,
1142 box_x_lo, box_y_lo, box_x_hi, box_y_hi, pipe);
1143 if(cl_err != CL_SUCCESS) goto out;
1144 goto reassemble;
1145 }
1146
1147 // structure tensor of the recovered luminance
1148 {
1149 const int kernel = global_data->kernel_hl_box3;
1150 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &luminance);
1151 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch1);
1152 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
1153 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
1154 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1155 if(cl_err != CL_SUCCESS) goto out;
1156 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch1);
1157 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch2);
1158 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1159 if(cl_err != CL_SUCCESS) goto out;
1160 }
1161 {
1162 const int local_size = 64, n_groups = 256;
1163 partials = dt_opencl_alloc_device_buffer(devid, sizeof(float) * n_groups);
1164 if(!partials)
1165 {
1166 cl_err = DT_OPENCL_DEFAULT_ERROR;
1167 goto out;
1168 }
1169 const int kernel = global_data->kernel_hl_grad_reduce;
1170 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
1171 size_t local[3] = { local_size, 1, 1 };
1172 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch2);
1173 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &tensor_xx); // gx stash
1174 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &tensor_xy); // grad_y stash
1175 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &partials);
1176 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
1177 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
1178 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float) * local_size, NULL);
1179 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
1180 if(cl_err != CL_SUCCESS) goto out;
1181
1182 {
1183 const int gfin = global_data->kernel_hl_reduce_finalize;
1184 const int gstride = 1, gmode = 2;
1185 const float gscale = 1.f / (float)region_pixels;
1186 size_t gone[3] = { 1, 1, 1 };
1187 dt_opencl_set_kernel_arg(devid, gfin, 0, sizeof(cl_mem), &partials);
1188 dt_opencl_set_kernel_arg(devid, gfin, 1, sizeof(cl_mem), &gnorm_dev);
1189 dt_opencl_set_kernel_arg(devid, gfin, 2, sizeof(int), &n_groups);
1190 dt_opencl_set_kernel_arg(devid, gfin, 3, sizeof(int), &gstride);
1191 dt_opencl_set_kernel_arg(devid, gfin, 4, sizeof(int), &gmode);
1192 dt_opencl_set_kernel_arg(devid, gfin, 5, sizeof(float), &gscale);
1193 cl_err = dt_opencl_enqueue_kernel_2d(devid, gfin, gone);
1194 if(cl_err != CL_SUCCESS) goto out;
1195 }
1196
1197 const int kernel_tensor = global_data->kernel_hl_aniso_tensor;
1198 dt_opencl_set_kernel_arg(devid, kernel_tensor, 0, sizeof(cl_mem), &tensor_xx);
1199 dt_opencl_set_kernel_arg(devid, kernel_tensor, 1, sizeof(cl_mem), &tensor_xy);
1200 dt_opencl_set_kernel_arg(devid, kernel_tensor, 2, sizeof(cl_mem), &scratch1); // tensor_xx out (reuse)
1201 dt_opencl_set_kernel_arg(devid, kernel_tensor, 3, sizeof(cl_mem), &scratch2); // tensor_xy out (reuse)
1202 dt_opencl_set_kernel_arg(devid, kernel_tensor, 4, sizeof(cl_mem), &tensor_yy);
1203 dt_opencl_set_kernel_arg(devid, kernel_tensor, 5, sizeof(int), &region_w);
1204 dt_opencl_set_kernel_arg(devid, kernel_tensor, 6, sizeof(int), &region_h);
1205 dt_opencl_set_kernel_arg(devid, kernel_tensor, 7, sizeof(cl_mem), &gnorm_dev);
1206 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel_tensor, size);
1207 if(cl_err != CL_SUCCESS) goto out;
1208 }
1209 // tensor now lives in (scratch1, scratch2, tensor_yy) = (tensor_xx, tensor_xy, tensor_yy)
1210
1211 // host symbolic: unknown list + ND ordering (reach 1: 8-neighbour stencil)
1212 grid_to_unknown = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * region_pixels, pipe);
1213 unknown_to_grid = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1214 unknown_x = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1215 unknown_y = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1216 perm = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1217 inverse_perm = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1218 matrix_col_ptr = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * (n_unknowns + 1), pipe);
1219 perm_grid = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * n_unknowns, pipe);
1220 edge_weights = (float *)dt_pixelpipe_cache_alloc_align(sizeof(float) * (size_t)n_unknowns * 8, pipe);
1221 if(!grid_to_unknown || !unknown_to_grid || !unknown_x || !unknown_y || !perm || !inverse_perm || !matrix_col_ptr
1222 || !perm_grid || !edge_weights)
1223 {
1224 cl_err = DT_OPENCL_DEFAULT_ERROR;
1225 goto out;
1226 }
1227 {
1228 int unknown_index = 0;
1229 for(size_t i = 0; i < region_pixels; i++)
1230 {
1231 grid_to_unknown[i] = hole_mask[i] ? unknown_index : -1;
1232 if(hole_mask[i])
1233 {
1234 unknown_to_grid[unknown_index] = (int)i;
1235 unknown_y[unknown_index] = (int)(i / region_w);
1236 unknown_x[unknown_index] = (int)(i - (size_t)unknown_y[unknown_index] * region_w);
1237 unknown_index++;
1238 }
1239 }
1240 for(int i = 0; i < n_unknowns; i++) perm[i] = i;
1241 _sp_nd_order(perm, n_unknowns, unknown_x, unknown_y, 1);
1242 for(int perm_index = 0; perm_index < n_unknowns; perm_index++) inverse_perm[perm[perm_index]] = perm_index;
1243 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
1244 perm_grid[perm_index] = unknown_to_grid[perm[perm_index]];
1245 }
1246
1247 // edge weights on the device (they steer the RHS kernels too), compact download for assembly
1248 perm_grid_dev = _sp_cl_upload(devid, perm_grid, sizeof(int) * n_unknowns);
1249 edge_weights_dev = dt_opencl_alloc_device_buffer(devid, sizeof(float) * (size_t)n_unknowns * 8);
1250 rhs_dev = dt_opencl_alloc_device_buffer(devid, sizeof(double) * n_unknowns);
1251 if(!perm_grid_dev || !edge_weights_dev || !rhs_dev)
1252 {
1253 cl_err = DT_OPENCL_DEFAULT_ERROR;
1254 goto out;
1255 }
1256 {
1257 const int kernel = global_data->kernel_hl_aniso_weights;
1258 size_t size_1d[3] = { ROUNDUP(n_unknowns, 64), 1, 1 };
1259 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch1);
1260 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch2);
1261 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &tensor_yy);
1262 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &perm_grid_dev);
1263 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &edge_weights_dev);
1264 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &n_unknowns);
1265 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_w);
1266 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_h);
1267 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_1d);
1268 if(cl_err != CL_SUCCESS) goto out;
1269 }
1270 cl_err = dt_opencl_read_buffer_from_device(devid, edge_weights, edge_weights_dev, 0,
1271 sizeof(float) * (size_t)n_unknowns * 8, CL_TRUE);
1272 if(cl_err != CL_SUCCESS) goto out;
1273
1274 // host assembly from the downloaded weights, exactly the CPU _aniso_div_solve pattern
1275 {
1276 static const int neighbour_dy[8] = { 0, 0, -1, 1, -1, 1, -1, 1 };
1277 static const int neighbour_dx[8] = { -1, 1, 0, 0, -1, 1, 1, -1 };
1278 int success = 1;
1279 for(int pass = 0; pass < 2 && success; pass++)
1280 {
1281 if(pass == 1)
1282 {
1283 int total = 0;
1284 for(int perm_index = 0; perm_index < n_unknowns; perm_index++)
1285 {
1286 const int c = matrix_col_ptr[perm_index];
1287 matrix_col_ptr[perm_index] = total;
1288 total += c;
1289 }
1290 matrix_col_ptr[n_unknowns] = total;
1291 matrix_row_index = (int *)dt_pixelpipe_cache_alloc_align(sizeof(int) * total, pipe);
1292 matrix_values = (double *)dt_pixelpipe_cache_alloc_align(sizeof(double) * total, pipe);
1293 if(!matrix_row_index || !matrix_values) success = 0;
1294 }
1295
1296 for(int perm_index = 0; perm_index < n_unknowns && success; perm_index++)
1297 {
1298 const int origin_grid = perm_grid[perm_index];
1299 const int origin_y = origin_grid / region_w, origin_x = origin_grid - origin_y * region_w;
1300 double diag = 0.0;
1301 int n_col_entries = 0;
1302
1303 for(int edge = 0; edge < 8; edge++)
1304 {
1305 const float weight_value = edge_weights[(size_t)perm_index * 8 + edge];
1306 // NaN-safe: !(weight_value > 0) also skips NaN weights (NaN pixels survive the blurs), which
1307 // 'weight_value <= 0' would let through into a wildly out-of-bounds grid_to_unknown read below
1308 if(!(weight_value > 0.f)) continue; // outside the border, a zeroed diagonal, or NaN
1309 const int neighbour_x = origin_x + neighbour_dx[edge], neighbour_y = origin_y + neighbour_dy[edge];
1310 if(neighbour_x < 0 || neighbour_y < 0 || neighbour_x >= region_w || neighbour_y >= region_h)
1311 continue; // same guard as the CPU
1312 diag += weight_value;
1313 const size_t j = (size_t)neighbour_y * region_w + neighbour_x;
1314 if(grid_to_unknown[j] >= 0)
1315 {
1316 const int target_row = inverse_perm[grid_to_unknown[j]];
1317 if(target_row < perm_index)
1318 {
1319 if(pass == 1)
1320 {
1321 matrix_row_index[matrix_col_ptr[perm_index] + n_col_entries] = target_row;
1322 matrix_values[matrix_col_ptr[perm_index] + n_col_entries] = -(double)weight_value;
1323 }
1324 n_col_entries++;
1325 }
1326 }
1327 }
1328 if(pass == 1)
1329 {
1330 matrix_row_index[matrix_col_ptr[perm_index] + n_col_entries] = perm_index;
1331 matrix_values[matrix_col_ptr[perm_index] + n_col_entries] = diag + (double)react;
1332 }
1333 n_col_entries++;
1334 if(pass == 0) matrix_col_ptr[perm_index] = n_col_entries;
1335 }
1336 }
1337 if(!success)
1338 {
1339 cl_err = DT_OPENCL_DEFAULT_ERROR;
1340 goto out;
1341 }
1342 }
1343
1344 factor = _sp_chol_factor_cl(devid, _hl_sp_chol_kernels(gd_void), n_unknowns, matrix_col_ptr, matrix_row_index,
1345 matrix_values);
1346 if(!factor)
1347 {
1348 cl_err = DT_OPENCL_DEFAULT_ERROR;
1349 goto out;
1350 }
1351
1352 for(int c = 0; c < 3; c++)
1353 {
1354 {
1355 const int kernel = global_data->kernel_hl_aniso_rhs;
1356 size_t size_1d[3] = { ROUNDUP(n_unknowns, 64), 1, 1 };
1357 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &edge_weights_dev);
1358 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid_packed);
1359 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &ratios);
1360 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &perm_grid_dev);
1361 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &rhs_dev);
1362 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &n_unknowns);
1363 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_w);
1364 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_h);
1365 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &c);
1366 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(float), &react);
1367 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(float), &react_target[c]);
1368 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_1d);
1369 if(cl_err != CL_SUCCESS) goto out;
1370 }
1371 if(_sp_chol_solve_cl(factor, _hl_sp_chol_kernels(gd_void), rhs_dev))
1372 {
1373 cl_err = DT_OPENCL_DEFAULT_ERROR;
1374 goto out;
1375 }
1376 {
1377 const int kernel = global_data->kernel_hl_aniso_scatter;
1378 size_t size_1d[3] = { ROUNDUP(n_unknowns, 64), 1, 1 };
1379 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &rhs_dev);
1380 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &perm_grid_dev);
1381 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &ratios);
1382 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &n_unknowns);
1383 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
1384 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_1d);
1385 if(cl_err != CL_SUCCESS) goto out;
1386 }
1387 }
1388
1389reassemble:;
1390 // Full-resolution projected polish, both solver paths (mirrors the CPU block): the
1391 // saturation floors active as an obstacle inside a short structure-steered relaxation, so
1392 // the field settles smoothly around the constraint instead of being clamped pointwise
1393 // at the reassembly.
1394 {
1395 cl_mem grad_y = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1396 cl_mem dobs3 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels * 3);
1397 cl_mem dhole3 = dt_opencl_alloc_device_buffer(devid, region_pixels * 3);
1398 cl_mem diffuse_a = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1399 cl_mem diffuse_b = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
1400 cl_mem ppart = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 256);
1401 cl_mem aflags = dt_opencl_alloc_device_buffer(devid, sizeof(int) * 3);
1402 if(!grad_y || !dobs3 || !dhole3 || !diffuse_a || !diffuse_b || !ppart || !aflags)
1403 cl_err = DT_OPENCL_DEFAULT_ERROR;
1404
1405 // full-res structure tensor of the recovered luminance (box3 x2, gradient, D)
1406 if(cl_err == CL_SUCCESS)
1407 {
1408 const int kernel = global_data->kernel_hl_box3;
1409 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &luminance);
1410 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch1);
1411 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
1412 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
1413 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1414 if(cl_err == CL_SUCCESS)
1415 {
1416 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch1);
1417 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch2);
1418 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1419 }
1420 }
1421 if(cl_err == CL_SUCCESS)
1422 {
1423 const int local_size = 64, n_groups = 256;
1424 const int kernel = global_data->kernel_hl_grad_reduce;
1425 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
1426 size_t local[3] = { local_size, 1, 1 };
1427 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &scratch2);
1428 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &scratch1); // gx
1429 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &grad_y); // grad_y
1430 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &ppart);
1431 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
1432 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
1433 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float) * local_size, NULL);
1434 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
1435 if(cl_err == CL_SUCCESS)
1436 {
1437 {
1438 const int gfin = global_data->kernel_hl_reduce_finalize;
1439 const int gstride = 1, gmode = 2, gn = 256;
1440 const float gscale = 1.f / (float)region_pixels;
1441 size_t gone[3] = { 1, 1, 1 };
1442 dt_opencl_set_kernel_arg(devid, gfin, 0, sizeof(cl_mem), &ppart);
1443 dt_opencl_set_kernel_arg(devid, gfin, 1, sizeof(cl_mem), &gnorm_dev);
1444 dt_opencl_set_kernel_arg(devid, gfin, 2, sizeof(int), &gn);
1445 dt_opencl_set_kernel_arg(devid, gfin, 3, sizeof(int), &gstride);
1446 dt_opencl_set_kernel_arg(devid, gfin, 4, sizeof(int), &gmode);
1447 dt_opencl_set_kernel_arg(devid, gfin, 5, sizeof(float), &gscale);
1448 cl_err = dt_opencl_enqueue_kernel_2d(devid, gfin, gone);
1449 if(cl_err != CL_SUCCESS) goto out;
1450 const int kernel_tensor = global_data->kernel_hl_aniso_tensor;
1451 dt_opencl_set_kernel_arg(devid, kernel_tensor, 0, sizeof(cl_mem), &scratch1);
1452 dt_opencl_set_kernel_arg(devid, kernel_tensor, 1, sizeof(cl_mem), &grad_y);
1453 dt_opencl_set_kernel_arg(devid, kernel_tensor, 2, sizeof(cl_mem), &tensor_xx);
1454 dt_opencl_set_kernel_arg(devid, kernel_tensor, 3, sizeof(cl_mem), &tensor_xy);
1455 dt_opencl_set_kernel_arg(devid, kernel_tensor, 4, sizeof(cl_mem), &tensor_yy);
1456 dt_opencl_set_kernel_arg(devid, kernel_tensor, 5, sizeof(int), &region_w);
1457 dt_opencl_set_kernel_arg(devid, kernel_tensor, 6, sizeof(int), &region_h);
1458 dt_opencl_set_kernel_arg(devid, kernel_tensor, 7, sizeof(cl_mem), &gnorm_dev);
1459 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel_tensor, size);
1460 }
1461 }
1462 }
1463 if(cl_err == CL_SUCCESS)
1464 {
1465 const int kernel = global_data->kernel_hl_aniso_obs_full;
1466 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &valid_packed);
1467 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &clip0);
1468 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &luminance);
1469 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &dobs3);
1470 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &dhole3);
1471 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_w);
1472 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_h);
1473 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), &epsilon);
1474 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1475 }
1476
1477 // Activity gate (mirrors the CPU block): a channel whose obstacle can never fire skips
1478 // its 60 full-res sweeps entirely -- the field is already settled by the solvers.
1479 int active[3] = { 0, 0, 0 };
1480 if(cl_err == CL_SUCCESS)
1481 {
1482 cl_err = dt_opencl_write_buffer_to_device(devid, active, aflags, 0, sizeof(int) * 3, CL_TRUE);
1483 if(cl_err == CL_SUCCESS)
1484 {
1485 const int kernel = global_data->kernel_hl_aniso_obs_flags;
1486 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &ratios);
1487 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &dobs3);
1488 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &dhole3);
1489 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &aflags);
1490 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
1491 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
1492 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1493 }
1494 if(cl_err == CL_SUCCESS)
1495 cl_err = dt_opencl_read_buffer_from_device(devid, active, aflags, 0, sizeof(int) * 3, CL_TRUE);
1496 }
1497 // the reaction must run its sweeps even where the obstacle can never fire (CPU react_on)
1498 if(react > 0.f) active[0] = active[1] = active[2] = 1;
1499
1500 size_t size_box[3]
1501 = { ROUNDUPDWD(box_x_hi - box_x_lo + 1, devid), ROUNDUPDHT(box_y_hi - box_y_lo + 1, devid), 1 };
1502 for(int c = 0; c < 3 && cl_err == CL_SUCCESS; c++)
1503 {
1504 if(!active[c]) continue;
1505 {
1506 const int kernel = global_data->kernel_hl_pyr_getc4;
1507 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &ratios);
1508 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &diffuse_a);
1509 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
1510 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
1511 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
1512 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1513 }
1514 if(cl_err == CL_SUCCESS)
1515 {
1516 const int kernel = global_data->kernel_hl_pyr_project;
1517 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &diffuse_a);
1518 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &dobs3);
1519 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &dhole3);
1520 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
1521 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
1522 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &c);
1523 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1524 }
1525 if(cl_err == CL_SUCCESS)
1526 cl_err = dt_opencl_enqueue_copy_buffer_to_buffer(devid, diffuse_a, diffuse_b, 0, 0,
1527 sizeof(float) * region_pixels);
1528
1529 cl_mem current_buf = diffuse_a, other_buf = diffuse_b;
1530 for(int iter = 0; iter < 60 && cl_err == CL_SUCCESS; iter++)
1531 {
1532 const int kernel = global_data->kernel_hl_aniso_iter;
1533 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &current_buf);
1534 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &other_buf);
1535 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &dhole3);
1536 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &tensor_xx);
1537 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &tensor_xy);
1538 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &tensor_yy);
1539 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &dobs3);
1540 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_w);
1541 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &region_h);
1542 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &c);
1543 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &box_x_lo);
1544 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(int), &box_y_lo);
1545 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(int), &box_x_hi);
1546 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(int), &box_y_hi);
1547 dt_opencl_set_kernel_arg(devid, kernel, 14, sizeof(float), &react);
1548 dt_opencl_set_kernel_arg(devid, kernel, 15, sizeof(float), &react_target[c]);
1549 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_box);
1550 cl_mem swap_buf = current_buf;
1551 current_buf = other_buf;
1552 other_buf = swap_buf;
1553 }
1554 if(cl_err == CL_SUCCESS)
1555 {
1556 const int kernel = global_data->kernel_hl_pyr_putc4;
1557 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &current_buf);
1558 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &ratios);
1559 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
1560 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
1561 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &c);
1562 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1563 }
1564 }
1565
1573 if(cl_err != CL_SUCCESS) goto out;
1574 }
1575
1576 {
1577 const int kernel = global_data->kernel_hl_aniso_reassemble;
1578 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
1579 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid_packed);
1580 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &luminance);
1581 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &ratios);
1582 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &clip0);
1583 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_w);
1584 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_h);
1585 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), &epsilon);
1586 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(float), &floor_gate);
1587 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
1588 }
1589
1590out:
1592 dt_opencl_release_mem_object(valid_packed);
1602 dt_opencl_release_mem_object(perm_grid_dev);
1603 dt_opencl_release_mem_object(edge_weights_dev);
1606 dt_pixelpipe_cache_free_align(grid_to_unknown);
1607 dt_pixelpipe_cache_free_align(unknown_to_grid);
1611 dt_pixelpipe_cache_free_align(inverse_perm);
1612 dt_pixelpipe_cache_free_align(matrix_col_ptr);
1613 dt_pixelpipe_cache_free_align(matrix_row_index);
1614 dt_pixelpipe_cache_free_align(matrix_values);
1616 dt_pixelpipe_cache_free_align(edge_weights);
1618 return cl_err;
1619}
1620
1621#endif // HAVE_OPENCL && DT_HL_SPARSE_SOLVE && ANISO_SOLVER 2
__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, const float react, const float react_target)
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
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 float react, const dt_aligned_pixel_t react_target, const dt_dev_pixelpipe_t *pipe)
Definition chroma.c:161
__DT_CLONE_TARGETS__ void _aniso_chroma(_hl_region_ctx_t *const ctx)
Definition chroma.c:336
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:55
static const float x
const dt_colormatrix_t dt_aligned_pixel_t out
const float delta
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
Definition eaw.c:29
static int perm[512]
Definition grain.c:174
static int permutation[]
Definition grain.c:160
static float kernel(const float *x, const float *y)
float *const restrict luminance
float *const restrict const size_t k
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:2554
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2970
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:2738
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:2727
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:2545
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:2560
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:2714
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
#define DT_OPENCL_DEFAULT_ERROR
Definition opencl.h:61
#define ROUNDUP(a, n)
Definition opencl.h:82
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
static _sp_chol_cl_kernels_t _hl_sp_chol_kernels(void *gd_void)
Definition pde.h:107
const float factor
Definition pdf.h:91
#define dt_pixelpipe_cache_alloc_align(size, pipe)
#define dt_pixelpipe_cache_free_align(mem)
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
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 void _sp_chol_free(_sp_chol_t *factor)
static void _sp_chol_solve(const _sp_chol_t *const factor, double *const restrict rhs)
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 int cache_id)
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
dt_dev_pixelpipe_type_t type
#define __DT_CLONE_TARGETS__
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