Skip to content

Commit 3fabb76

Browse files
Pool Vp8LHistogram memory.
1 parent e905b0a commit 3fabb76

File tree

12 files changed

+539
-288
lines changed

12 files changed

+539
-288
lines changed

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

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ public static Vp8LBackwardRefs GetBackwardReferences(
5050
double bitCostBest = -1;
5151
int cacheBitsInitial = cacheBits;
5252
Vp8LHashChain? hashChainBox = null;
53-
var stats = new Vp8LStreaks();
54-
var bitsEntropy = new Vp8LBitEntropy();
53+
Vp8LStreaks stats = new();
54+
Vp8LBitEntropy bitsEntropy = new();
5555
for (int lz77Type = 1; lz77TypesToTry > 0; lz77TypesToTry &= ~lz77Type, lz77Type <<= 1)
5656
{
5757
int cacheBitsTmp = cacheBitsInitial;
@@ -76,21 +76,19 @@ public static Vp8LBackwardRefs GetBackwardReferences(
7676
}
7777

7878
// Next, try with a color cache and update the references.
79-
cacheBitsTmp = CalculateBestCacheSize(bgra, quality, worst, cacheBitsTmp);
79+
cacheBitsTmp = CalculateBestCacheSize(memoryAllocator, bgra, quality, worst, cacheBitsTmp);
8080
if (cacheBitsTmp > 0)
8181
{
8282
BackwardRefsWithLocalCache(bgra, cacheBitsTmp, worst);
8383
}
8484

8585
// Keep the best backward references.
86-
var histo = new Vp8LHistogram(worst, cacheBitsTmp);
86+
using Vp8LHistogram histo = new(memoryAllocator, worst, cacheBitsTmp);
8787
double bitCost = histo.EstimateBits(stats, bitsEntropy);
8888

8989
if (lz77TypeBest == 0 || bitCost < bitCostBest)
9090
{
91-
Vp8LBackwardRefs tmp = worst;
92-
worst = best;
93-
best = tmp;
91+
(best, worst) = (worst, best);
9492
bitCostBest = bitCost;
9593
cacheBits = cacheBitsTmp;
9694
lz77TypeBest = lz77Type;
@@ -102,7 +100,7 @@ public static Vp8LBackwardRefs GetBackwardReferences(
102100
{
103101
Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox!;
104102
BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChainTmp, best, worst);
105-
var histo = new Vp8LHistogram(worst, cacheBits);
103+
using Vp8LHistogram histo = new(memoryAllocator, worst, cacheBits);
106104
double bitCostTrace = histo.EstimateBits(stats, bitsEntropy);
107105
if (bitCostTrace < bitCostBest)
108106
{
@@ -123,7 +121,12 @@ public static Vp8LBackwardRefs GetBackwardReferences(
123121
/// The local color cache is also disabled for the lower (smaller then 25) quality.
124122
/// </summary>
125123
/// <returns>Best cache size.</returns>
126-
private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, uint quality, Vp8LBackwardRefs refs, int bestCacheBits)
124+
private static int CalculateBestCacheSize(
125+
MemoryAllocator memoryAllocator,
126+
ReadOnlySpan<uint> bgra,
127+
uint quality,
128+
Vp8LBackwardRefs refs,
129+
int bestCacheBits)
127130
{
128131
int cacheBitsMax = quality <= 25 ? 0 : bestCacheBits;
129132
if (cacheBitsMax == 0)
@@ -134,11 +137,15 @@ private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, uint quality,
134137

135138
double entropyMin = MaxEntropy;
136139
int pos = 0;
137-
var colorCache = new ColorCache[WebpConstants.MaxColorCacheBits + 1];
138-
var histos = new Vp8LHistogram[WebpConstants.MaxColorCacheBits + 1];
140+
141+
// TODO: Pass from outer loop and clear.
142+
ColorCache[] colorCache = new ColorCache[WebpConstants.MaxColorCacheBits + 1];
143+
144+
// TODO: Use fixed size.
145+
using Vp8LHistogramSet histos = new(memoryAllocator, WebpConstants.MaxColorCacheBits + 1, 0);
139146
for (int i = 0; i <= WebpConstants.MaxColorCacheBits; i++)
140147
{
141-
histos[i] = new Vp8LHistogram(paletteCodeBits: i);
148+
histos[i].PaletteCodeBits = i;
142149
colorCache[i] = new ColorCache(i);
143150
}
144151

@@ -149,10 +156,10 @@ private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, uint quality,
149156
if (v.IsLiteral())
150157
{
151158
uint pix = bgra[pos++];
152-
uint a = (pix >> 24) & 0xff;
153-
uint r = (pix >> 16) & 0xff;
154-
uint g = (pix >> 8) & 0xff;
155-
uint b = (pix >> 0) & 0xff;
159+
int a = (int)(pix >> 24) & 0xff;
160+
int r = (int)(pix >> 16) & 0xff;
161+
int g = (int)(pix >> 8) & 0xff;
162+
int b = (int)(pix >> 0) & 0xff;
156163

157164
// The keys of the caches can be derived from the longest one.
158165
int key = ColorCache.HashPix(pix, 32 - cacheBitsMax);
@@ -218,8 +225,8 @@ private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, uint quality,
218225
}
219226
}
220227

221-
var stats = new Vp8LStreaks();
222-
var bitsEntropy = new Vp8LBitEntropy();
228+
Vp8LStreaks stats = new();
229+
Vp8LBitEntropy bitsEntropy = new();
223230
for (int i = 0; i <= cacheBitsMax; i++)
224231
{
225232
double entropy = histos[i].EstimateBits(stats, bitsEntropy);
@@ -266,7 +273,7 @@ private static void BackwardReferencesHashChainDistanceOnly(
266273
int pixCount = xSize * ySize;
267274
bool useColorCache = cacheBits > 0;
268275
int literalArraySize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (cacheBits > 0 ? 1 << cacheBits : 0);
269-
var costModel = new CostModel(literalArraySize);
276+
CostModel costModel = new(memoryAllocator, literalArraySize);
270277
int offsetPrev = -1;
271278
int lenPrev = -1;
272279
double offsetCost = -1;
@@ -280,7 +287,7 @@ private static void BackwardReferencesHashChainDistanceOnly(
280287
}
281288

282289
costModel.Build(xSize, cacheBits, refs);
283-
using var costManager = new CostManager(memoryAllocator, distArrayBuffer, pixCount, costModel);
290+
using CostManager costManager = new(memoryAllocator, distArrayBuffer, pixCount, costModel);
284291
Span<float> costManagerCosts = costManager.Costs.GetSpan();
285292
Span<ushort> distArray = distArrayBuffer.GetSpan();
286293

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4+
using SixLabors.ImageSharp.Memory;
5+
46
namespace SixLabors.ImageSharp.Formats.Webp.Lossless;
57

68
internal class CostModel
79
{
10+
private readonly MemoryAllocator memoryAllocator;
811
private const int ValuesInBytes = 256;
912

1013
/// <summary>
1114
/// Initializes a new instance of the <see cref="CostModel"/> class.
1215
/// </summary>
16+
/// <param name="memoryAllocator">The memory allocator.</param>
1317
/// <param name="literalArraySize">The literal array size.</param>
14-
public CostModel(int literalArraySize)
18+
public CostModel(MemoryAllocator memoryAllocator, int literalArraySize)
1519
{
20+
this.memoryAllocator = memoryAllocator;
1621
this.Alpha = new double[ValuesInBytes];
1722
this.Red = new double[ValuesInBytes];
1823
this.Blue = new double[ValuesInBytes];
@@ -32,13 +37,12 @@ public CostModel(int literalArraySize)
3237

3338
public void Build(int xSize, int cacheBits, Vp8LBackwardRefs backwardRefs)
3439
{
35-
var histogram = new Vp8LHistogram(cacheBits);
36-
using System.Collections.Generic.List<PixOrCopy>.Enumerator refsEnumerator = backwardRefs.Refs.GetEnumerator();
40+
using Vp8LHistogram histogram = new(this.memoryAllocator, cacheBits);
3741

3842
// The following code is similar to HistogramCreate but converts the distance to plane code.
39-
while (refsEnumerator.MoveNext())
43+
for (int i = 0; i < backwardRefs.Refs.Count; i++)
4044
{
41-
histogram.AddSinglePixOrCopy(refsEnumerator.Current, true, xSize);
45+
histogram.AddSinglePixOrCopy(backwardRefs.Refs[i], true, xSize);
4246
}
4347

4448
ConvertPopulationCountTableToBitEstimates(histogram.NumCodes(), histogram.Literal, this.Literal);
@@ -70,7 +74,7 @@ public double GetCacheCost(uint idx)
7074

7175
public double GetLiteralCost(uint v) => this.Alpha[v >> 24] + this.Red[(v >> 16) & 0xff] + this.Literal[(v >> 8) & 0xff] + this.Blue[v & 0xff];
7276

73-
private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, uint[] populationCounts, double[] output)
77+
private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, Span<uint> populationCounts, double[] output)
7478
{
7579
uint sum = 0;
7680
int nonzeros = 0;

0 commit comments

Comments
 (0)