Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
simd.h
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 Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18#ifndef DT_SYSTEM_SIMD_H
19#define DT_SYSTEM_SIMD_H
20
21/* SIMD pixel primitives: the 4-float aligned pixel type, its vector helpers and the
22 * per-channel loop macros. Self-contained on purpose: low-level compute units include
23 * this instead of darktable.h. */
24
25#include "system/mem_alloc.h"
26#include "system/openmp.h"
27
28#include <glib.h>
29#include <math.h>
30
31#if defined(__aarch64__)
32#include <arm_neon.h>
33#endif
34
35#if defined(__x86_64__) || defined(__i386__)
36#include <xmmintrin.h> // needed for _mm_stream_ps
37#endif
38
39#ifdef __cplusplus
40extern "C" {
41#endif
42
43// Most code in dt assumes that the compiler is capable of auto-vectorization. In some cases, this will yield
44// suboptimal code if the compiler in fact does NOT auto-vectorize. Uncomment the following line for such a
45// compiler.
46//#define DT_NO_VECTORIZATION
47
48// For some combinations of compiler and architecture, the compiler may actually emit inferior code if given
49// a hint to vectorize a loop. Uncomment the following line if such a combination is the compilation target.
50//#define DT_NO_SIMD_HINTS
51
52// utility type to ease declaration of aligned small arrays to hold a pixel (and document their purpose)
54// SIMD view matching dt_aligned_pixel_t layout, for explicit 4-float vector math.
55typedef float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)));
56
57// To be able to vectorize per-pixel loops, we need to operate on all four channels, but if the compiler does
58// not auto-vectorize, doing so increases computation by 1/3 for a channel which typically is ignored anyway.
59// Select the appropriate number of channels over which to loop to produce the fastest code.
60#ifdef DT_NO_VECTORIZATION
61#define DT_PIXEL_SIMD_CHANNELS 3
62#else
63#define DT_PIXEL_SIMD_CHANNELS 4
64#endif
65
66// A macro which gives us a configurable shorthand to produce the optimal performance when processing all of the
67// channels in a pixel. Its first argument is the name of the variable to be used inside the 'for' loop it creates,
68// while the optional second argument is a set of OpenMP directives, typically specifying variable alignment.
69// If indexing off of the begining of any buffer allocated with dt's image or aligned allocation functions, the
70// alignment to specify is 64; otherwise, use 16, as there may have been an odd number of pixels from the start.
71// Sample usage:
72// for_each_channel(k,aligned(src,dest:16))
73// {
74// src[k] = dest[k] / 3.0f;
75// }
76#if defined(_OPENMP) && defined(OPENMP_SIMD_) && !defined(DT_NO_SIMD_HINTS)
77//https://stackoverflow.com/questions/45762357/how-to-concatenate-strings-in-the-arguments-of-pragma
78#define _DT_Pragma_(x) _Pragma(#x)
79#define _DT_Pragma(x) _DT_Pragma_(x)
80#define for_each_channel(_var, ...) \
81 _DT_Pragma(omp simd __VA_ARGS__) \
82 for (size_t _var = 0; _var < DT_PIXEL_SIMD_CHANNELS; _var++)
83#define for_four_channels(_var, ...) \
84 _DT_Pragma(omp simd __VA_ARGS__) \
85 for (size_t _var = 0; _var < 4; _var++)
86#else
87#define for_each_channel(_var, ...) \
88 for (size_t _var = 0; _var < DT_PIXEL_SIMD_CHANNELS; _var++)
89#define for_four_channels(_var, ...) \
90 for (size_t _var = 0; _var < 4; _var++)
91#endif
92
93static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t dt_simd_set1(const float value)
94{
95 return (dt_aligned_pixel_simd_t){ value, value, value, value };
96}
97
98static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
99dt_simd_abs(const dt_aligned_pixel_simd_t value)
100{
101 dt_aligned_pixel_simd_t out = value;
102 for(int c = 0; c < 4; c++)
103 out[c] = fabsf(value[c]);
104 return out;
105}
106
107static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
108dt_simd_max_zero(const dt_aligned_pixel_simd_t value)
109{
110 dt_aligned_pixel_simd_t out = value;
111 for(int c = 0; c < 4; c++)
112 out[c] = (isfinite(value[c])) ? MAX(value[c], 0.0f) : 0.f;
113 return out;
114}
115
116static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
117dt_simd_copysign(const dt_aligned_pixel_simd_t magnitude, const dt_aligned_pixel_simd_t sign)
118{
119 dt_aligned_pixel_simd_t out = magnitude;
120 for(int c = 0; c < 4; c++)
121 out[c] = copysignf(magnitude[c], sign[c]);
122 return out;
123}
124
125static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
126dt_simd_pow(const dt_aligned_pixel_simd_t base, const dt_aligned_pixel_simd_t exponent)
127{
128 dt_aligned_pixel_simd_t out = base;
129 for(int c = 0; c < 4; c++)
130 out[c] = powf(base[c], exponent[c]);
131 return out;
132}
133
134static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
135dt_load_simd(const float *const pixel)
136{
137 dt_aligned_pixel_simd_t out;
138 __builtin_memcpy(&out, pixel, sizeof(out));
139 return out;
140}
141
142static inline __attribute__((always_inline)) void
143dt_store_simd(float *const pixel, const dt_aligned_pixel_simd_t value)
144{
145 __builtin_memcpy(pixel, &value, sizeof(value));
146}
147
148static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
149dt_load_simd_aligned(const float *const pixel)
150{
151 const float *const in = (const float *const)__builtin_assume_aligned(pixel, 16);
152 return dt_load_simd(in);
153}
154
155static inline __attribute__((always_inline)) void
156dt_store_simd_aligned(float *const pixel, const dt_aligned_pixel_simd_t value)
157{
158 float *const out = (float *const)__builtin_assume_aligned(pixel, 16);
160}
161
162static inline __attribute__((always_inline)) void
163dt_store_simd_nontemporal(float *const pixel, const dt_aligned_pixel_simd_t value)
164{
165 float *const out = (float *const)__builtin_assume_aligned(pixel, 16);
166
167#if defined(__x86_64__) || defined(__i386__)
168 const union
169 {
170 dt_aligned_pixel_simd_t simd;
171 __m128 sse;
172 } cast = { .simd = value };
173 _mm_stream_ps(out, cast.sse);
174#elif defined(__aarch64__)
175 const union
176 {
177 dt_aligned_pixel_simd_t simd;
179 } cast = { .simd = value };
180 vst1q_f32(out, cast.neon);
181#elif (__clang__+0 > 7) && (__clang__+0 < 10)
183#else
185#endif
186}
187
188static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
189dt_mat3x4_mul_vec4(const dt_aligned_pixel_simd_t in, const dt_aligned_pixel_simd_t row0,
190 const dt_aligned_pixel_simd_t row1, const dt_aligned_pixel_simd_t row2)
191{
192 // Keep the multiply first in each accumulation step so GCC contracts this
193 // into chained FMA instructions in the multiversioned FMA clones too.
194 dt_aligned_pixel_simd_t out = row0 * in[0];
195 out = row1 * in[1] + out;
196 return row2 * in[2] + out;
197}
198
199// copy the RGB channels of a pixel using nontemporal stores if
200// possible; includes the 'alpha' channel as well if faster due to
201// vectorization, but subsequent code should ignore the value of the
202// alpha unless explicitly set afterwards (since it might not have
203// been copied). NOTE: nontemporal stores will actually be *slower*
204// if we immediately access the pixel again. This function should
205// only be used when processing an entire image before doing anything
206// else with the destination buffer.
207static inline void copy_pixel_nontemporal(
208 float *const __restrict__ out,
209 const float *const __restrict__ in)
210{
212}
213
214// copy the RGB channels of a pixel; includes the 'alpha' channel as well if faster due to vectorization, but
215// subsequent code should ignore the value of the alpha unless explicitly set afterwards (since it might not have
216// been copied)
217static inline void copy_pixel(float *const __restrict__ out, const float *const __restrict__ in)
218{
220}
221
222#ifdef __cplusplus
223}
224#endif
225
226#endif // DT_SYSTEM_SIMD_H
227
228// clang-format off
229// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
230// vim: shiftwidth=2 expandtab tabstop=2 cindent
231// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
232// clang-format on
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vin, dt_colormatrix_row_to_simd(matrix, 0), dt_colormatrix_row_to_simd(matrix, 1), dt_colormatrix_row_to_simd(matrix, 2)))
float *const restrict const size_t k
#define DT_ALIGNED_PIXEL
Definition mem_alloc.h:61
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row1
Definition simd.h:190
dt_store_simd(out, value)
static void copy_pixel(float *const __restrict__ out, const float *const __restrict__ in)
Definition simd.h:217
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
return out
Definition simd.h:122
#define for_each_channel(_var,...)
Definition simd.h:87
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
static void copy_pixel_nontemporal(float *const __restrict__ out, const float *const __restrict__ in)
Definition simd.h:207
static const dt_aligned_pixel_simd_t sign
Definition simd.h:118
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row2
Definition simd.h:191
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
static const dt_aligned_pixel_simd_t exponent
Definition simd.h:127
static const dt_aligned_pixel_simd_t row0
Definition simd.h:189
#define MAX(a, b)
Definition thinplate.c:29