-
-
Notifications
You must be signed in to change notification settings - Fork 894
Expand file tree
/
Copy pathPixelConversion_Rgba32_To_Bgra32.cs
More file actions
381 lines (313 loc) · 12.4 KB
/
Copy pathPixelConversion_Rgba32_To_Bgra32.cs
File metadata and controls
381 lines (313 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion;
// [MonoJob]
// [RyuJitX64Job]
public class PixelConversion_Rgba32_To_Bgra32
{
private Rgba32[] source;
private Bgra32[] dest;
[StructLayout(LayoutKind.Sequential)]
private struct Tuple4OfUInt32
{
private uint v0;
private uint v1;
private uint v2;
private uint v3;
public void ConvertMe()
{
this.v0 = FromRgba32.ToBgra32(this.v0);
this.v1 = FromRgba32.ToBgra32(this.v1);
this.v2 = FromRgba32.ToBgra32(this.v2);
this.v3 = FromRgba32.ToBgra32(this.v3);
}
}
[Params(64)]
public int Count { get; set; }
[GlobalSetup]
public void Setup()
{
this.source = new Rgba32[this.Count];
this.dest = new Bgra32[this.Count];
}
[Benchmark(Baseline = true)]
public void Default()
{
ref Rgba32 sBase = ref this.source[0];
ref Bgra32 dBase = ref this.dest[0];
for (nuint i = 0; i < (uint)this.Count; i++)
{
ref Rgba32 s = ref Unsafe.Add(ref sBase, i);
Unsafe.Add(ref dBase, i) = Bgra32.FromRgba32(s);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Default_GenericImpl<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest)
where TPixel : unmanaged, IPixel<TPixel>
{
ref Rgba32 sBase = ref MemoryMarshal.GetReference(source);
ref TPixel dBase = ref MemoryMarshal.GetReference(dest);
for (nuint i = 0; i < (uint)source.Length; i++)
{
ref Rgba32 s = ref Unsafe.Add(ref sBase, i);
Unsafe.Add(ref dBase, i) = TPixel.FromRgba32(s);
}
}
[Benchmark]
public void Default_Generic() => Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan());
[Benchmark]
public void Default_Group2()
{
ref Rgba32 sBase = ref this.source[0];
ref Bgra32 dBase = ref this.dest[0];
for (nuint i = 0; i < (uint)this.Count; i += 2)
{
ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i);
Rgba32 s1 = Unsafe.Add(ref s0, 1);
ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i);
d0 = Bgra32.FromRgba32(s0);
Unsafe.Add(ref d0, 1) = Bgra32.FromRgba32(s1);
}
}
[Benchmark]
public void Default_Group4()
{
ref Rgba32 sBase = ref this.source[0];
ref Bgra32 dBase = ref this.dest[0];
for (nuint i = 0; i < (uint)this.Count; i += 4)
{
ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i);
ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1);
ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1);
Rgba32 s3 = Unsafe.Add(ref s2, 1);
ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i);
ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1);
ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1);
d0 = Bgra32.FromRgba32(s0);
d1 = Bgra32.FromRgba32(s1);
d2 = Bgra32.FromRgba32(s2);
Unsafe.Add(ref d2, 1) = Bgra32.FromRgba32(s3);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Group4GenericImpl<TPixel>(ReadOnlySpan<Rgba32> source, Span<TPixel> dest)
where TPixel : unmanaged, IPixel<TPixel>
{
ref Rgba32 sBase = ref MemoryMarshal.GetReference(source);
ref TPixel dBase = ref MemoryMarshal.GetReference(dest);
for (nuint i = 0; i < (uint)source.Length; i += 4)
{
ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i);
ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1);
ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1);
Rgba32 s3 = Unsafe.Add(ref s2, 1);
ref TPixel d0 = ref Unsafe.Add(ref dBase, i);
ref TPixel d1 = ref Unsafe.Add(ref d0, 1);
ref TPixel d2 = ref Unsafe.Add(ref d1, 1);
d0 = TPixel.FromRgba32(s0);
d1 = TPixel.FromRgba32(s1);
d2 = TPixel.FromRgba32(s2);
Unsafe.Add(ref d2, 1) = TPixel.FromRgba32(s3);
}
}
// [Benchmark]
public void Default_Group4_Generic() => Group4GenericImpl(this.source.AsSpan(), this.dest.AsSpan());
// [Benchmark]
public void Default_Group8()
{
ref Rgba32 sBase = ref this.source[0];
ref Bgra32 dBase = ref this.dest[0];
for (nuint i = 0; i < (uint)this.Count / 4; i += 4)
{
ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i);
ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1);
ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1);
ref Rgba32 s3 = ref Unsafe.Add(ref s1, 1);
ref Rgba32 s4 = ref Unsafe.Add(ref s3, 1);
ref Rgba32 s5 = ref Unsafe.Add(ref s4, 1);
ref Rgba32 s6 = ref Unsafe.Add(ref s5, 1);
Rgba32 s7 = Unsafe.Add(ref s6, 1);
ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i);
ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1);
ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1);
ref Bgra32 d3 = ref Unsafe.Add(ref d2, 1);
ref Bgra32 d4 = ref Unsafe.Add(ref d3, 1);
ref Bgra32 d5 = ref Unsafe.Add(ref d4, 1);
ref Bgra32 d6 = ref Unsafe.Add(ref d5, 1);
d0 = Bgra32.FromRgba32(s0);
d1 = Bgra32.FromRgba32(s1);
d2 = Bgra32.FromRgba32(s2);
d3 = Bgra32.FromRgba32(s3);
d4 = Bgra32.FromRgba32(s4);
d5 = Bgra32.FromRgba32(s5);
d6 = Bgra32.FromRgba32(s6);
Unsafe.Add(ref d6, 1) = Bgra32.FromRgba32(s7);
}
}
[Benchmark]
public void BitOps()
{
ref uint sBase = ref Unsafe.As<Rgba32, uint>(ref this.source[0]);
ref uint dBase = ref Unsafe.As<Bgra32, uint>(ref this.dest[0]);
for (nuint i = 0; i < (uint)this.Count; i++)
{
uint s = Unsafe.Add(ref sBase, i);
Unsafe.Add(ref dBase, i) = FromRgba32.ToBgra32(s);
}
}
[Benchmark]
public void Bitops_Tuple()
{
ref Tuple4OfUInt32 sBase = ref Unsafe.As<Rgba32, Tuple4OfUInt32>(ref this.source[0]);
ref Tuple4OfUInt32 dBase = ref Unsafe.As<Bgra32, Tuple4OfUInt32>(ref this.dest[0]);
for (nuint i = 0; i < (uint)this.Count / 4; i++)
{
ref Tuple4OfUInt32 d = ref Unsafe.Add(ref dBase, i);
d = Unsafe.Add(ref sBase, i);
d.ConvertMe();
}
}
// [Benchmark]
public void Bitops_SingleTuple()
{
ref Tuple4OfUInt32 sBase = ref Unsafe.As<Rgba32, Tuple4OfUInt32>(ref this.source[0]);
for (nuint i = 0; i < (uint)this.Count / 4; i++)
{
Unsafe.Add(ref sBase, i).ConvertMe();
}
}
// [Benchmark]
public void Bitops_Simd()
{
ref InlineArray8<uint> sBase = ref Unsafe.As<Rgba32, InlineArray8<uint>>(ref this.source[0]);
ref InlineArray8<uint> dBase = ref Unsafe.As<Bgra32, InlineArray8<uint>>(ref this.dest[0]);
for (nuint i = 0; i < (uint)this.Count / 8; i++)
{
BitopsSimdImpl(ref Unsafe.Add(ref sBase, i), ref Unsafe.Add(ref dBase, i));
}
}
#pragma warning disable SA1132 // Do not combine fields
[StructLayout(LayoutKind.Sequential)]
private struct B
{
public uint Tmp2, Tmp5, Tmp8, Tmp11, Tmp14, Tmp17, Tmp20, Tmp23;
}
[StructLayout(LayoutKind.Sequential)]
private struct C
{
public uint Tmp3, Tmp6, Tmp9, Tmp12, Tmp15, Tmp18, Tmp21, Tmp24;
}
#pragma warning restore SA1132 // Do not combine fields
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void BitopsSimdImpl(ref InlineArray8<uint> s, ref InlineArray8<uint> d)
{
Vector<uint> sVec = Unsafe.As<InlineArray8<uint>, Vector<uint>>(ref s);
Vector<uint> aMask = new(0xFF00FF00);
Vector<uint> bMask = new(0x00FF00FF);
Vector<uint> aa = sVec & aMask;
Vector<uint> bb = sVec & bMask;
B b = Unsafe.As<Vector<uint>, B>(ref bb);
C c = default;
c.Tmp3 = (b.Tmp2 << 16) | (b.Tmp2 >> 16);
c.Tmp6 = (b.Tmp5 << 16) | (b.Tmp5 >> 16);
c.Tmp9 = (b.Tmp8 << 16) | (b.Tmp8 >> 16);
c.Tmp12 = (b.Tmp11 << 16) | (b.Tmp11 >> 16);
c.Tmp15 = (b.Tmp14 << 16) | (b.Tmp14 >> 16);
c.Tmp18 = (b.Tmp17 << 16) | (b.Tmp17 >> 16);
c.Tmp21 = (b.Tmp20 << 16) | (b.Tmp20 >> 16);
c.Tmp24 = (b.Tmp23 << 16) | (b.Tmp23 >> 16);
Vector<uint> cc = Unsafe.As<C, Vector<uint>>(ref c);
Vector<uint> dd = aa + cc;
d = Unsafe.As<Vector<uint>, InlineArray8<uint>>(ref dd);
}
// [Benchmark]
public void BitOps_Group2()
{
ref uint sBase = ref Unsafe.As<Rgba32, uint>(ref this.source[0]);
ref uint dBase = ref Unsafe.As<Bgra32, uint>(ref this.dest[0]);
for (nuint i = 0; i < (uint)this.Count; i++)
{
ref uint s0 = ref Unsafe.Add(ref sBase, i);
uint s1 = Unsafe.Add(ref s0, 1);
ref uint d0 = ref Unsafe.Add(ref dBase, i);
d0 = FromRgba32.ToBgra32(s0);
Unsafe.Add(ref d0, 1) = FromRgba32.ToBgra32(s1);
}
}
[Benchmark]
public void BitOps_GroupAsULong()
{
ref ulong sBase = ref Unsafe.As<Rgba32, ulong>(ref this.source[0]);
ref ulong dBase = ref Unsafe.As<Bgra32, ulong>(ref this.dest[0]);
for (nuint i = 0; i < (uint)this.Count / 2; i++)
{
ulong s = Unsafe.Add(ref sBase, i);
uint lo = (uint)s;
uint hi = (uint)(s >> 32);
lo = FromRgba32.ToBgra32(lo);
hi = FromRgba32.ToBgra32(hi);
s = (ulong)(hi << 32) | lo;
Unsafe.Add(ref dBase, i) = s;
}
}
// [Benchmark]
public void BitOps_GroupAsULong_V2()
{
ref ulong sBase = ref Unsafe.As<Rgba32, ulong>(ref this.source[0]);
ref ulong dBase = ref Unsafe.As<Bgra32, ulong>(ref this.dest[0]);
for (nuint i = 0; i < (uint)this.Count / 2; i++)
{
ulong s = Unsafe.Add(ref sBase, i);
uint lo = (uint)s;
uint hi = (uint)(s >> 32);
uint tmp1 = lo & 0xFF00FF00;
uint tmp4 = hi & 0xFF00FF00;
uint tmp2 = lo & 0x00FF00FF;
uint tmp5 = hi & 0x00FF00FF;
uint tmp3 = (tmp2 << 16) | (tmp2 >> 16);
uint tmp6 = (tmp5 << 16) | (tmp5 >> 16);
lo = tmp1 + tmp3;
hi = tmp4 + tmp6;
s = (ulong)(hi << 32) | lo;
Unsafe.Add(ref dBase, i) = s;
}
}
public static class FromRgba32
{
[MethodImpl(InliningOptions.ShortMethod)]
public static uint ToArgb32(uint packedRgba)
{
// packedRgba = [aa bb gg rr]
// ROL(8, packedRgba) = [bb gg rr aa]
return (packedRgba << 8) | (packedRgba >> 24);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static uint ToBgra32(uint packedRgba)
{
// packedRgba = [aa bb gg rr]
// tmp1 = [aa 00 gg 00]
// tmp2 = [00 bb 00 rr]
// tmp3=ROL(16, tmp2) = [00 rr 00 bb]
// tmp1 + tmp3 = [aa rr gg bb]
uint tmp1 = packedRgba & 0xFF00FF00;
uint tmp2 = packedRgba & 0x00FF00FF;
uint tmp3 = (tmp2 << 16) | (tmp2 >> 16);
return tmp1 + tmp3;
}
}
// RESULTS:
// Method | Count | Mean | Error | StdDev | Scaled | ScaledSD |
// -------------------- |------ |---------:|----------:|----------:|-------:|---------:|
// Default | 64 | 82.67 ns | 0.6737 ns | 0.5625 ns | 1.00 | 0.00 |
// Default_Generic | 64 | 88.73 ns | 1.7959 ns | 1.7638 ns | 1.07 | 0.02 |
// Default_Group2 | 64 | 91.03 ns | 1.5237 ns | 1.3508 ns | 1.10 | 0.02 |
// Default_Group4 | 64 | 86.62 ns | 1.5737 ns | 1.4720 ns | 1.05 | 0.02 |
// BitOps | 64 | 57.45 ns | 0.6067 ns | 0.5066 ns | 0.69 | 0.01 |
// Bitops_Tuple | 64 | 75.47 ns | 1.1824 ns | 1.1060 ns | 0.91 | 0.01 |
// BitOps_GroupAsULong | 64 | 65.42 ns | 0.7157 ns | 0.6695 ns | 0.79 | 0.01 |
}