Improve sheet packing in Dune 2000.

In a3d0a50f4d, SpriteCache is updated to sort sprites by height before adding them onto the sheet. This improves packing by reducing wasted space as the sprites are packed onto the sheet. D2kSpriteSequence does not fully benefit from this change, as it creates additional sprites afterwards in the ResolveSprites method. These are not sorted, so they often waste space due to height changes between adjacent sprites and cause an inefficient packing. Sorting them in place is insufficient, as each sequence performs the operation independently. So sets of sprites across different sequences end up with poor packing overall. We need all the sprites to be collected together and sorted in one place for best effect.

We restructure SpriteCache to allow a frame mutation function to be provided when reserving sprites. This removes the need for the ReserveFrames and ResolveFrames methods in SpriteCache. D2kSpriteSequence can use this new function to pass in the required modification, and no longer has to add frames to the sheet builder itself. Now the SpriteCache can apply the desired frame mutations, it can batch together these mutated frames with the other frames and sort them all as a single batch. With all frames sorted together the maximum benefit of this packing approach is realised.

This reduces the number of BGRA sheets required for the d2k mod from 3 to 2.
This commit is contained in:
RoosterDragon
2024-02-24 18:44:13 +00:00
committed by Gustas
parent dc0f26a1cd
commit 4fca85f63d
2 changed files with 34 additions and 74 deletions

View File

@@ -62,6 +62,13 @@ namespace OpenRA.Mods.Cnc.Graphics
var offset = LoadField(Offset, data, defaults);
var blendMode = LoadField(BlendMode, data, defaults);
Func<ISpriteFrame, ISpriteFrame> adjustFrame = null;
if (remapColor != default || convertShroudToFog)
adjustFrame = RemapFrame;
ISpriteFrame RemapFrame(ISpriteFrame f) =>
(f is R8Loader.RemappableFrame rf) ? rf.WithSequenceFlags(useShadow, convertShroudToFog, remapColor) : f;
var combineNode = data.NodeWithKeyOrDefault(Combine.Key);
if (combineNode != null)
{
@@ -75,11 +82,7 @@ namespace OpenRA.Mods.Cnc.Graphics
foreach (var f in ParseCombineFilenames(modData, tileset, subFrames, subData))
{
int token;
if (remapColor != default || convertShroudToFog)
token = cache.ReserveFrames(f.Filename, f.LoadFrames, f.Location);
else
token = cache.ReserveSprites(f.Filename, f.LoadFrames, f.Location);
var token = cache.ReserveSprites(f.Filename, f.LoadFrames, f.Location, adjustFrame);
spritesToLoad.Add(new SpriteReservation
{
@@ -98,11 +101,7 @@ namespace OpenRA.Mods.Cnc.Graphics
{
foreach (var f in ParseFilenames(modData, tileset, frames, data, defaults))
{
int token;
if (remapColor != default || convertShroudToFog)
token = cache.ReserveFrames(f.Filename, f.LoadFrames, f.Location);
else
token = cache.ReserveSprites(f.Filename, f.LoadFrames, f.Location);
var token = cache.ReserveSprites(f.Filename, f.LoadFrames, f.Location, adjustFrame);
spritesToLoad.Add(new SpriteReservation
{
@@ -129,20 +128,7 @@ namespace OpenRA.Mods.Cnc.Graphics
var allSprites = spritesToLoad.SelectMany(r =>
{
Sprite[] resolved;
if (remapColor != default || convertShroudToFog)
resolved = cache.ResolveFrames(r.Token)
.Select(f => (f is R8Loader.RemappableFrame rf) ? rf.WithSequenceFlags(useShadow, convertShroudToFog, remapColor) : f)
.Select(f =>
{
if (f == null)
return null;
return cache.SheetBuilders[SheetBuilder.FrameTypeToSheetType(f.Type)]
.Add(f.Data, f.Type, f.Size, 0, f.Offset);
}).ToArray();
else
resolved = cache.ResolveSprites(r.Token);
var resolved = cache.ResolveSprites(r.Token);
if (r.Frames != null)
resolved = r.Frames.Select(f => resolved[f]).ToArray();