Avoid a globally shared SheetBuilder in SpriteFont.
A globally shared sheet builder leaks memory and resources between mod switches. Instead, we create and inject the sheet builder during mod startup to ensure we still share the builder across all fonts, but can reclaim it when the mod is unloaded.
This commit is contained in:
@@ -37,6 +37,8 @@ namespace OpenRA.Graphics
|
||||
readonly Queue<IVertexBuffer<Vertex>> tempBuffers = new Queue<IVertexBuffer<Vertex>>();
|
||||
readonly Stack<Rectangle> scissorState = new Stack<Rectangle>();
|
||||
|
||||
SheetBuilder fontSheetBuilder;
|
||||
|
||||
Size? lastResolution;
|
||||
int2? lastScroll;
|
||||
float? lastZoom;
|
||||
@@ -94,8 +96,13 @@ namespace OpenRA.Graphics
|
||||
public void InitializeFonts(Manifest m)
|
||||
{
|
||||
using (new Support.PerfTimer("SpriteFonts"))
|
||||
{
|
||||
if (fontSheetBuilder != null)
|
||||
fontSheetBuilder.Dispose();
|
||||
fontSheetBuilder = new SheetBuilder(SheetType.BGRA);
|
||||
Fonts = m.Fonts.ToDictionary(x => x.Key,
|
||||
x => new SpriteFont(Platform.ResolvePath(x.Value.First), x.Value.Second)).AsReadOnly();
|
||||
x => new SpriteFont(Platform.ResolvePath(x.Value.First), x.Value.Second, fontSheetBuilder)).AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginFrame(int2 scroll, float zoom)
|
||||
@@ -246,6 +253,8 @@ namespace OpenRA.Graphics
|
||||
foreach (var buffer in tempBuffers)
|
||||
buffer.Dispose();
|
||||
tempBuffers.Clear();
|
||||
if (fontSheetBuilder != null)
|
||||
fontSheetBuilder.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
public sealed class SheetBuilder : IDisposable
|
||||
{
|
||||
public readonly SheetType Type;
|
||||
readonly List<Sheet> sheets = new List<Sheet>();
|
||||
readonly SheetType type;
|
||||
readonly Func<Sheet> allocateSheet;
|
||||
|
||||
Sheet current;
|
||||
@@ -54,7 +54,7 @@ namespace OpenRA.Graphics
|
||||
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
|
||||
{
|
||||
channel = TextureChannel.Red;
|
||||
type = t;
|
||||
Type = t;
|
||||
current = allocateSheet();
|
||||
sheets.Add(current);
|
||||
this.allocateSheet = allocateSheet;
|
||||
@@ -93,7 +93,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
TextureChannel? NextChannel(TextureChannel t)
|
||||
{
|
||||
var nextChannel = (int)t + (int)type;
|
||||
var nextChannel = (int)t + (int)Type;
|
||||
if (nextChannel > (int)TextureChannel.Alpha)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -19,26 +19,27 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public class SpriteFont
|
||||
{
|
||||
static Library library = new Library();
|
||||
static SheetBuilder builder;
|
||||
static readonly Library Library = new Library();
|
||||
|
||||
readonly int size;
|
||||
|
||||
readonly SheetBuilder builder;
|
||||
readonly Func<string, float> lineWidth;
|
||||
readonly Face face;
|
||||
readonly Cache<Pair<char, Color>, GlyphInfo> glyphs;
|
||||
|
||||
public SpriteFont(string name, int size)
|
||||
public SpriteFont(string name, int size, SheetBuilder builder)
|
||||
{
|
||||
this.size = size;
|
||||
if (builder.Type != SheetType.BGRA)
|
||||
throw new ArgumentException("The sheet builder must create BGRA sheets.", "builder");
|
||||
|
||||
face = new Face(library, name);
|
||||
this.size = size;
|
||||
this.builder = builder;
|
||||
|
||||
face = new Face(Library, name);
|
||||
face.SetPixelSizes((uint)size, (uint)size);
|
||||
|
||||
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);
|
||||
|
||||
// setup a SheetBuilder for our private use
|
||||
// TODO: SheetBuilder state is leaked between mod switches
|
||||
if (builder == null)
|
||||
builder = new SheetBuilder(SheetType.BGRA);
|
||||
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
|
||||
lineWidth = line => line.Sum(characterWidth);
|
||||
|
||||
@@ -96,9 +97,6 @@ namespace OpenRA.Graphics
|
||||
return new int2((int)Math.Ceiling(lines.Max(lineWidth)), lines.Length * size);
|
||||
}
|
||||
|
||||
Cache<Pair<char, Color>, GlyphInfo> glyphs;
|
||||
Face face;
|
||||
|
||||
GlyphInfo CreateGlyph(Pair<char, Color> c)
|
||||
{
|
||||
face.LoadChar(c.First, LoadFlags.Default, LoadTarget.Normal);
|
||||
|
||||
Reference in New Issue
Block a user