From 61770d59b1b9db6547ca6eb629fbee2bc71506fb Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Fri, 27 May 2022 11:41:21 +0200 Subject: [PATCH] further improve RuntimeTexturePacker performance by only checking against already packed textures --- MLEM.Data/RuntimeTexturePacker.cs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/MLEM.Data/RuntimeTexturePacker.cs b/MLEM.Data/RuntimeTexturePacker.cs index 923505f..e149c99 100644 --- a/MLEM.Data/RuntimeTexturePacker.cs +++ b/MLEM.Data/RuntimeTexturePacker.cs @@ -34,7 +34,8 @@ namespace MLEM.Data { /// public TimeSpan LastTotalTime => this.LastCalculationTime + this.LastPackTime; - private readonly List textures = new List(); + private readonly List texturesToPack = new List(); + private readonly List alreadyPackedTextures = new List(); private readonly Dictionary dataCache = new Dictionary(); private readonly bool autoIncreaseMaxWidth; private readonly bool forcePowerOfTwo; @@ -148,7 +149,7 @@ namespace MLEM.Data { throw new InvalidOperationException($"Cannot add texture with width {texture.Width} to a texture packer with max width {this.maxWidth}"); } } - this.textures.Add(new Request(texture, result, padding, padWithPixels)); + this.texturesToPack.Add(new Request(texture, result, padding, padWithPixels)); } /// @@ -162,19 +163,19 @@ namespace MLEM.Data { if (this.PackedTexture != null) throw new InvalidOperationException("Cannot pack a texture packer that is already packed"); - // we pack larger textures first, so that smaller textures can fit in the gaps that larger ones leave - this.textures.Sort((r1, r2) => (r2.Texture.Width * r2.Texture.Height).CompareTo(r1.Texture.Width * r1.Texture.Height)); - // set pack areas for each request + // we pack larger textures first, so that smaller textures can fit in the gaps that larger ones leave var stopwatch = Stopwatch.StartNew(); - foreach (var request in this.textures) + foreach (var request in this.texturesToPack.OrderByDescending(t => t.Texture.Width * t.Texture.Height)) { request.PackedArea = this.FindFreeArea(request); + this.alreadyPackedTextures.Add(request); + } stopwatch.Stop(); this.LastCalculationTime = stopwatch.Elapsed; // figure out texture size and generate texture - var width = this.textures.Max(t => t.PackedArea.Right); - var height = this.textures.Max(t => t.PackedArea.Bottom); + var width = this.alreadyPackedTextures.Max(t => t.PackedArea.Right); + var height = this.alreadyPackedTextures.Max(t => t.PackedArea.Bottom); if (this.forcePowerOfTwo) { width = ToPowerOfTwo(width); height = ToPowerOfTwo(height); @@ -186,21 +187,22 @@ namespace MLEM.Data { // copy texture data onto the packed texture stopwatch.Restart(); using (var data = this.PackedTexture.GetTextureData()) { - foreach (var request in this.textures) + foreach (var request in this.alreadyPackedTextures) this.CopyRegion(data, request); } stopwatch.Stop(); this.LastPackTime = stopwatch.Elapsed; // invoke callbacks - foreach (var request in this.textures) { + foreach (var request in this.alreadyPackedTextures) { var packedArea = request.PackedArea.Shrink(new Point(request.Padding)); request.Result.Invoke(new TextureRegion(this.PackedTexture, packedArea)); if (this.disposeTextures) request.Texture.Texture.Dispose(); } - this.textures.Clear(); + this.texturesToPack.Clear(); + this.alreadyPackedTextures.Clear(); this.dataCache.Clear(); } @@ -210,7 +212,8 @@ namespace MLEM.Data { public void Reset() { this.PackedTexture?.Dispose(); this.PackedTexture = null; - this.textures.Clear(); + this.texturesToPack.Clear(); + this.alreadyPackedTextures.Clear(); this.dataCache.Clear(); this.LastCalculationTime = TimeSpan.Zero; this.LastPackTime = TimeSpan.Zero; @@ -231,7 +234,7 @@ namespace MLEM.Data { while (true) { var intersected = false; var area = new Rectangle(pos, size); - foreach (var tex in this.textures) { + foreach (var tex in this.alreadyPackedTextures) { if (tex.PackedArea.Intersects(area)) { pos.X = tex.PackedArea.Right; // when we move down, we want to move down by the smallest intersecting texture's height