Skip to content

Commit ed63358

Browse files
authored
Merge branch 'master' into patch-1
2 parents 082b0ee + 6ffe496 commit ed63358

File tree

4 files changed

+78
-34
lines changed

4 files changed

+78
-34
lines changed

engine/Sandbox.AppSystem/AppSystem.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ public virtual void Shutdown()
251251
GlobalContext.Menu.Shutdown();
252252
GlobalContext.Game.Shutdown();
253253

254+
// Clear font manager cache, dispose native font handles
255+
FontManager.Instance.Clear( true );
256+
254257
// Drain any disposables queued for end-of-frame — no more frames
255258
// will run during shutdown so these would otherwise leak.
256259
EngineLoop.DrainFrameEndDisposables();

engine/Sandbox.Engine/Systems/Render/TextRendering/FontManager.cs

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using SkiaSharp;
2-
using System.Collections.Concurrent;
1+
using Sandbox.Engine;
2+
using SkiaSharp;
33
using System.Diagnostics.CodeAnalysis;
44
using Topten.RichTextKit;
55

@@ -24,11 +24,24 @@ internal class FontManager : FontMapper
2424
{
2525
public static FontManager Instance = new FontManager();
2626

27-
static ConcurrentDictionary<int, SKTypeface> LoadedFonts = new();
28-
29-
static Dictionary<int, SKTypeface> Cache = new();
27+
public IEnumerable<string> FontFamilies
28+
{
29+
get
30+
{
31+
lock ( LoadedFonts )
32+
{
33+
return LoadedFonts.Values.Select( x => x.Typeface.FamilyName ).Distinct().ToList();
34+
}
35+
}
36+
}
3037

31-
public static IEnumerable<string> FontFamilies => LoadedFonts.Values.Select( x => x.FamilyName ).Distinct();
38+
struct LoadedTypeface
39+
{
40+
public SKTypeface Typeface;
41+
public bool IsMenu;
42+
}
43+
Dictionary<int, LoadedTypeface> LoadedFonts = new();
44+
Dictionary<int, SKTypeface> Cache = new();
3245

3346
private void Load( System.IO.Stream stream )
3447
{
@@ -37,19 +50,36 @@ private void Load( System.IO.Stream stream )
3750
var face = SKTypeface.FromStream( stream );
3851
if ( face is null ) return;
3952

40-
var hash = HashCode.Combine( face.FamilyName, face.FontWeight, face.FontSlant );
53+
bool isMenu = GlobalContext.Current == GlobalContext.Menu;
4154

42-
LoadedFonts[hash] = face;
55+
var hash = HashCode.Combine( face.FamilyName, face.FontWeight, face.FontSlant );
56+
lock ( LoadedFonts )
57+
{
58+
if ( LoadedFonts.ContainsKey( hash ) )
59+
{
60+
face.Dispose();
61+
return;
62+
}
63+
64+
LoadedFonts[hash] = new()
65+
{
66+
Typeface = face,
67+
IsMenu = isMenu
68+
};
69+
}
4370

44-
Log.Trace( $"Loaded font {face.FamilyName} weight {face.FontWeight}" );
71+
Log.Trace( $"Loaded font {face.FamilyName} weight {face.FontWeight} (IsMenu: {isMenu})" );
4572
}
4673

4774
List<FileWatch> watchers = new();
4875

4976
public void LoadAll( BaseFileSystem fileSystem )
5077
{
51-
// If we're loading new fonts, we may have cached it already
52-
Cache.Clear();
78+
lock ( Cache )
79+
{
80+
// empty cache new fonts can replace fallbacks and best-matches
81+
Cache.Clear();
82+
}
5383

5484
var fontFiles = fileSystem.FindFile( "/fonts/", "*.ttf", true )
5585
.Union( fileSystem.FindFile( "/fonts/", "*.otf", true ) );
@@ -71,7 +101,10 @@ public void LoadAll( BaseFileSystem fileSystem )
71101

72102
private void OnFontFilesChanged( FileWatch w, BaseFileSystem fs )
73103
{
74-
Cache.Clear();
104+
lock ( Cache )
105+
{
106+
Cache.Clear();
107+
}
75108

76109
foreach ( var file in w.Changes )
77110
{
@@ -85,18 +118,22 @@ private void OnFontFilesChanged( FileWatch w, BaseFileSystem fs )
85118
/// </summary>
86119
private SKTypeface GetBestTypeface( IStyle style )
87120
{
88-
// Must be of same family
89-
var familyFonts = LoadedFonts.Values.Where( x => string.Equals( x.FamilyName, style.FontFamily, StringComparison.OrdinalIgnoreCase ) );
90-
if ( !familyFonts.Any() ) return null;
91-
92-
// Get matching slants, if no matching fallback to regular
93-
var slantFonts = familyFonts.Where( x => x.IsItalic == style.FontItalic );
94-
if ( slantFonts.Any() ) familyFonts = slantFonts;
95-
96-
// Finally get the closest font weight
97-
return familyFonts.Select( x => new { x, distance = Math.Abs( x.FontWeight - style.FontWeight ) } )
98-
.OrderBy( x => x.distance )
99-
.First().x;
121+
lock ( LoadedFonts )
122+
{
123+
// Must be of same family
124+
var familyFonts = LoadedFonts.Values.Select( x => x.Typeface )
125+
.Where( x => string.Equals( x.FamilyName, style.FontFamily, StringComparison.OrdinalIgnoreCase ) );
126+
if ( !familyFonts.Any() ) return null;
127+
128+
// Get matching slants, if no matching fallback to regular
129+
var slantFonts = familyFonts.Where( x => x.IsItalic == style.FontItalic );
130+
if ( slantFonts.Any() ) familyFonts = slantFonts;
131+
132+
// Finally get the closest font weight
133+
return familyFonts.Select( x => new { x, distance = Math.Abs( x.FontWeight - style.FontWeight ) } )
134+
.OrderBy( x => x.distance )
135+
.First().x;
136+
}
100137
}
101138

102139
public override SKTypeface TypefaceFromStyle( IStyle style, bool ignoreFontVariants )
@@ -121,26 +158,30 @@ public override SKTypeface TypefaceFromStyle( IStyle style, bool ignoreFontVaria
121158
return f;
122159
}
123160

124-
public void Reset()
161+
public void Clear( bool removeMenu )
125162
{
126163
foreach ( var watcher in watchers )
127164
{
128165
watcher.Dispose();
129166
}
130167
watchers.Clear();
131168

132-
foreach ( var (_, font) in LoadedFonts )
169+
lock ( LoadedFonts )
133170
{
134-
font?.Dispose();
171+
foreach ( var (hash, font) in LoadedFonts.ToArray() )
172+
{
173+
if ( !removeMenu && font.IsMenu )
174+
continue;
175+
176+
LoadedFonts.Remove( hash );
177+
font.Typeface?.Dispose();
178+
}
135179
}
136180

137-
foreach ( var (_, font) in Cache )
181+
lock ( Cache )
138182
{
139-
font?.Dispose();
183+
Cache.Clear();
140184
}
141-
142-
LoadedFonts.Clear();
143-
Cache.Clear();
144185
}
145186
}
146187

engine/Sandbox.GameInstance/GameInstanceDll.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public void ResetEnvironment()
147147
DidMountNetworkedFiles = false;
148148
}
149149

150-
FontManager.Instance.Reset();
150+
FontManager.Instance.Clear( false );
151151
FontManager.Instance.LoadAll( FileSystem.Mounted );
152152

153153
AssemblyEnroller?.Dispose();

engine/Sandbox.Tools/Utility/Utility.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ public static string GetSearchPaths()
665665
return EngineGlobal.GetGameSearchPath();
666666
}
667667

668-
public static IEnumerable<string> FontFamilies => FontManager.FontFamilies;
668+
public static IEnumerable<string> FontFamilies => FontManager.Instance.FontFamilies;
669669

670670
/// <summary>
671671
/// Access to the client's render settings

0 commit comments

Comments
 (0)