Skip to content

Commit bea6598

Browse files
committed
use pinned buffers in Vp8LHistogram
1 parent 63829e8 commit bea6598

File tree

7 files changed

+77
-76
lines changed

7 files changed

+77
-76
lines changed

src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static Vp8LBackwardRefs GetBackwardReferences(
8585
}
8686

8787
// Keep the best backward references.
88-
using Vp8LHistogram histo = new(memoryAllocator, worst, cacheBitsTmp);
88+
using Vp8LHistogram histo = Vp8LHistogram.Create(memoryAllocator, worst, cacheBitsTmp);
8989
double bitCost = histo.EstimateBits(stats, bitsEntropy);
9090

9191
if (lz77TypeBest == 0 || bitCost < bitCostBest)
@@ -102,7 +102,7 @@ public static Vp8LBackwardRefs GetBackwardReferences(
102102
{
103103
Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox!;
104104
BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChainTmp, best, worst);
105-
using Vp8LHistogram histo = new(memoryAllocator, worst, cacheBits);
105+
using Vp8LHistogram histo = Vp8LHistogram.Create(memoryAllocator, worst, cacheBits);
106106
double bitCostTrace = histo.EstimateBits(stats, bitsEntropy);
107107
if (bitCostTrace < bitCostBest)
108108
{

src/ImageSharp/Formats/Webp/Lossless/CostModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public CostModel(MemoryAllocator memoryAllocator, int literalArraySize)
3737

3838
public void Build(int xSize, int cacheBits, Vp8LBackwardRefs backwardRefs)
3939
{
40-
using Vp8LHistogram histogram = new(this.memoryAllocator, cacheBits);
40+
using Vp8LHistogram histogram = Vp8LHistogram.Create(this.memoryAllocator, cacheBits);
4141

4242
// The following code is similar to HistogramCreate but converts the distance to plane code.
4343
for (int i = 0; i < backwardRefs.Refs.Count; i++)

src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ private void EncodeImage(int width, int height, bool useCache, CrunchConfig conf
589589
Vp8LBackwardRefs refsTmp = this.Refs[refsBest.Equals(this.Refs[0]) ? 1 : 0];
590590

591591
this.bitWriter.Reset(bwInit);
592-
using Vp8LHistogram tmpHisto = new(this.memoryAllocator, cacheBits);
592+
using Vp8LHistogram tmpHisto = Vp8LHistogram.Create(this.memoryAllocator, cacheBits);
593593
using Vp8LHistogramSet histogramImage = new(this.memoryAllocator, histogramImageXySize, cacheBits);
594594

595595
// Build histogram image and symbols from backward references.

src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,27 @@
1010

1111
namespace SixLabors.ImageSharp.Formats.Webp.Lossless;
1212

13-
internal sealed class Vp8LHistogram : IDisposable
13+
internal sealed unsafe class Vp8LHistogram : IDisposable
1414
{
1515
private const uint NonTrivialSym = 0xffffffff;
16-
private readonly IMemoryOwner<uint> buffer;
16+
private readonly IMemoryOwner<uint>? bufferOwner;
17+
private readonly Memory<uint> buffer;
18+
private readonly MemoryHandle bufferHandle;
19+
20+
private readonly uint* red;
21+
private readonly uint* blue;
22+
private readonly uint* alpha;
23+
private readonly uint* distance;
24+
private readonly uint* literal;
25+
private readonly uint* isUsed;
26+
1727
private const int RedSize = WebpConstants.NumLiteralCodes;
1828
private const int BlueSize = WebpConstants.NumLiteralCodes;
1929
private const int AlphaSize = WebpConstants.NumLiteralCodes;
2030
private const int DistanceSize = WebpConstants.NumDistanceCodes;
2131
public const int LiteralSize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (1 << WebpConstants.MaxColorCacheBits) + 1;
2232
private const int UsedSize = 5; // 5 for literal, red, blue, alpha, distance
2333
public const int BufferSize = RedSize + BlueSize + AlphaSize + DistanceSize + LiteralSize + UsedSize;
24-
private readonly bool isSetMember;
25-
26-
/// <summary>
27-
/// Initializes a new instance of the <see cref="Vp8LHistogram"/> class.
28-
/// </summary>
29-
/// <param name="memoryAllocator">The memory allocator.</param>
30-
/// <param name="refs">The backward references to initialize the histogram with.</param>
31-
/// <param name="paletteCodeBits">The palette code bits.</param>
32-
public Vp8LHistogram(MemoryAllocator memoryAllocator, Vp8LBackwardRefs refs, int paletteCodeBits)
33-
: this(memoryAllocator, paletteCodeBits) => this.StoreRefs(refs);
34-
35-
/// <summary>
36-
/// Initializes a new instance of the <see cref="Vp8LHistogram"/> class.
37-
/// </summary>
38-
/// <param name="memoryAllocator">The memory allocator.</param>
39-
/// <param name="paletteCodeBits">The palette code bits.</param>
40-
public Vp8LHistogram(MemoryAllocator memoryAllocator, int paletteCodeBits)
41-
{
42-
this.buffer = memoryAllocator.Allocate<uint>(BufferSize, AllocationOptions.Clean);
43-
this.PaletteCodeBits = paletteCodeBits;
44-
}
4534

4635
/// <summary>
4736
/// Initializes a new instance of the <see cref="Vp8LHistogram"/> class.
@@ -52,7 +41,7 @@ public Vp8LHistogram(MemoryAllocator memoryAllocator, int paletteCodeBits)
5241
/// <param name="buffer">The backing buffer.</param>
5342
/// <param name="refs">The backward references to initialize the histogram with.</param>
5443
/// <param name="paletteCodeBits">The palette code bits.</param>
55-
public Vp8LHistogram(IMemoryOwner<uint> buffer, Vp8LBackwardRefs refs, int paletteCodeBits)
44+
public Vp8LHistogram(Memory<uint> buffer, Vp8LBackwardRefs refs, int paletteCodeBits)
5645
: this(buffer, paletteCodeBits) => this.StoreRefs(refs);
5746

5847
/// <summary>
@@ -63,11 +52,20 @@ public Vp8LHistogram(IMemoryOwner<uint> buffer, Vp8LBackwardRefs refs, int palet
6352
/// </remarks>
6453
/// <param name="buffer">The backing buffer.</param>
6554
/// <param name="paletteCodeBits">The palette code bits.</param>
66-
public Vp8LHistogram(IMemoryOwner<uint> buffer, int paletteCodeBits)
55+
/// <param name="bufferOwner">Optional buffer owner to dispose.</param>
56+
public Vp8LHistogram(Memory<uint> buffer, int paletteCodeBits, IMemoryOwner<uint>? bufferOwner = null)
6757
{
58+
this.bufferOwner = bufferOwner;
6859
this.buffer = buffer;
60+
this.bufferHandle = this.buffer.Pin();
6961
this.PaletteCodeBits = paletteCodeBits;
70-
this.isSetMember = true;
62+
63+
this.red = (uint*)this.bufferHandle.Pointer;
64+
this.blue = this.red + RedSize;
65+
this.alpha = this.blue + BlueSize;
66+
this.distance = this.alpha + AlphaSize;
67+
this.literal = this.distance + DistanceSize;
68+
this.isUsed = this.literal + LiteralSize;
7169
}
7270

7371
/// <summary>
@@ -95,22 +93,43 @@ public Vp8LHistogram(IMemoryOwner<uint> buffer, int paletteCodeBits)
9593
/// </summary>
9694
public double BlueCost { get; set; }
9795

98-
public Span<uint> Red => this.buffer.GetSpan()[..RedSize];
96+
public Span<uint> Red => new(this.red, RedSize);
9997

100-
public Span<uint> Blue => this.buffer.GetSpan().Slice(RedSize, BlueSize);
98+
public Span<uint> Blue => new(this.blue, BlueSize);
10199

102-
public Span<uint> Alpha => this.buffer.GetSpan().Slice(RedSize + BlueSize, AlphaSize);
100+
public Span<uint> Alpha => new(this.alpha, AlphaSize);
103101

104-
public Span<uint> Distance => this.buffer.GetSpan().Slice(RedSize + BlueSize + AlphaSize, DistanceSize);
102+
public Span<uint> Distance => new(this.distance, DistanceSize);
105103

106-
public Span<uint> Literal => this.buffer.GetSpan().Slice(RedSize + BlueSize + AlphaSize + DistanceSize, LiteralSize);
104+
public Span<uint> Literal => new(this.literal, LiteralSize);
107105

108106
public uint TrivialSymbol { get; set; }
109107

110-
private Span<uint> IsUsedSpan => this.buffer.GetSpan().Slice(RedSize + BlueSize + AlphaSize + DistanceSize + LiteralSize, UsedSize);
108+
private Span<uint> IsUsedSpan => new(this.isUsed, UsedSize);
109+
110+
private Span<uint> TotalSpan => new(this.red, BufferSize);
111111

112112
public bool IsDisposed { get; set; }
113113

114+
/// <summary>
115+
/// Creates an <see cref="Vp8LHistogram"/> that is not a member of a <see cref="Vp8LHistogramSet"/>.
116+
/// </summary>
117+
public static Vp8LHistogram Create(MemoryAllocator memoryAllocator, int paletteCodeBits)
118+
{
119+
IMemoryOwner<uint> bufferOwner = memoryAllocator.Allocate<uint>(BufferSize, AllocationOptions.Clean);
120+
return new Vp8LHistogram(bufferOwner.Memory, paletteCodeBits, bufferOwner);
121+
}
122+
123+
/// <summary>
124+
/// Creates an <see cref="Vp8LHistogram"/> that is not a member of a <see cref="Vp8LHistogramSet"/>.
125+
/// </summary>
126+
public static Vp8LHistogram Create(MemoryAllocator memoryAllocator, Vp8LBackwardRefs refs, int paletteCodeBits)
127+
{
128+
Vp8LHistogram histogram = Create(memoryAllocator, paletteCodeBits);
129+
histogram.StoreRefs(refs);
130+
return histogram;
131+
}
132+
114133
[MethodImpl(MethodImplOptions.AggressiveInlining)]
115134
public bool IsUsed(int index) => this.IsUsedSpan[index] == 1u;
116135

@@ -140,7 +159,7 @@ public void CopyTo(Vp8LHistogram other)
140159

141160
public void Clear()
142161
{
143-
this.buffer.Clear();
162+
this.TotalSpan.Clear();
144163
this.PaletteCodeBits = 0;
145164
this.BitCost = 0;
146165
this.LiteralCost = 0;
@@ -607,11 +626,8 @@ public void Dispose()
607626
{
608627
if (!this.IsDisposed)
609628
{
610-
if (!this.isSetMember)
611-
{
612-
this.buffer.Dispose();
613-
}
614-
629+
this.bufferHandle.Dispose();
630+
this.bufferOwner?.Dispose();
615631
this.IsDisposed = true;
616632
}
617633
}

src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogramSet.cs

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public Vp8LHistogramSet(MemoryAllocator memoryAllocator, int capacity, int cache
2323
this.items = new List<Vp8LHistogram>(capacity);
2424
for (int i = 0; i < capacity; i++)
2525
{
26-
SetItemMemoryOwner owner = new(this.buffer.Memory.Slice(Vp8LHistogram.BufferSize * i, Vp8LHistogram.BufferSize));
27-
this.items.Add(new Vp8LHistogram(owner, cacheBits));
26+
Memory<uint> subBuffer = this.buffer.Memory.Slice(Vp8LHistogram.BufferSize * i, Vp8LHistogram.BufferSize);
27+
this.items.Add(new Vp8LHistogram(subBuffer, cacheBits));
2828
}
2929
}
3030

@@ -35,8 +35,8 @@ public Vp8LHistogramSet(MemoryAllocator memoryAllocator, Vp8LBackwardRefs refs,
3535
this.items = new List<Vp8LHistogram>(capacity);
3636
for (int i = 0; i < capacity; i++)
3737
{
38-
SetItemMemoryOwner owner = new(this.buffer.Memory.Slice(Vp8LHistogram.BufferSize * i, Vp8LHistogram.BufferSize));
39-
this.items.Add(new Vp8LHistogram(owner, refs, cacheBits));
38+
Memory<uint> subBuffer = this.buffer.Memory.Slice(Vp8LHistogram.BufferSize * i, Vp8LHistogram.BufferSize);
39+
this.items.Add(new Vp8LHistogram(subBuffer, refs, cacheBits));
4040
}
4141
}
4242

@@ -82,13 +82,13 @@ public void Dispose()
8282
return;
8383
}
8484

85-
this.buffer.Dispose();
86-
8785
foreach (Vp8LHistogram item in this.items)
8886
{
87+
// First, make sure to unpin individual sub buffers.
8988
item?.Dispose();
9089
}
9190

91+
this.buffer.Dispose();
9292
this.items.Clear();
9393
this.isDisposed = true;
9494
}
@@ -107,16 +107,4 @@ private void CheckDisposed()
107107
}
108108

109109
private static void ThrowDisposed() => throw new ObjectDisposedException(nameof(Vp8LHistogramSet));
110-
111-
private sealed class SetItemMemoryOwner : IMemoryOwner<uint>
112-
{
113-
public SetItemMemoryOwner(Memory<uint> memory) => this.Memory = memory;
114-
115-
public Memory<uint> Memory { get; }
116-
117-
public void Dispose()
118-
{
119-
// Do nothing, the underlying memory is owned by the parent set.
120-
}
121-
}
122110
}

tests/ImageSharp.Tests/Formats/WebP/DominantCostRangeTests.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,10 @@ public void UpdateDominantCostRange_Works()
2525
{
2626
// arrange
2727
DominantCostRange dominantCostRange = new();
28-
using Vp8LHistogram histogram = new(Configuration.Default.MemoryAllocator, 10)
29-
{
30-
LiteralCost = 1.0d,
31-
RedCost = 2.0d,
32-
BlueCost = 3.0d
33-
};
28+
using Vp8LHistogram histogram = Vp8LHistogram.Create(Configuration.Default.MemoryAllocator, 10);
29+
histogram.LiteralCost = 1.0d;
30+
histogram.RedCost = 2.0d;
31+
histogram.BlueCost = 3.0d;
3432

3533
// act
3634
dominantCostRange.UpdateDominantCostRange(histogram);
@@ -59,13 +57,12 @@ public void GetHistoBinIndex_Works(int partitions, int expectedIndex)
5957
RedMax = 191.0,
6058
RedMin = 109.0
6159
};
62-
using Vp8LHistogram histogram = new(Configuration.Default.MemoryAllocator, 6)
63-
{
64-
LiteralCost = 247.0d,
65-
RedCost = 112.0d,
66-
BlueCost = 202.0d,
67-
BitCost = 733.0d
68-
};
60+
using Vp8LHistogram histogram = Vp8LHistogram.Create(Configuration.Default.MemoryAllocator, 6);
61+
histogram.LiteralCost = 247.0d;
62+
histogram.RedCost = 112.0d;
63+
histogram.BlueCost = 202.0d;
64+
histogram.BitCost = 733.0d;
65+
6966
dominantCostRange.UpdateDominantCostRange(histogram);
7067

7168
// act

tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,15 @@ private static void RunAddVectorTest()
7878
}
7979

8080
MemoryAllocator memoryAllocator = Configuration.Default.MemoryAllocator;
81-
using Vp8LHistogram histogram0 = new(memoryAllocator, backwardRefs, 3);
82-
using Vp8LHistogram histogram1 = new(memoryAllocator, backwardRefs, 3);
81+
using Vp8LHistogram histogram0 = Vp8LHistogram.Create(memoryAllocator, backwardRefs, 3);
82+
using Vp8LHistogram histogram1 = Vp8LHistogram.Create(memoryAllocator, backwardRefs, 3);
8383
for (int i = 0; i < 5; i++)
8484
{
8585
histogram0.IsUsed(i, true);
8686
histogram1.IsUsed(i, true);
8787
}
8888

89-
using Vp8LHistogram output = new(memoryAllocator, 3);
89+
using Vp8LHistogram output = Vp8LHistogram.Create(memoryAllocator, 3);
9090

9191
// act
9292
histogram0.Add(histogram1, output);

0 commit comments

Comments
 (0)