Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
pixelpipe_cpu.c
Go to the documentation of this file.
1/*
2 Private CPU pixelpipe backend.
3*/
4
5#include "system/macros.h"
7#include "system/openmp.h"
8#include "common/logging.h"
10#include "develop/iop_order.h"
11#include "develop/blend.h"
13
14#include <assert.h>
15#include <math.h>
16#include <stdio.h>
17
19 const dt_dev_pixelpipe_iop_t *previous_piece,
21 gboolean *const cache_output,
22 dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
23{
24 dt_iop_module_t *module = piece->module;
25 float *input = input_entry ? dt_pixel_cache_entry_get_data(input_entry) : NULL;
26 void *output = dt_pixel_cache_entry_get_data(output_entry);
27 float *process_input_temp = NULL;
28 float *blend_input_temp = NULL;
29 float *blend_output_temp = NULL;
30 const float *process_input = input;
31 const float *blend_input = input;
32 void *blend_output = output;
33 const dt_iop_buffer_dsc_t actual_input_dsc = previous_piece ? previous_piece->dsc_out : pipe->dev->image_storage.dsc;
34 dt_iop_buffer_dsc_t process_input_dsc = actual_input_dsc;
35 dt_iop_buffer_dsc_t blend_input_dsc = actual_input_dsc;
36 dt_iop_buffer_dsc_t blend_output_dsc = piece->dsc_out;
37 gboolean input_locked = FALSE;
38
39 if(IS_NULL_PTR(input) && !(module->flags() & IOP_FLAGS_TAKE_NO_INPUT))
40 {
41 fprintf(stdout, "[dev_pixelpipe] %s got a NULL input, report that to developers\n", module->name());
42 return 1;
43 }
44 if(IS_NULL_PTR(output))
45 output = dt_pixel_cache_alloc(output_entry);
46
47 // NULL only when the pixelpipe cache refused the buffer -- the pressure valve or the arena -- and
48 // the cache has already logged why: a lack of memory, not a bug.
49 if(IS_NULL_PTR(output))
50 {
51 fprintf(stdout, "[dev_pixelpipe] %s got no output buffer: the pixelpipe cache refused it\n", module->name());
52 return 1;
53 }
54
55 const dt_iop_order_iccprofile_info_t *const work_profile
56 = (process_input_dsc.cst != IOP_CS_RAW || piece->dsc_in.cst != IOP_CS_RAW)
58 : NULL;
59
60 const int cst_before = process_input_dsc.cst;
61 if(process_input_dsc.cst != piece->dsc_in.cst
62 && !(dt_iop_colorspace_is_rgb(process_input_dsc.cst) && dt_iop_colorspace_is_rgb(piece->dsc_in.cst)))
63 {
64 process_input_temp
65 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_in.width * piece->roi_in.height * 4, pipe);
66 if(IS_NULL_PTR(process_input_temp))
67 return 1;
68
70 input_locked = TRUE;
71 dt_colorspaces_apply_profile(module->op, module->multi_name, input, process_input_temp, piece->roi_in.width,
72 piece->roi_in.height, process_input_dsc.cst, piece->dsc_in.cst,
73 &process_input_dsc.cst, work_profile);
75 input_locked = FALSE;
76 process_input = process_input_temp;
77 }
78 else if(process_input_dsc.cst != piece->dsc_in.cst)
79 {
80 process_input_dsc.cst = piece->dsc_in.cst;
81 if(input_entry)
82 {
84 input_locked = TRUE;
85 }
86 }
87 else
88 {
89 if(input_entry)
90 {
92 input_locked = TRUE;
93 }
94 }
95 const int cst_after = process_input_dsc.cst;
96
97 dt_dev_pixelpipe_debug_dump_module_io(pipe, module, "pre", FALSE, &piece->dsc_in, &piece->dsc_out,
98 &piece->roi_in, &piece->roi_out,
99 process_input_dsc.bpp, piece->dsc_out.bpp, cst_before, cst_after);
100
101 if((dt_get_debug_flags() & DT_DEBUG_NAN) && !IS_NULL_PTR(output) && piece->dsc_out.datatype == TYPE_FLOAT)
102 {
103 const size_t ch = piece->dsc_out.channels;
104 const size_t count = (size_t)piece->roi_out.width * (size_t)piece->roi_out.height * ch;
105 float *out = (float *)output;
107 for(size_t k = 0; k < count; k++)
108 out[k] = NAN;
109 }
110
111 /* Asked only when tiling could save memory. The answer chooses between process() and
112 * process_tiling(), and getting it EVICTS cache lines -- other pipes' included -- until the
113 * module's whole working set fits one contiguous arena run. For a module that cannot be tiled,
114 * or whose tiling would fall back to process(), it chooses nothing, process() runs either way,
115 * and the eviction only throws work away. The module's own allocations still evict what each
116 * of them needs, when it is made. */
117 const gboolean fitting
118 = !piece->process_tiling_ready
119 || !dt_tiling_piece_can_save_memory(module, pipe, piece, process_input_dsc.bpp)
120 || dt_tiling_piece_fits_host_memory(MAX(piece->roi_in.width, piece->roi_out.width),
121 MAX(piece->roi_in.height, piece->roi_out.height),
122 MAX(process_input_dsc.bpp, piece->dsc_out.bpp),
123 tiling->factor, tiling->overhead);
124
125 int err = 0;
126 if(!fitting && piece->process_tiling_ready)
127 {
128 err = module->process_tiling(module, pipe, piece, process_input, output, process_input_dsc.bpp);
130 *pixelpipe_flow &= ~(PIXELPIPE_FLOW_PROCESSED_ON_GPU);
131 }
132 else
133 {
134 err = module->process(module, pipe, piece, process_input, output);
135 *pixelpipe_flow |= PIXELPIPE_FLOW_PROCESSED_ON_CPU;
137 }
138
139 if(err)
140 {
141 // a module that reads the killswitch mid-run fails for it (doc/module-abandon.md): that run was
142 // abandoned, not broken, and stays as quiet as a stop between two modules
144 dt_print(DT_DEBUG_PIPE, "[pixelpipe] %s process on CPU stopped: the pipe abandoned its run\n",
145 module->name());
146 else
147 fprintf(stdout, "[pixelpipe] %s process on CPU returned with an error\n", module->name());
148 if(input_locked)
150 dt_pixelpipe_cache_free_align(process_input_temp);
151 return err;
152 }
153
154 if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)
155 {
156 blend_input = process_input;
157 blend_input_dsc = process_input_dsc;
158 blend_output = output;
159 blend_output_dsc = piece->dsc_out;
160
161 const dt_pixelpipe_blend_transform_t blend_transforms
163 if(blend_transforms != DT_DEV_PIXELPIPE_BLEND_TRANSFORM_NONE)
164 {
166 const int blend_in_before = blend_input_dsc.cst;
167 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_INPUT)
168 {
169 blend_input_temp
170 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_in.width * piece->roi_in.height * 4, pipe);
171 if(IS_NULL_PTR(blend_input_temp))
172 {
173 if(input_locked)
175 dt_pixelpipe_cache_free_align(process_input_temp);
176 return 1;
177 }
178
179 dt_colorspaces_apply_profile(module->op, module->multi_name, process_input, blend_input_temp, piece->roi_in.width,
180 piece->roi_in.height, blend_input_dsc.cst, blend_cst,
181 &blend_input_dsc.cst, work_profile);
182 blend_input = blend_input_temp;
183 if(input_locked)
184 {
186 input_locked = FALSE;
187 }
188 }
189 const int blend_in_after = blend_input_dsc.cst;
190
191 dt_dev_pixelpipe_debug_dump_module_io(pipe, module, "blend-in", FALSE, &process_input_dsc, &blend_input_dsc,
192 &piece->roi_in, &piece->roi_in,
193 process_input_dsc.bpp, blend_input_dsc.bpp,
194 blend_in_before, blend_in_after);
195
196 const int blend_out_before = blend_output_dsc.cst;
197 if(blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT)
198 {
199 blend_output_temp
200 = dt_pixelpipe_cache_alloc_align_float((size_t)piece->roi_out.width * piece->roi_out.height * 4, pipe);
201 if(IS_NULL_PTR(blend_output_temp))
202 {
203 if(input_locked)
205 dt_pixelpipe_cache_free_align(blend_input_temp);
206 dt_pixelpipe_cache_free_align(process_input_temp);
207 return 1;
208 }
209
210 dt_colorspaces_apply_profile(module->op, module->multi_name, output, blend_output_temp, piece->roi_out.width,
211 piece->roi_out.height, blend_output_dsc.cst, blend_cst,
212 &blend_output_dsc.cst, work_profile);
213 blend_output = blend_output_temp;
214 }
215 const int blend_out_after = blend_output_dsc.cst;
216
217 dt_dev_pixelpipe_debug_dump_module_io(pipe, module, "blend-out", FALSE, &piece->dsc_out, &blend_output_dsc,
218 &piece->roi_out, &piece->roi_out,
219 piece->dsc_out.bpp, blend_output_dsc.bpp,
220 blend_out_before, blend_out_after);
221 }
222
223 err = dt_develop_blend_process(module, pipe, piece, blend_input, blend_output);
224 *pixelpipe_flow |= PIXELPIPE_FLOW_BLENDED_ON_CPU;
225 *pixelpipe_flow &= ~(PIXELPIPE_FLOW_BLENDED_ON_GPU);
226
227 if(!err && (blend_transforms & DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT))
228 {
229 // A mask or channel preview is converted like any output: the preview is authored in the
230 // blending space precisely so this conversion lands it where the display expects it.
231 dt_colorspaces_apply_profile(module->op, module->multi_name, blend_output, output, piece->roi_out.width,
232 piece->roi_out.height, blend_output_dsc.cst, piece->dsc_out.cst,
233 &blend_output_dsc.cst, work_profile);
234 }
235 }
236
237 if(input_locked)
239 dt_pixelpipe_cache_free_align(blend_output_temp);
240 dt_pixelpipe_cache_free_align(blend_input_temp);
241 dt_pixelpipe_cache_free_align(process_input_temp);
242
243 return err;
244}
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
dt_iop_colorspace_type_t dt_develop_blend_colorspace(const dt_dev_pixelpipe_iop_t *const piece, dt_iop_colorspace_type_t cst)
Definition blend.c:234
int dt_develop_blend_process(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition blend.c:719
dt_iop_colorspace_type_t
@ IOP_CS_RAW
void dt_colorspaces_apply_profile(const char *const op_name, const char *const instance_name, const float *const image_in, float *const image_out, const int width, const int height, const int cst_from, const int cst_to, int *converted_cst, const dt_iop_order_iccprofile_info_t *const profile_info)
Convert a 4-channel float buffer between RGB and Lab through one profile.
const dt_colormatrix_t dt_aligned_pixel_t out
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition format.h:75
@ TYPE_FLOAT
Definition format.h:56
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_TAKE_NO_INPUT
Definition imageop.h:195
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
@ DT_DEBUG_PIPE
Definition logging.h:76
@ DT_DEBUG_NAN
Definition logging.h:61
int32_t dt_get_debug_flags(void)
Definition darktable.c:2094
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#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 __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
void * dt_pixel_cache_entry_get_data(dt_pixel_cache_entry_t *entry)
Peek the host pixels a cache entry holds for its current hash, without allocating.
void dt_dev_pixelpipe_cache_rdlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
void * dt_pixel_cache_alloc(dt_pixel_cache_entry_t *cache_entry)
Actually allocate the memory buffer attached to the cache entry once you create it with dt_dev_pixelp...
#define dt_pixelpipe_cache_free_align(mem)
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
int pixelpipe_process_on_CPU(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const dt_dev_pixelpipe_iop_t *previous_piece, dt_develop_tiling_t *tiling, dt_pixelpipe_flow_t *pixelpipe_flow, gboolean *const cache_output, dt_pixel_cache_entry_t *input_entry, dt_pixel_cache_entry_t *output_entry)
dt_pixelpipe_blend_transform_t dt_dev_pixelpipe_transform_for_blend(const dt_iop_module_t *const self, const dt_dev_pixelpipe_iop_t *const piece, const dt_iop_buffer_dsc_t *const output_dsc)
void dt_dev_pixelpipe_debug_dump_module_io(dt_dev_pixelpipe_t *pipe, dt_iop_module_t *module, const char *stage, const gboolean is_cl, const dt_iop_buffer_dsc_t *in_dsc, const dt_iop_buffer_dsc_t *out_dsc, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out, const size_t in_bpp, const size_t out_bpp, const int cst_before, const int cst_after)
static gboolean dt_dev_pixelpipe_has_shutdown(const dt_dev_pixelpipe_t *pipe)
dt_pixelpipe_flow_t
@ PIXELPIPE_FLOW_PROCESSED_ON_CPU
@ PIXELPIPE_FLOW_PROCESSED_WITH_TILING
@ PIXELPIPE_FLOW_PROCESSED_ON_GPU
@ PIXELPIPE_FLOW_BLENDED_ON_CPU
@ PIXELPIPE_FLOW_BLENDED_ON_GPU
dt_pixelpipe_blend_transform_t
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_INPUT
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_NONE
@ DT_DEV_PIXELPIPE_BLEND_TRANSFORM_OUTPUT
dt_iop_buffer_dsc_t dsc_out
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:225
dt_iop_buffer_dsc_t dsc
Definition image.h:428
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
#define MAX(a, b)
Definition thinplate.c:29
gboolean dt_tiling_piece_can_save_memory(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const int in_bpp)
Whether process_tiling() can run this piece in less memory than process() would.
Definition tiling.c:1456
int dt_tiling_piece_fits_host_memory(const size_t width, const size_t height, const unsigned bpp, const float factor, const size_t overhead)
Definition tiling.c:1469