Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
core.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// Self-dome fallback and all-clip joint core stages (CPU + OpenCL). (implementation; see core.h for the public
20// API.)
21
22#include "common/darktable.h"
24#include "control/control.h"
25#include "develop/imageop.h"
27#include "iop/highlights/blur.h"
29#include "iop/highlights/core.h"
30#include "iop/highlights/dome.h"
31#include "iop/highlights/knee.h"
32#include "iop/highlights/pde.h"
33#include <math.h>
34#include <string.h>
35
38{
39 const _hl_region_t *const region = ctx->region;
40 const dt_dev_pixelpipe_t *const pipe = ctx->pipe;
41 const int region_w = ctx->region_w;
42 const int region_h = ctx->region_h;
43 const size_t region_pixels = ctx->region_pixels;
44 const float epsilon = ctx->epsilon;
45 float *const restrict estimate = ctx->estimate;
46 float *const restrict valid = ctx->valid;
47 float *const restrict plane1 = ctx->plane1;
48 float *const restrict valid_variance = ctx->valid_variance;
49 float *const restrict clip0 = ctx->clip0;
50 uint8_t *const restrict hole = ctx->hole;
51 float *const restrict solver_field = ctx->solver_field;
52 float *const restrict dome_lum = ctx->dome_lum;
53 float *const restrict lum_accum = ctx->lum_accum;
54 float *const restrict flat_target = ctx->flat_target;
55
56 // --- decide whether the per-channel self-dome fallback is worth solving ---
57 // It only matters where a channel is clipped, a guide survives, yet the colour-line is
58 // weak (We = Wc^2 well below 1): decorrelated content. Correlated content stays on the
59 // guide (We ~ 1), so skip the three biharmonic solves entirely -- the common case.
60 int need_self = 0;
61 for(size_t i = 0; i < region_pixels; i++)
62 {
63 const int anyvalid = (valid[i * 4 + 0] >= 0.5f) || (valid[i * 4 + 1] >= 0.5f) || (valid[i * 4 + 2] >= 0.5f);
64 if(!anyvalid) continue;
65 for(int c = 0; c < 3; c++)
66 if(valid[i * 4 + c] < 0.5f && valid_variance[i * 4 + c] * valid_variance[i * 4 + c] < 0.9f) need_self = 1;
67 if(need_self) break;
68 }
69
70 // --- self-dome fallback, only if needed ---
71 if(need_self)
72 {
73 // One SHARED downsampling factor sized from the UNION (any-clip) hole -- the largest, so
74 // the coarse grid stays within DT_HL_DOME_NMAX and every channel is approximated at the
75 // same resolution.
76 size_t nh_union = 0;
77 for(size_t i = 0; i < region_pixels; i++)
78 if(valid[i * 4 + 0] < 0.5f || valid[i * 4 + 1] < 0.5f || valid[i * 4 + 2] < 0.5f) nh_union++;
79
80 const int ds_shared = MAX(1, (int)ceilf(sqrtf((float)nh_union / (float)DT_HL_DOME_NMAX_SPARSE)));
81
82 // HUE-COUPLED dome: three independently-domed channels can drift apart exactly where the
83 // fallback engages (a low-R^2 zone), splitting the hue toward green/magenta -- the original
84 // failure this fallback used to be disabled for. Instead dome ONE shared quantity per kind:
85 // the LUMINANCE (biharmonic, gradient-extending) and a SMOOTH chromaticity (harmonic fill
86 // of the ratios from the rim). dome_c = L_dome * chroma_c: every channel shares the same
87 // shape, so the fallback cannot drift the hue by construction.
88 HL_PFOR()
89 for(size_t i = 0; i < region_pixels; i++)
90 {
91 hole[i] = (valid[i * 4 + 0] < 0.5f || valid[i * 4 + 1] < 0.5f || valid[i * 4 + 2] < 0.5f);
92 lum_accum[i] = estimate[i * 4 + 0] + estimate[i * 4 + 1] + estimate[i * 4 + 2]; // L_sum = R+G+B
93 solver_field[i] = lum_accum[i];
94 }
95
96 // one shared biharmonic BRIGHTNESS dome over the union hole: Delta^2 L_sum = 0 with the
97 // valid rim as Dirichlet data (term 2 of E_bihar, hue-coupled form). Doming L_sum once and
98 // reusing it for all channels is what prevents three per-channel domes drifting the hue.
99 _biharmonic_dome(solver_field, hole, region_w, region_h, ds_shared, pipe);
100 memcpy(dome_lum, solver_field, region_pixels * sizeof(float));
101
102 // smooth chromaticity over the union hole (ratio planes stored in s1's 4-ch layout): each
103 // channel's ratio r_c = est_c / L_sum is a BOUNDED quantity, so a plain harmonic fill (flat
104 // rim-matched inpaint, no biharmonic doming) is the right tool -- brightness gets the dome,
105 // colour gets the harmonic fill, and recombining as dome_c = L_dome * r_c couples the hue.
106 const int cf_base = (int)(CLAMP(region->radius / 6.f, 8.f, 64.f) / 4.f);
107
108 for(int c = 0; c < 3; c++)
109 {
110 HL_PFOR()
111 for(size_t i = 0; i < region_pixels; i++)
112 flat_target[i] = estimate[i * 4 + c] / fmaxf(lum_accum[i], epsilon); // ratio r_c = est_c / L_sum
113
114 _cf_harmonic_fill(flat_target, hole, region_w, region_h, cf_base, NULL, pipe); // harmonic (Delta r = 0)
115
116 HL_PFOR()
117 for(size_t i = 0; i < region_pixels; i++) plane1[i * 4 + c] = fmaxf(flat_target[i], 0.f);
118 }
119
120 // recombine dome_c = L_dome * (r_c / sum r) and blend it into the estimate by the depth-gated
121 // KEEP weight conf_weight = Wc^2 (= 1 - dome_fraction of step 6): est = keep*est + (1-keep)*dome.
122 // A pixel with no surviving guide takes the dome outright (the all-clip core rebuilds it just after).
123 HL_PFOR()
124 for(size_t i = 0; i < region_pixels; i++)
125 {
126 if(!hole[i]) continue;
127
128 const float caccum = fmaxf(plane1[i * 4 + 0] + plane1[i * 4 + 1] + plane1[i * 4 + 2], epsilon); // sum r
129 const int anyvalid = (valid[i * 4 + 0] >= 0.5f) || (valid[i * 4 + 1] >= 0.5f) || (valid[i * 4 + 2] >= 0.5f);
130
131 for(int c = 0; c < 3; c++)
132 if(valid[i * 4 + c] < 0.5f)
133 {
134 const float dome = dome_lum[i] * (plane1[i * 4 + c] / caccum); // dome_c = L_dome * chroma share
135 const float conf_weight = valid_variance[i * 4 + c] * valid_variance[i * 4 + c]; // keep = Wc^2
136 estimate[i * 4 + c] = anyvalid ? (conf_weight * estimate[i * 4 + c] + (1.f - conf_weight) * dome) : dome;
137 }
138 }
139
140 // Re-assert the saturation floor AFTER the self dome (the prototype floors here): the dome only
141 // continues the valid rim, it does not know about saturation, so it can undershoot a clipped
142 // channel below its clip level. Monotone (only raises), so it never overshoots or drifts hue.
144 for(size_t i = 0; i < region_pixels; i++)
145 for(int c = 0; c < 3; c++)
146 if(valid[i * 4 + c] < 0.5f) estimate[i * 4 + c] = fmaxf(estimate[i * 4 + c], clip0[i * 4 + c]);
147 }
148}
149
152{
153 const _hl_region_t *const region = ctx->region;
154 const dt_dev_pixelpipe_t *const pipe = ctx->pipe;
155 const int region_w = ctx->region_w;
156 const int region_h = ctx->region_h;
157 const size_t region_pixels = ctx->region_pixels;
158 const float epsilon = ctx->epsilon;
159 const int max_cg_iter = ctx->max_cg_iter;
160 const float solid_color = ctx->solid_color;
161 float *const restrict estimate = ctx->estimate;
162 float *const restrict valid = ctx->valid;
163 float *const restrict plane1 = ctx->plane1;
164 float *const restrict clip0 = ctx->clip0;
165 uint8_t *const restrict hole = ctx->hole;
166 float *const restrict solver_field = ctx->solver_field;
167 float *const restrict dome_lum = ctx->dome_lum;
168 float *const restrict lum_accum = ctx->lum_accum;
169 float *const restrict reaction_weight = ctx->reaction_weight;
170 float *const restrict flat_target = ctx->flat_target;
171 float *const restrict cg_residual = ctx->cg_residual;
172 float *const restrict cg_dir = ctx->cg_dir;
173 float *const restrict cg_operator = ctx->cg_operator;
174 float *const restrict cg_tmp1 = ctx->cg_tmp1;
175 float *const restrict cg_tmp2 = ctx->cg_tmp2;
176
177 // --- all-clipped core: shared biharmonic luminance dome x diffused chromaticity ---
178 // Only pixels with NO surviving channel. Extending this to 2-clip pixels was tried and reverted:
179 // the bright sky is itself 2-clip (R,G clipped, B not), so it got swept into the coupled core
180 // and filled with diffused magenta chroma that bled into the sky. 2-clip pixels keep their
181 // (two-or-one-guide) guided/self-dome estimate; only the truly guide-less core is rebuilt here.
182 //
183 // MATHS BRIDGE -- Step 7 all-clip core (article §"Filling holes with no survivor", §"The
184 // algorithm" step 7). Magnitude and chrominance are split and reconstructed by different
185 // operators: ONE shared biharmonic luminance dome L_dome (Delta^2 L_sum = 0, E_bihar) for the
186 // magnitude common to all three channels, and the screened-Poisson rim-diffused chrominance
187 // r = RGB/L_sum ((lambda*I-Delta) r = lambda_solid*r_target, E_chrominance) carried inward from
188 // the reconstructed annulus. Recombination core_c = L_dome * (r_c / sum_j r_j), then a feathered
189 // blurred hand-over into the surrounding coefficient-field reconstruction (no hard core rim).
190 int has_allc = 0;
191 __OMP_PARALLEL_FOR__(reduction(| : has_allc))
192 for(size_t i = 0; i < region_pixels; i++)
193 {
194 hole[i] = (valid[i * 4 + 0] < 0.5f && valid[i * 4 + 1] < 0.5f && valid[i * 4 + 2] < 0.5f);
195 if(hole[i]) has_allc = 1;
196 }
197
198 if(has_allc)
199 {
200 // one shared luminance dome (biharmonic) from the reconstructed annulus rim
201 // L_sum = R + G + B (the summed luminance, the magnitude shared by all three channels)
203 for(size_t i = 0; i < region_pixels; i++)
204 {
205 lum_accum[i] = estimate[i * 4 + 0] + estimate[i * 4 + 1] + estimate[i * 4 + 2];
206 solver_field[i] = lum_accum[i];
207 }
208
209 // Delta^2 L_sum = 0 on the core, L_sum|dOmega = L_valid on the reconstructed annulus rim:
210 // E_bihar magnitude dome (one scalar solve, not three, so no channel collapses off-hue)
211 _biharmonic_dome(solver_field, hole, region_w, region_h, 0,
212 pipe); // shared biharmonic luminance dome (auto ds)
213 memcpy(dome_lum, solver_field, region_pixels * sizeof(float));
214
215 // The all-clip core has EVERY channel saturated, so its luminance is at least the accum of the
216 // clip levels -- the brightest, not something to extrapolate downward. The biharmonic dome can
217 // dip below that (the floored rim has no upward gradient to continue), which darkens the centre
218 // below the annulus. Floor the dome at the saturated accum so the core is never darker than "all
219 // channels at clip". Above-clip doming is kept where the dome exceeds it.
221 for(size_t i = 0; i < region_pixels; i++)
222 if(hole[i])
223 {
224 // saturation floor on the dome: L_dome >= sum_c clip0_c ("all three channels at clip",
225 // the brightest the core can be); monotone, so it never dims a valid rim or shifts hue
226 const float lsat = clip0[i * 4 + 0] + clip0[i * 4 + 1] + clip0[i * 4 + 2];
227 dome_lum[i] = fmaxf(dome_lum[i], lsat);
228 }
229
230 // mean valid chromaticity -> flat target for the "inpaint a flat color" slider
231 // r_target = <RGB/L_sum> over fully-valid pixels: the screened-Poisson reaction pulls the
232 // core chroma toward this flat colour (article's bar-c_c, the mean valid chromaticity)
233 // accumulate in DOUBLE: a float running accum of ~1e5 terms carries an ULP of ~4e-3 per
234 // add near its final magnitude, which biased the mean by ~1e-4 relative (enough to show
235 // as a 4e-4 CPU-vs-GPU divergence on the reaction target)
236 dt_aligned_pixel_t cmean = { 0.f, 0.f, 0.f, 0.f };
237 double cacc[3] = { 0.0, 0.0, 0.0 };
238 double count = 0.0;
239 for(size_t i = 0; i < region_pixels; i++)
240 {
241 if(!(valid[i * 4 + 0] >= 0.5f && valid[i * 4 + 1] >= 0.5f && valid[i * 4 + 2] >= 0.5f)) continue;
242 const float invL = 1.f / fmaxf(lum_accum[i], epsilon);
243 cacc[0] += (double)(estimate[i * 4 + 0] * invL);
244 cacc[1] += (double)(estimate[i * 4 + 1] * invL);
245 cacc[2] += (double)(estimate[i * 4 + 2] * invL);
246 count += 1.0;
247 }
248 if(count > 0.0)
249 for(int c = 0; c < 3; c++) cmean[c] = (float)(cacc[c] / count);
250
251 // chromaticity: harmonic diffusion from the rim, with a screened-Poisson reaction
252 // pulling the core hue toward the flat mean by solid_color ("inpaint a flat color").
253 // react = lambda_solid = solid_color^2 * 4: the screening strength; 0 -> pure harmonic
254 // (Delta r = 0), larger -> a flatter, more uniform "solid colour" fill
255 const float react = solid_color * solid_color * 4.f;
257 for(size_t i = 0; i < region_pixels; i++) reaction_weight[i] = react;
258
259 // factor A = lambda_solid*I - Delta (order 1) ONCE; it serves the three channels (same matrix,
260 // three right-hand sides) -- the direct solve is EXACT where the float CG stopped at a tolerance
261 int *sp_pgrid = NULL;
262 int sp_nh = 0;
263 _sp_chol_t *sp_S = _sp_pde_factor(hole, (react > 0.f) ? reaction_weight : NULL, 1, 1.f, region_w, region_h,
264 &sp_pgrid, &sp_nh, pipe);
265 double *sp_b = sp_S ? (double *)dt_pixelpipe_cache_alloc_align(sizeof(double) * sp_nh, ctx->pipe) : NULL;
266 if(sp_S && !sp_b)
267 {
268 _sp_chol_free(sp_S);
269 sp_S = NULL;
270 }
271
272 for(int c = 0; c < 3; c++)
273 {
275 for(size_t i = 0; i < region_pixels; i++)
276 {
277 // boundary (Dirichlet) = the real rim chroma r_valid = est_c/L_sum; hole initial guess =
278 // the mean valid (amber) chroma r_target, so an under-converged core centre biases to
279 // amber, never to the guided magenta
280 solver_field[i] = hole[i] ? cmean[c] : (estimate[i * 4 + c] / fmaxf(lum_accum[i], epsilon));
281 flat_target[i] = cmean[c]; // r_target plane for the screening reaction term
282 }
283
284 // solve (lambda_solid*I - Delta) r_c = lambda_solid*r_target on the hole, r_c|dOmega = r_valid
285 if(sp_S)
286 _sp_pde_solve(sp_S, sp_pgrid, solver_field, hole, (react > 0.f) ? reaction_weight : NULL,
287 (react > 0.f) ? flat_target : NULL, NULL, 1, 1.f, region_w, region_h, sp_b, cg_tmp1, cg_tmp2,
288 cg_residual);
289 else
290 _region_pde_solve(solver_field, hole, (react > 0.f) ? reaction_weight : NULL,
291 (react > 0.f) ? flat_target : NULL, NULL, 1, 1.f, region_w, region_h, cg_residual,
292 cg_dir, cg_operator, cg_tmp1, cg_tmp2, max_cg_iter);
293
295 for(size_t i = 0; i < region_pixels; i++) plane1[i * 4 + c] = fmaxf(solver_field[i], 0.f);
296 }
297
298 _sp_chol_free(sp_S);
301
302 // FEATHERED composite: a hard all-clip mask makes the core <-> annulus hand-off a seam by
303 // construction. The dome (ldb ~ lsb outside the hole) and the diffused chroma (s1 = real
304 // ratios outside) are both valid past the hole boundary, so blending them in over a
305 // blurred mask is continuous in space at no cost to the core rebuild itself.
306 // core mask -> 1 inside, 0 outside; blurred into a smooth feather weight (the one smooth
307 // weight in the method: it blends two RECONSTRUCTIONS, never reclassifies measurements)
309 for(size_t i = 0; i < region_pixels; i++) solver_field[i] = hole[i] ? 1.f : 0.f;
310
311 _knee_blur(solver_field, reaction_weight, region_w, region_h,
312 fmaxf(4.f, CLAMP(region->radius / 6.f, 8.f, 64.f) / 4.f));
313
315 for(size_t i = 0; i < region_pixels; i++)
316 {
317 const float fit_weight = CLAMP(reaction_weight[i], 0.f, 1.f); // feather alpha (blurred core mask)
318 const float caccum = fmaxf(plane1[i * 4 + 0] + plane1[i * 4 + 1] + plane1[i * 4 + 2], epsilon); // sum_j r_j
319
320 if(hole[i])
321 {
322 // interior: core rebuild, full strength: core_c = L_dome * (r_c / sum_j r_j) (RGB = L*r)
323 for(int c = 0; c < 3; c++) estimate[i * 4 + c] = dome_lum[i] * (plane1[i * 4 + c] / caccum);
324 }
325 else if(fit_weight > 1e-4f)
326 {
327 // feather ring outside the core: alpha*core_c + (1-alpha)*est, on CLIPPED channels of
328 // the surrounding reconstruction only -- valid data is never touched
329 for(int c = 0; c < 3; c++)
330 if(valid[i * 4 + c] < 0.5f)
331 estimate[i * 4 + c] = fit_weight * dome_lum[i] * (plane1[i * 4 + c] / caccum)
332 + (1.f - fit_weight) * estimate[i * 4 + c];
333 }
334 }
335 }
336}
337
338// ============================ OpenCL ============================
339
340#if defined(HAVE_OPENCL) && DT_HL_SPARSE_SOLVE
341cl_int _selfdome_stage_cl(const int devid, void *gd_void, cl_mem estimate, cl_mem valid, cl_mem model_quality,
342 cl_mem clip0, cl_mem depth, const int region_w, const int region_h, const float cf_sigma,
343 const float reg_radius, const int ds_shared, const dt_dev_pixelpipe_t *pipe)
344{
346 const size_t region_pixels = (size_t)region_w * region_h;
347 cl_int cl_err = DT_OPENCL_DEFAULT_ERROR;
348 size_t size[3] = { ROUNDUPDWD(region_w, devid), ROUNDUPDHT(region_h, devid), 1 };
349 const float epsilon = 1e-6f;
350
351 cl_mem luminance = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
352 cl_mem hole = dt_opencl_alloc_device_buffer(devid, region_pixels);
353 cl_mem dome_lum = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
354 cl_mem ratio0 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
355 cl_mem ratio1 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
356 cl_mem ratio2 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
357 cl_mem ratios[3];
358 ratios[0] = ratio0;
359 ratios[1] = ratio1;
360 ratios[2] = ratio2;
361 if(!luminance || !hole || !dome_lum || !ratio0 || !ratio1 || !ratio2) goto out;
362
363 // soft floor first (production order: floor -> dome gate -> self dome)
364 {
365 const int kernel = global_data->kernel_hl_soft_floor;
366 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
367 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
368 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &clip0);
369 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
370 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
371 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
372 if(cl_err != CL_SUCCESS) goto out;
373 }
374
375 // brightness plane (sum of the three channels) + union hole mask (any clipped channel)
376 {
377 const int kernel = global_data->kernel_hl_lsb_hole;
378 const int allmode = 0; // union hole: ANY clipped channel
379 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
380 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
381 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &luminance);
382 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &hole);
383 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
384 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
385 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &allmode);
386 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
387 if(cl_err != CL_SUCCESS) goto out;
388 }
389
390 // debug dump (HL_REG_DUMP=<file path>): save this region's brightness plane + hole mask
391 // to the given file for offline replay through the HL_DOMECL_TEST self-test (the path is
392 // taken from the variable itself: no fixed world-writable location)
393 const char *reg_dump_path = getenv("HL_REG_DUMP");
394 if(reg_dump_path && reg_dump_path[0])
395 {
396 float *dump_data = dt_pixelpipe_cache_alloc_align_float(region_pixels, pipe);
397 uint8_t *dump_hole = (uint8_t *)dt_pixelpipe_cache_alloc_align(region_pixels, pipe);
398 if(dump_data && dump_hole
399 && dt_opencl_read_buffer_from_device(devid, dump_data, luminance, 0, sizeof(float) * region_pixels, CL_TRUE)
400 == CL_SUCCESS
401 && dt_opencl_read_buffer_from_device(devid, dump_hole, hole, 0, region_pixels, CL_TRUE) == CL_SUCCESS)
402 {
403 FILE *dump_file = g_fopen(reg_dump_path, "wb");
404 if(dump_file)
405 {
406 fwrite(&region_w, sizeof(int), 1, dump_file);
407 fwrite(&region_h, sizeof(int), 1, dump_file);
408 const int downsample_val = ds_shared;
409 fwrite(&downsample_val, sizeof(int), 1, dump_file);
410 fwrite(dump_data, sizeof(float), region_pixels, dump_file);
411 fwrite(dump_hole, 1, region_pixels, dump_file);
412 fclose(dump_file);
413 }
414 }
417 }
418 // shared biharmonic brightness dome over the union hole (GPU sparse Cholesky inside)
419 cl_err
420 = dt_opencl_enqueue_copy_buffer_to_buffer(devid, luminance, dome_lum, 0, 0, sizeof(float) * region_pixels);
421 if(cl_err != CL_SUCCESS) goto out;
422 cl_err = _biharmonic_dome_cl(devid, gd_void, dome_lum, hole, region_w, region_h, ds_shared, pipe);
423 if(cl_err != CL_SUCCESS) goto out;
424
425 // harmonically filled chromaticity ratios over the union hole
426 {
427 const int cf_base = (int)(CLAMP(reg_radius / 6.f, 8.f, 64.f) / 4.f);
428 for(int c = 0; c < 3 && cl_err == CL_SUCCESS; c++)
429 {
430 const int kernel = global_data->kernel_hl_ratio_plane;
431 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
432 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &luminance);
433 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &ratios[c]);
434 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
435 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
436 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &c);
437 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), &epsilon);
438 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
439 if(cl_err == CL_SUCCESS)
440 cl_err = _cf_harmonic_fill_cl(devid, gd_void, ratios[c], hole, region_w, region_h, cf_base, 1, NULL);
441 }
442 if(cl_err != CL_SUCCESS) goto out;
443 }
444
445 // depth-gated blend: dome value x filled ratios replaces the estimate where the fit is
446 // doubtful and the pixel is shallow enough for the dome to be trustworthy
447 {
448 const int kernel = global_data->kernel_hl_dome_blend;
449 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
450 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
451 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &model_quality);
452 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &depth);
453 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &dome_lum);
454 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &ratio0);
455 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &ratio1);
456 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), &ratio2);
457 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(cl_mem), &hole);
458 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &region_w);
459 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(int), &region_h);
460 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(float), &cf_sigma);
461 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(float), &epsilon);
462 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
463 if(cl_err != CL_SUCCESS) goto out;
464 }
465
466 // hard floor re-assert: a clipped channel saturated, so its true value is >= its clip level
467 {
468 const int kernel = global_data->kernel_hl_hard_floor;
469 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
470 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
471 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &clip0);
472 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
473 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
474 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
475 }
476
477out:
484 return cl_err;
485}
486
487#endif // HAVE_OPENCL && DT_HL_SPARSE_SOLVE
488
489#if defined(HAVE_OPENCL) && DT_HL_SPARSE_SOLVE
490
491#endif // HAVE_OPENCL && DT_HL_SPARSE_SOLVE
492
493#if defined(HAVE_OPENCL) && DT_HL_SPARSE_SOLVE
494cl_int _joint_core_stage_cl(const int devid, void *gd_void, cl_mem estimate, cl_mem valid, cl_mem clip0,
495 const int region_w, const int region_h, const float solid_color,
496 const float reg_radius, const int extent, const dt_dev_pixelpipe_t *pipe)
497{
499 const size_t region_pixels = (size_t)region_w * region_h;
500 cl_int cl_err = DT_OPENCL_DEFAULT_ERROR;
501 size_t size[3] = { ROUNDUPDWD(region_w, devid), ROUNDUPDHT(region_h, devid), 1 };
502 const float epsilon = 1e-6f;
503 const float react
504 = solid_color * solid_color * 4.f; // lambda_solid: the screened-Poisson reaction (flat-colour pull)
505
506 if(global_data->kernel_hl_pde_rhs < 0 || global_data->kernel_hl_pde_scatter < 0) return cl_err; // no fp64 device
507
508 cl_mem luminance = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
509 cl_mem hole = dt_opencl_alloc_device_buffer(devid, region_pixels);
510 cl_mem dome_lum = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
511 cl_mem embedded = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
512 cl_mem ratio0 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
513 cl_mem ratio1 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
514 cl_mem ratio2 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
515 cl_mem cg_field = dt_opencl_alloc_device_buffer(devid, sizeof(float) * region_pixels);
516 cl_mem ratios[3];
517 ratios[0] = ratio0;
518 ratios[1] = ratio1;
519 ratios[2] = ratio2;
520 cl_mem partial_sums = NULL, perm_grid_dev = NULL, rhs_dev = NULL, mask_img = NULL, mask_blur = NULL;
521 uint8_t *hole_mask = (uint8_t *)dt_pixelpipe_cache_alloc_align(region_pixels, pipe);
522 int *matrix_col_ptr = NULL, *matrix_row_index = NULL, *perm_grid = NULL;
523 double *matrix_values = NULL;
524 _sp_chol_cl_t *factor = NULL;
525 dt_aligned_pixel_t chroma_mean = { 0.f, 0.f, 0.f, 0.f };
526 if(!luminance || !hole || !dome_lum || !embedded || !ratio0 || !ratio1 || !ratio2 || !cg_field || !hole_mask)
527 goto out;
528
529 // luminance + ALL-clip hole (no surviving channel)
530 {
531 const int kernel = global_data->kernel_hl_lsb_hole;
532 const int all_clip_mode = 1;
533 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
534 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
535 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &luminance);
536 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &hole);
537 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
538 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
539 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &all_clip_mode);
540 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
541 if(cl_err != CL_SUCCESS) goto out;
542 }
543
544 // the sparse symbolic analysis needs the mask on the host anyway; it also gives the
545 // all-clip count for the early exit and the CPU's auto grid factor for the dome
546 cl_err = dt_opencl_read_buffer_from_device(devid, hole_mask, hole, 0, region_pixels, CL_TRUE);
547 if(cl_err != CL_SUCCESS) goto out;
548
549 size_t n_hole_fine = 0;
550 for(size_t i = 0; i < region_pixels; i++)
551 if(hole_mask[i]) n_hole_fine++;
552 if(n_hole_fine == 0)
553 {
554 cl_err = CL_SUCCESS;
555 goto out;
556 }
557 const int downsample = MAX(1, (int)ceilf(sqrtf((float)n_hole_fine / (float)DT_HL_DOME_NMAX_SPARSE)));
558
559 // shared biharmonic luminance dome, floored at "all channels at clip"
560 cl_err
561 = dt_opencl_enqueue_copy_buffer_to_buffer(devid, luminance, dome_lum, 0, 0, sizeof(float) * region_pixels);
562 if(cl_err != CL_SUCCESS) goto out;
563 cl_err = _biharmonic_dome_cl(devid, gd_void, dome_lum, hole, region_w, region_h, downsample, pipe);
564 if(cl_err != CL_SUCCESS) goto out;
565 {
566 const int kernel = global_data->kernel_hl_core_floor;
567 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &dome_lum);
568 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &hole);
569 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &clip0);
570 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_w);
571 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_h);
572 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
573 if(cl_err != CL_SUCCESS) goto out;
574 }
575
576 // mean valid chromaticity: device partial sums, host finish
577 {
578 const int local_size = 64, n_groups = 256;
579 const int n_pixels = (int)region_pixels;
580 partial_sums = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * n_groups);
581 if(!partial_sums)
582 {
584 goto out;
585 }
586 const int kernel = global_data->kernel_hl_cmean_reduce;
587 size_t sizes[3] = { (size_t)n_groups * local_size, 1, 1 };
588 size_t local[3] = { local_size, 1, 1 };
589 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
590 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
591 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &luminance);
592 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &partial_sums);
593 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &n_pixels);
594 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), &epsilon);
595 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float) * 4 * local_size, NULL);
596 cl_err = dt_opencl_enqueue_kernel_2d_with_local(devid, kernel, sizes, local);
597 if(cl_err != CL_SUCCESS) goto out;
598
599 float partial_host[4 * 256];
600 cl_err = dt_opencl_read_buffer_from_device(devid, partial_host, partial_sums, 0, sizeof(float) * 4 * n_groups,
601 CL_TRUE);
602 if(cl_err != CL_SUCCESS) goto out;
603 double accum[4] = { 0.0, 0.0, 0.0, 0.0 };
604 for(int group = 0; group < n_groups; group++)
605 for(int k = 0; k < 4; k++) accum[k] += (double)partial_host[group * 4 + k];
606 if(accum[3] > 0.0)
607 for(int c = 0; c < 3; c++) chroma_mean[c] = (float)(accum[c] / accum[3]);
608 }
609
610 // ONE symbolic analysis + GPU numeric factorization for the three channels; when the core
611 // exceeds DT_HL_SPARSE_MAX (or the factorization fails) take the same road as the CPU:
612 // the matrix-free CG, here fully on the device
613 // assemble A = lambda_solid*I - Delta (order 1) over the all-clip hole; use_cg when the core
614 // is too large for the direct factorization (mirrors the CPU _sp_pde_factor / CG choice)
615 int n_unknowns = 0;
616 int use_cg
617 = !_sp_pde_assemble(hole_mask, NULL, (react > 0.f) ? react : 0.f, 1, 1.f, region_w, region_h,
618 &matrix_col_ptr, &matrix_row_index, &matrix_values, &perm_grid, &n_unknowns, pipe);
619 if(!use_cg)
620 {
621 factor = _sp_chol_factor_cl(devid, _hl_sp_chol_kernels(gd_void), n_unknowns, matrix_col_ptr, matrix_row_index,
622 matrix_values);
623 perm_grid_dev = factor ? _sp_cl_upload(devid, perm_grid, sizeof(int) * n_unknowns) : NULL;
624 rhs_dev = factor ? dt_opencl_alloc_device_buffer(devid, sizeof(double) * n_unknowns) : NULL;
625 if(!factor)
626 use_cg = 1;
627 else if(!perm_grid_dev || !rhs_dev)
628 {
630 goto out;
631 }
632 }
633 const int max_iter = CLAMP(2 * extent, 200, 2000);
634
635 // per channel: build the chromaticity ratio plane, solve its diffusion system, store into ratios[c]
636 for(int c = 0; c < 3; c++)
637 {
638 // init: ratio plane on valid pixels, flat-colour seed on the hole (cg_field = solver unknown)
639 {
640 const int kernel = global_data->kernel_hl_pde_init;
641 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
642 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &luminance);
643 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &hole);
644 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &embedded);
645 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &ratios[c]);
646 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &cg_field);
647 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(int), &region_w);
648 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), &region_h);
649 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &c);
650 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(float), &chroma_mean[c]);
651 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(float), &epsilon);
652 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
653 if(cl_err != CL_SUCCESS) goto out;
654 }
655 // direct path: assemble this channel's right-hand side on the device...
656 if(!use_cg)
657 {
658 const int kernel = global_data->kernel_hl_pde_rhs;
659 size_t size_1d[3] = { ROUNDUP(n_unknowns, 64), 1, 1 };
660 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &embedded);
661 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &perm_grid_dev);
662 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &rhs_dev);
663 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &n_unknowns);
664 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), &region_w);
665 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &region_h);
666 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), &react);
667 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), &chroma_mean[c]);
668 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_1d);
669 if(cl_err != CL_SUCCESS) goto out;
670 }
671 // ...solve with the shared Cholesky factor...
672 if(!use_cg)
673 {
674 if(_sp_chol_solve_cl(factor, _hl_sp_chol_kernels(gd_void), rhs_dev))
675 {
677 goto out;
678 }
679 // validate before scattering: the device factor kernel takes sqrt() of the pivots
680 // without checking their sign, so a system whose replicate-clamped border rows are not
681 // positive definite yields quiet NaN -- the CPU factor REJECTS such systems and falls
682 // back to conjugate gradient, and the device path must degrade the same way instead of
683 // blending NaN into the output. n_unknowns <= 16384 doubles = at most 128 KB on the bus.
684 double *solution_check = (double *)dt_pixelpipe_cache_alloc_align(sizeof(double) * n_unknowns, pipe);
685 int finite = (solution_check != NULL);
686 if(solution_check)
687 {
688 finite = (dt_opencl_read_buffer_from_device(devid, solution_check, rhs_dev, 0, sizeof(double) * n_unknowns,
689 CL_TRUE)
690 == CL_SUCCESS);
691 for(int check_index = 0; finite && check_index < n_unknowns; check_index++)
692 if(!isfinite(solution_check[check_index])) finite = 0;
693 dt_pixelpipe_cache_free_align(solution_check);
694 }
695 if(!finite)
696 {
698 factor = NULL;
699 use_cg = 1; // this channel and the remaining ones take the iterative road
700 }
701 else
702 {
703 // ...and scatter the solution back into the ratio plane
704 const int kernel = global_data->kernel_hl_pde_scatter;
705 size_t size_1d[3] = { ROUNDUP(n_unknowns, 64), 1, 1 };
706 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &rhs_dev);
707 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &perm_grid_dev);
708 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &ratios[c]);
709 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &n_unknowns);
710 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size_1d);
711 if(cl_err != CL_SUCCESS) goto out;
712 continue;
713 }
714 }
715 // iterative road: on-device conjugate gradient on the seeded unknown, then clamp
716 // the ratios non-negative (also the recovery path when the direct solve was rejected)
717 {
718 cl_err = _region_pde_cg_cl(devid, gd_void, cg_field, hole, region_w, region_h, (react > 0.f) ? react : 0.f,
719 (react > 0.f) ? chroma_mean[c] : 0.f, max_iter);
720 if(cl_err != CL_SUCCESS) goto out;
721 const int kernel = global_data->kernel_hl_relu;
722 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &cg_field);
723 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &ratios[c]);
724 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
725 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
726 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
727 if(cl_err != CL_SUCCESS) goto out;
728 }
729 }
730
731 // feathered composite: blur the core mask and blend dome x ratios into estimate through it
732 // (no hard hand-off at the core rim)
733 mask_img = dt_opencl_alloc_device(devid, region_w, region_h, sizeof(float));
734 mask_blur = dt_opencl_alloc_device(devid, region_w, region_h, sizeof(float));
735 if(!mask_img || !mask_blur)
736 {
738 goto out;
739 }
740 {
741 const int kernel = global_data->kernel_hl_mask_to_img1;
742 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &hole);
743 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &mask_img);
744 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &region_w);
745 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &region_h);
746 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
747 if(cl_err != CL_SUCCESS) goto out;
748 }
749 cl_err = _region_blur1_cl(devid, mask_img, mask_blur, region_w, region_h,
750 fmaxf(4.f, CLAMP(reg_radius / 6.f, 8.f, 64.f) / 4.f));
751 if(cl_err != CL_SUCCESS) goto out;
752 {
753 const int kernel = global_data->kernel_hl_core_blend;
754 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &estimate);
755 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &valid);
756 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &hole);
757 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &dome_lum);
758 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &ratio0);
759 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &ratio1);
760 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &ratio2);
761 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), &mask_blur);
762 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(int), &region_w);
763 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(int), &region_h);
764 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(float), &epsilon);
765 cl_err = dt_opencl_enqueue_kernel_2d(devid, kernel, size);
766 }
767
768out:
777 dt_opencl_release_mem_object(partial_sums);
778 dt_opencl_release_mem_object(perm_grid_dev);
783 dt_pixelpipe_cache_free_align(matrix_col_ptr);
784 dt_pixelpipe_cache_free_align(matrix_row_index);
785 dt_pixelpipe_cache_free_align(matrix_values);
788 return cl_err;
789}
790
791#endif // HAVE_OPENCL && DT_HL_SPARSE_SOLVE
cl_int _cf_harmonic_fill_cl(const int devid, void *gd_void, cl_mem val, cl_mem hole, const int region_w, const int region_h, const int base_ds, const int mask_is_hole, cl_mem steer)
void _cf_harmonic_fill(float *const restrict val, const uint8_t *const restrict hole, const int region_w, const int region_h, const int base_ds, const float *const restrict steer, const dt_dev_pixelpipe_t *pipe)
const dt_colormatrix_t dt_aligned_pixel_t out
__DT_CLONE_TARGETS__ void _joint_core(_hl_region_ctx_t *const ctx)
Definition core.c:151
__DT_CLONE_TARGETS__ void _selfdome(_hl_region_ctx_t *const ctx)
Definition core.c:37
#define dt_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
__DT_CLONE_TARGETS__ void _biharmonic_dome(float *const restrict field, const uint8_t *const restrict hole, const int region_w, const int region_h, const int forced_downsample, const dt_dev_pixelpipe_t *pipe)
Definition dome.c:33
static float kernel(const float *x, const float *y)
static void _knee_blur(const float *const restrict in, float *const restrict out, const int width, const int height, const float sigma)
Definition knee.h:31
float *const restrict luminance
float *const restrict const size_t k
size_t size
Definition mipmap_cache.c:3
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2580
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2504
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
__DT_CLONE_TARGETS__ void _region_pde_solve(float *const restrict field, const uint8_t *const restrict hole, const float *const restrict diffusion, const float *const restrict target, const float *const restrict source, const int order, const float lambda, const int region_w, const int region_h, float *const restrict residual, float *const restrict search_dir, float *const restrict operator_dir, float *const restrict embedded, float *const restrict scratch, const int maxiter)
Definition pde.c:368
_sp_chol_t * _sp_pde_factor(const uint8_t *const restrict hole, const float *const restrict diffusion, const int order, const float lambda, const int region_w, const int region_h, int **perm_out, int *n_unknowns_out, const dt_dev_pixelpipe_t *pipe)
Definition pde.c:308
__DT_CLONE_TARGETS__ void _sp_pde_solve(const _sp_chol_t *const factor, const int *const restrict perm_grid, float *const restrict field, const uint8_t *const restrict hole, const float *const restrict diffusion, const float *const restrict target, const float *const restrict source, const int order, const float lambda, const int region_w, const int region_h, double *const restrict rhs, float *const restrict embedded, float *const restrict operator_out, float *const restrict scratch)
Definition pde.c:334
int _sp_pde_assemble(const uint8_t *const restrict hole, const float *const restrict diffusion, const float diffusion_const, const int order, const float lambda, const int region_w, const int region_h, int **matrix_col_ptr_out, int **matrix_row_index_out, double **matrix_values_out, int **perm_grid_out, int *n_unknowns_out, const dt_dev_pixelpipe_t *const pipe)
Definition pde.c:173
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_chol_free(_sp_chol_t *factor)
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_DOME_NMAX_SPARSE
#define HL_PFOR(...)
const _hl_region_t * region
const dt_dev_pixelpipe_t * pipe
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MAX(a, b)
Definition thinplate.c:29