Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
locallaplaciancl.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016-2017 johannes hanika.
4 Copyright (C) 2017 Ulrich Pegelow.
5 Copyright (C) 2019-2020 Aurélien PIERRE.
6 Copyright (C) 2020 Heiko Bauke.
7 Copyright (C) 2020 Pascal Obry.
8 Copyright (C) 2021 Chris Elston.
9 Copyright (C) 2022 Hanno Schwalm.
10 Copyright (C) 2022 Martin Bařinka.
11
12 darktable is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 darktable is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with darktable. If not, see <http://www.gnu.org/licenses/>.
24*/
25#include "system/macros.h"
26#include "system/mem_alloc.h"
27#ifdef HAVE_OPENCL
28#include "common/opencl.h"
30
31#define max_levels 30
32#define num_gamma 6
33
34// downsample width/height to given level
35static inline uint64_t dl(uint64_t size, const int level)
36{
37 for(int l=0;l<level;l++)
38 size = (size-1)/2+1;
39 return size;
40}
41
42/* The kernels this subsystem compiles, owned HERE. They used to be handed to
43 * common/opencl.c, parked on the application-wide dt_opencl_t, and read back from it --
44 * a round trip through a god-struct that added nothing but an ordering. opencl.c still
45 * calls init/free, because the kernels must be built after the devices exist, but the
46 * pointer never leaves this file. */
48
50{
52
53 const int program = 19; // locallaplacian.cl, from programs.conf
54 g->kernel_pad_input = dt_opencl_create_kernel(program, "pad_input");
55 g->kernel_gauss_expand = dt_opencl_create_kernel(program, "gauss_expand");
56 g->kernel_gauss_reduce = dt_opencl_create_kernel(program, "gauss_reduce");
57 g->kernel_laplacian_assemble = dt_opencl_create_kernel(program, "laplacian_assemble");
58 g->kernel_process_curve = dt_opencl_create_kernel(program, "process_curve");
59 g->kernel_write_back = dt_opencl_create_kernel(program, "write_back");
61}
62
64{
67 if(IS_NULL_PTR(g)) return;
68
69 dt_opencl_free_kernel(g->kernel_pad_input);
70 dt_opencl_free_kernel(g->kernel_gauss_expand);
71 dt_opencl_free_kernel(g->kernel_gauss_reduce);
72 dt_opencl_free_kernel(g->kernel_laplacian_assemble);
73 dt_opencl_free_kernel(g->kernel_process_curve);
74 dt_opencl_free_kernel(g->kernel_write_back);
75
76 dt_free(g);
77}
78
80{
81 if(IS_NULL_PTR(g)) return;
82
83 // free device mem
84 for(int l=0;l<max_levels;l++)
85 {
86 dt_opencl_release_mem_object(g->dev_padded[l]);
87 dt_opencl_release_mem_object(g->dev_output[l]);
88 for(int k=0;k<num_gamma;k++)
89 dt_opencl_release_mem_object(g->dev_processed[k][l]);
90 }
91 for(int k=0;k<num_gamma;k++) dt_free(g->dev_processed[k]);
92 dt_free(g->dev_padded);
93 dt_free(g->dev_output);
94 dt_free(g->dev_processed);
95 g->dev_padded = g->dev_output = 0;
96 g->dev_processed = 0;
97 dt_free(g);
98}
99
101 const int devid,
102 const int width, // width of input image
103 const int height, // height of input image
104 const float sigma, // user param: separate shadows/mid-tones/highlights
105 const float shadows, // user param: lift shadows
106 const float highlights, // user param: compress highlights
107 const float clarity) // user param: increase clarity/local contrast
108{
110 if(IS_NULL_PTR(g)) return NULL;
111
113 g->devid = devid;
114 g->width = width;
115 g->height = height;
116 g->sigma = sigma;
117 g->shadows = shadows;
118 g->highlights = highlights;
119 g->clarity = clarity;
120 g->dev_padded = calloc(max_levels, sizeof(cl_mem));
121 g->dev_output = calloc(max_levels, sizeof(cl_mem));
122 g->dev_processed = calloc(num_gamma, sizeof(cl_mem *));
123 for(int k=0;k<num_gamma;k++)
124 g->dev_processed[k] = calloc(max_levels, sizeof(cl_mem));
125
126 g->num_levels = MIN(max_levels, 31-__builtin_clz(MIN(width,height)));
127 g->max_supp = 1<<(g->num_levels-1);
128 g->bwidth = ROUNDUPDWD(width + 2*g->max_supp, devid);
129 g->bheight = ROUNDUPDHT(height + 2*g->max_supp, devid);
130
131 // get intermediate vector buffers with read-write access
132 for(int l=0;l<g->num_levels;l++)
133 {
134 g->dev_padded[l] = dt_opencl_alloc_device(devid, ROUNDUPDWD(dl(g->bwidth, l), devid), ROUNDUPDHT(dl(g->bheight, l), devid), sizeof(float));
135 if(!g->dev_padded[l]) goto error;
136 g->dev_output[l] = dt_opencl_alloc_device(devid, ROUNDUPDWD(dl(g->bwidth, l), devid), ROUNDUPDHT(dl(g->bheight, l), devid), sizeof(float));
137 if(!g->dev_output[l]) goto error;
138 for(int k=0;k<num_gamma;k++)
139 {
140 g->dev_processed[k][l] = dt_opencl_alloc_device(devid, ROUNDUPDWD(dl(g->bwidth, l), devid), ROUNDUPDHT(dl(g->bheight, l), devid), sizeof(float));
141 if(!g->dev_processed[k][l]) goto error;
142 }
143 }
144
145 return g;
146
147error:
148 fprintf(stderr, "[local laplacian cl] could not allocate temporary buffers\n");
150 return NULL;
151}
152
154 dt_local_laplacian_cl_t *b, // opencl context with temp buffers
155 cl_mem input, // input buffer in some Labx or yuvx format
156 cl_mem output) // output buffer with colour
157{
158 cl_int err = -666;
159
160 if(b->bwidth <= 1 || b->bheight <= 1) return err;
161
162 size_t sizes_pad[] = { ROUNDUPDWD(b->bwidth, b->devid), ROUNDUPDHT(b->bheight, b->devid), 1 };
163 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 0, sizeof(cl_mem), &input);
164 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 1, sizeof(cl_mem), &b->dev_padded[0]);
165 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 2, sizeof(int), &b->width);
166 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 3, sizeof(int), &b->height);
167 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 4, sizeof(int), &b->max_supp);
168 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 5, sizeof(int), &b->bwidth);
169 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 6, sizeof(int), &b->bheight);
170 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_pad_input, sizes_pad);
171 if(err != CL_SUCCESS) goto error;
172
173 // create gauss pyramid of padded input, write coarse directly to output
174 for(int l=1;l<b->num_levels;l++)
175 {
176 const int wd = dl(b->bwidth, l), ht = dl(b->bheight, l);
177 size_t sizes[] = { ROUNDUPDWD(wd, b->devid), ROUNDUPDHT(ht, b->devid), 1 };
178 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 0, sizeof(cl_mem), &b->dev_padded[l-1]);
179 if(l == b->num_levels-1)
180 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 1, sizeof(cl_mem), &b->dev_output[l]);
181 else
182 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 1, sizeof(cl_mem), &b->dev_padded[l]);
183 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 2, sizeof(int), &wd);
184 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 3, sizeof(int), &ht);
185 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_gauss_reduce, sizes);
186 if(err != CL_SUCCESS) goto error;
187 }
188
189 for(int k=0;k<num_gamma;k++)
190 { // process images
191 const float g = (k+.5f)/(float)num_gamma;
192 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 0, sizeof(cl_mem), &b->dev_padded[0]);
193 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 1, sizeof(cl_mem), &b->dev_processed[k][0]);
194 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 2, sizeof(float), &g);
195 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 3, sizeof(float), &b->sigma);
196 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 4, sizeof(float), &b->shadows);
197 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 5, sizeof(float), &b->highlights);
198 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 6, sizeof(float), &b->clarity);
199 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 7, sizeof(int), &b->bwidth);
200 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 8, sizeof(int), &b->bheight);
201 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_process_curve, sizes_pad);
202 if(err != CL_SUCCESS) goto error;
203
204 // create gaussian pyramids
205 for(int l=1;l<b->num_levels;l++)
206 {
207 const int wd = dl(b->bwidth, l), ht = dl(b->bheight, l);
208 size_t sizes[] = { ROUNDUPDWD(wd, b->devid), ROUNDUPDHT(ht, b->devid), 1 };
209 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 0, sizeof(cl_mem), &b->dev_processed[k][l-1]);
210 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 1, sizeof(cl_mem), &b->dev_processed[k][l]);
211 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 2, sizeof(int), &wd);
212 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 3, sizeof(int), &ht);
213 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_gauss_reduce, sizes);
214 if(err != CL_SUCCESS) goto error;
215 }
216 }
217
218 // assemble output pyramid coarse to fine
219 for(int l=b->num_levels-2;l >= 0; l--)
220 {
221 const int pw = dl(b->bwidth,l), ph = dl(b->bheight,l);
222 size_t sizes[] = { ROUNDUPDWD(pw, b->devid), ROUNDUPDHT(ph, b->devid), 1 };
223 // this is so dumb:
224 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 0, sizeof(cl_mem), &b->dev_padded[l]);
225 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 1, sizeof(cl_mem), &b->dev_output[l+1]);
226 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 2, sizeof(cl_mem), &b->dev_output[l]);
227 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 3, sizeof(cl_mem), &b->dev_processed[0][l]);
228 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 4, sizeof(cl_mem), &b->dev_processed[0][l+1]);
229 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 5, sizeof(cl_mem), &b->dev_processed[1][l]);
230 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 6, sizeof(cl_mem), &b->dev_processed[1][l+1]);
231 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 7, sizeof(cl_mem), &b->dev_processed[2][l]);
232 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 8, sizeof(cl_mem), &b->dev_processed[2][l+1]);
233 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 9, sizeof(cl_mem), &b->dev_processed[3][l]);
234 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 10, sizeof(cl_mem), &b->dev_processed[3][l+1]);
235 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 11, sizeof(cl_mem), &b->dev_processed[4][l]);
236 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 12, sizeof(cl_mem), &b->dev_processed[4][l+1]);
237 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 13, sizeof(cl_mem), &b->dev_processed[5][l]);
238 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 14, sizeof(cl_mem), &b->dev_processed[5][l+1]);
239 // dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 15, sizeof(cl_mem), &b->dev_processed[6][l]);
240 // dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 16, sizeof(cl_mem), &b->dev_processed[6][l+1]);
241 // dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 17, sizeof(cl_mem), &b->dev_processed[7][l]);
242 // dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 18, sizeof(cl_mem), &b->dev_processed[7][l+1]);
243 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 15, sizeof(int), &pw);
244 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 16, sizeof(int), &ph);
245 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_laplacian_assemble, sizes);
246 if(err != CL_SUCCESS) goto error;
247 }
248
249 // read back processed L channel and copy colours:
250 size_t sizes[] = { ROUNDUPDWD(b->width, b->devid), ROUNDUPDHT(b->height, b->devid), 1 };
251 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 0, sizeof(cl_mem), &input);
252 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 1, sizeof(cl_mem), &b->dev_output[0]);
253 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 2, sizeof(cl_mem), &output);
254 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 3, sizeof(int), &b->max_supp);
255 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 4, sizeof(int), &b->width);
256 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 5, sizeof(int), &b->height);
257 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_write_back, sizes);
258 if(err != CL_SUCCESS) goto error;
259
260 return CL_SUCCESS;
261
262error:
263 fprintf(stderr, "[local laplacian cl] failed: %d\n", err);
264 return err;
265}
266
267#undef max_levels
268#undef num_gamma
269#endif
270// clang-format off
271// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
272// vim: shiftwidth=2 expandtab tabstop=2 cindent
273// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
274// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
static dt_local_laplacian_cl_global_t * _local_laplacian_cl_global
cl_int dt_local_laplacian_cl(dt_local_laplacian_cl_t *b, cl_mem input, cl_mem output)
#define num_gamma
void dt_local_laplacian_init_cl_global(void)
#define max_levels
dt_local_laplacian_cl_t * dt_local_laplacian_init_cl(const int devid, const int width, const int height, const float sigma, const float shadows, const float highlights, const float clarity)
static uint64_t dl(uint64_t size, const int level)
void dt_local_laplacian_free_cl(dt_local_laplacian_cl_t *g)
void dt_local_laplacian_free_cl_global(void)
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
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(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2894
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
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
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
const float sigma
unsigned __int64 uint64_t
Definition strptime.c:75
#define MIN(a, b)
Definition thinplate.c:32