Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
d49b9ff
M1: Added Runtime SDF placeholder.
yuechen-li-dev Jan 29, 2026
3275c16
M2. MSDF Cache Scaffold.
yuechen-li-dev Jan 29, 2026
5d9185f
M3: Font works end to end, renders ugly circles.
yuechen-li-dev Jan 29, 2026
75f5369
M4 WIP: Buggy implementation with flickers so far.
yuechen-li-dev Jan 29, 2026
1fa1cd4
It works, but each text spins computer like jet engine.
yuechen-li-dev Jan 29, 2026
2f0b165
Async implementation and various optimizations.
yuechen-li-dev Jan 29, 2026
dd4f8e0
Fixed bad copypaste for SpriteFontAssetCompiler.cs
yuechen-li-dev Jan 30, 2026
0dc668c
Less sloppy Async.
yuechen-li-dev Jan 31, 2026
69e2f15
Revert "Less sloppy Async."
yuechen-li-dev Jan 31, 2026
7ad4caa
Remove runtime SDF bake size plumbing
yuechen-li-dev Feb 1, 2026
b04c959
Merge pull request #4 from yuechen-li-dev/codex/refactor-glyph-sizing…
yuechen-li-dev Feb 1, 2026
d5d9576
Adjusted default bakesize to 64 so SDF is high quality.
yuechen-li-dev Feb 1, 2026
18e227d
Merge branch 'stride3d:master' into Runtime-SDF-Font
yuechen-li-dev Feb 2, 2026
217a8fd
Changed to channel based async design.
yuechen-li-dev Feb 2, 2026
e2e9f09
Channel based refactor for async, introduce interface for easier libr…
yuechen-li-dev Feb 2, 2026
ae2e234
See above message. Wrong commit lol.
yuechen-li-dev Feb 2, 2026
a0058c4
Merge branch 'Runtime-SDF-Font' of https://github.com/yuechen-li-dev/…
yuechen-li-dev Feb 2, 2026
914819d
refactor for future MSDFGeneration.
yuechen-li-dev Feb 3, 2026
048b521
Initial wiring. Need to fix generationPipeline.
yuechen-li-dev Feb 3, 2026
d7d8d5b
Ok, it renders but only as blocks or dots. Debug time.
yuechen-li-dev Feb 3, 2026
dbba408
It works! Mostly. Need to squash some font specific bugs.
yuechen-li-dev Feb 4, 2026
a22eb17
Next try with MsdfGen too.
yuechen-li-dev Feb 4, 2026
e455a49
Comment edits.
yuechen-li-dev Feb 4, 2026
cc65561
clean up gitignore of temp file.
yuechen-li-dev Feb 4, 2026
4d9159c
Move offset logic out of ApplyUploadedGlyph for safety. Change glyphk…
yuechen-li-dev Feb 4, 2026
dd6b53a
Minor changes so VS would have less messages.
yuechen-li-dev Feb 4, 2026
738280e
Cleaned up nullable in FontSystem.
yuechen-li-dev Feb 9, 2026
92e5460
removed unsafe keyword and unneed cast for buffer copy.
yuechen-li-dev Feb 9, 2026
739c69e
Spacing consistancy in pregenerated glyph method.
yuechen-li-dev Feb 9, 2026
0bea15f
Changed the font manager to private.
yuechen-li-dev Feb 9, 2026
6a98305
Remove extra colon.
yuechen-li-dev Feb 9, 2026
a457fd6
Remerged MSDF method into FontManager. Remerged unused overload. Gene…
yuechen-li-dev Feb 10, 2026
3463072
Changed warning from scaffolding to indicate experimental feature. Si…
yuechen-li-dev Feb 10, 2026
113d16d
Moved comment to more accurately depict pipeline for upload step.
yuechen-li-dev Feb 10, 2026
f2930a2
Small refactor for EnsureSdfScheduled to address 0 dimension glyphs a…
yuechen-li-dev Feb 10, 2026
3176a76
Removed debug message from FontCacheManagerMSDF and cleanup for reada…
yuechen-li-dev Feb 10, 2026
dcfae4b
Move oversized glyph dimension check from FontCacheManager to Font fi…
yuechen-li-dev Feb 10, 2026
f8aadd1
Re-added accidentally deleted packer logic.
yuechen-li-dev Feb 10, 2026
0753019
remove unused bool isClosed definition from outline extractor.
yuechen-li-dev Feb 11, 2026
0a089c8
Merge master into runtime-msdf-font
yuechen-li-dev Apr 10, 2026
887ebba
Minor fix to align change with current master repo.
yuechen-li-dev Apr 10, 2026
5148580
Added unit test for RT-SDF font path. Made YamlSerializer stricter.
yuechen-li-dev Apr 10, 2026
6de2b12
Step 1: Minimal initial fix for thumbnail generation of RT-SDF font b…
yuechen-li-dev Apr 10, 2026
350f348
Step 2: Diagnosed and fixed layout state mutation bug.
yuechen-li-dev Apr 10, 2026
9f062ef
Step 3 of Diagnostic test.
yuechen-li-dev Apr 10, 2026
20d3d2e
Add bounded runtime SDF glyph upload convergence diagnostic
yuechen-li-dev Apr 10, 2026
9883203
Merge pull request #5 from yuechen-li-dev/codex/add-bounded-warmup-di…
yuechen-li-dev Apr 10, 2026
3218db5
Fix runtime SDF font thumbnail generation in the editor. Runtime SDF …
yuechen-li-dev Apr 10, 2026
e14b84b
Register Runtime SDF to be accessible in Game Studio.
yuechen-li-dev Apr 11, 2026
5694d01
FontManager indentation.
yuechen-li-dev Apr 11, 2026
7b986e1
Split generator functionality/bitmap based SDF fallback into partial …
yuechen-li-dev Apr 11, 2026
beb3a62
Flipped font in outline diagnostic rasterizer to correct orientation.
yuechen-li-dev Apr 11, 2026
f3d4c00
Added nullablility on FontSystem.cs.
yuechen-li-dev Apr 11, 2026
dd6dcf4
Finalize runtime SDF layout diagnostics as regression tests
yuechen-li-dev Apr 11, 2026
c02a360
Merge pull request #6 from yuechen-li-dev/codex/finalize-regression-t…
yuechen-li-dev Apr 11, 2026
aafbc06
Merge branch 'master' into runtime-msdf-font
yuechen-li-dev Apr 12, 2026
f567bc2
Fix accidentally repeated remora msdf package in central package mana…
yuechen-li-dev Apr 12, 2026
579573e
Fix MSDF outline extractor to use FreeTypeNative types
yuechen-li-dev Apr 12, 2026
fbf3164
Merge pull request #7 from yuechen-li-dev/codex/investigate-sharpfont…
yuechen-li-dev Apr 12, 2026
f0d1f9d
Added font outline extraction P/invoke to FreeTypeNative.cs
yuechen-li-dev Apr 12, 2026
79b3cac
Fix outline extraction RT-SDF via Native Freetype.
yuechen-li-dev Apr 12, 2026
053dfa1
Added warmup API, and preset warmup for basic latin characters.
yuechen-li-dev Apr 13, 2026
b75e6cd
Use assert.fail instead of assert.true(false) in new unit test.
yuechen-li-dev Apr 13, 2026
59f29f4
Add MSDF rasterizer and outline extractor regression tests
yuechen-li-dev Apr 13, 2026
dad95f2
Merge pull request #8 from yuechen-li-dev/codex/add-xunit-tests-for-m…
yuechen-li-dev Apr 13, 2026
addf782
Fix newly added unit tests.
yuechen-li-dev Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sources/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.264" />
<PackageVersion Include="PolySharp" Version="1.15.0" />
<PackageVersion Include="Remora.MSDFGen" Version="1.0.0" />
<PackageVersion Include="ServiceWire" Version="5.6.0" />
<PackageVersion Include="SharpDX" Version="4.2.0" />
<PackageVersion Include="SharpDX.D3DCompiler" Version="4.2.0" />
Expand Down Expand Up @@ -71,6 +72,7 @@
<PackageVersion Include="System.Reactive" Version="6.1.0" />
<PackageVersion Include="System.Reactive.Linq" Version="6.1.0" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="10.0.0" />
<PackageVersion Include="MSDF-Sharp.Core" Version="1.0.2"/>
</ItemGroup>
<!-- Visual Studio dependencies -->
<ItemGroup>
Expand Down
10 changes: 6 additions & 4 deletions sources/core/Stride.Core.Design/Yaml/YamlSerializerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ public virtual void ResetCache()

private void AssemblyRegistered(object? sender, AssemblyRegisteredEventArgs e)
{
// Process only our own assemblies
if (!e.Categories.Contains(AssemblyCommonCategories.Engine))
// Process engine and asset assemblies, since asset YAML tag resolution depends on both.
if (!e.Categories.Contains(AssemblyCommonCategories.Engine) &&
!e.Categories.Contains(AssemblyCommonCategories.Assets))
return;

lock (Lock)
Expand All @@ -56,8 +57,9 @@ private void AssemblyRegistered(object? sender, AssemblyRegisteredEventArgs e)

private void AssemblyUnregistered(object? sender, AssemblyRegisteredEventArgs e)
{
// Process only our own assemblies
if (!e.Categories.Contains(AssemblyCommonCategories.Engine))
// Process engine and asset assemblies, since asset YAML tag resolution depends on both.
if (!e.Categories.Contains(AssemblyCommonCategories.Engine) &&
!e.Categories.Contains(AssemblyCommonCategories.Assets))
return;

lock (Lock)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ TemplateFolders:
- !file Templates/Assets/BepuPhysics/HullAsset.sdtpl
- !file Templates/Assets/Fonts/OfflineRasterizedSpriteFont.sdtpl
- !file Templates/Assets/Fonts/RuntimeRasterizedSpriteFont.sdtpl
- !file Templates/Assets/Fonts/RuntimeSignedDistanceFieldSpriteFont.sdtpl
- !file Templates/Assets/Fonts/SignedDistanceFieldSpriteFont.sdtpl
- !file Templates/Assets/Materials/DefaultMaterial.sdtpl
- !file Templates/Assets/Materials/DiffuseMaterial.sdtpl
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
!TemplateAssetFactory
Id: 8D9A0F2D-2C9A-4F59-B8C1-0F4A6E6B9D11
AssetTypeName: SpriteFontAsset
Name: "Runtime SDF"
Scope: Asset
Description: A runtime signed-distance-field sprite font asset in which glyphs are generated at runtime from the embedded font file
Group: Font
Order: -70
Icon: ..\.sdtpl\SpriteFont.png
DefaultOutputName: SpriteFont
FactoryTypeName: RuntimeSignedDistanceFieldSpriteFontFactory
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System.IO;
using Stride.Assets.SpriteFont;
using Stride.Core.Assets;
using Stride.Core.Assets.Compiler;
using Stride.Core.BuildEngine;
using Stride.Core.IO;
using Stride.Assets.SpriteFont;
using Stride.Editor.Resources;
using Stride.Editor.Thumbnails;
using Stride.Graphics;
using Stride.Graphics.Font;

namespace Stride.Assets.Presentation.Thumbnails
{
Expand Down Expand Up @@ -55,12 +56,42 @@ public FontThumbnailBuildCommand(ThumbnailCompilerContext context, string url, A

protected override void SetThumbnailParameters()
{
Font = LoadedAsset;
FontSize = 0.75f;
TitleText = BuildTitleText();

if (Font != null && Font.FontType == SpriteFontType.SDF)
bool usingRasterizedPreviewForRuntimeSdf = false;

if (LoadedAsset is RuntimeSignedDistanceFieldSpriteFont runtimeSdfFont)
{
var fontSystem = runtimeSdfFont.FontSystem;
if (fontSystem != null)
{
Font = fontSystem.NewDynamic(
defaultSize: runtimeSdfFont.Size,
fontName: runtimeSdfFont.FontName,
style: runtimeSdfFont.Style,
antiAliasMode: FontAntiAliasMode.Grayscale,
useKerning: runtimeSdfFont.UseKerning,
extraSpacing: 0,
extraLineSpacing: 0,
defaultCharacter: runtimeSdfFont.DefaultCharacter ?? ' ');

usingRasterizedPreviewForRuntimeSdf = true;
}
else
{
Font = LoadedAsset;
}
}
else
{
Font = LoadedAsset;
}

if (!usingRasterizedPreviewForRuntimeSdf && Font != null && Font.FontType == SpriteFontType.SDF)
EffectInstance = UIBatch.SDFSpriteFontEffect;
else
EffectInstance = null;
}

protected virtual string BuildTitleText()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System;
using System.IO;
using Stride.Core.Assets;
using Stride.Core.Mathematics;
using Stride.Engine;
using Stride.Graphics;
using Stride.Graphics.Font;
using Stride.Rendering;
using Stride.Rendering.Compositing;

Expand Down Expand Up @@ -92,12 +94,23 @@ protected override void RenderSprites(RenderDrawContext context)
// Get the exact size of the font rendered with the desired size
typeNameSize = Font.MeasureString(TitleText, desiredFontSize);

// force pre-generation of the glyph
// Force pre-generation of the glyphs
Font.PreGenerateGlyphs(TitleText, new Vector2(desiredFontSize, desiredFontSize));
}

// the title text
SpriteBatch.DrawString(Font, TitleText, desiredFontSize, thumbnailSize/2, FontColor, 0, typeNameSize/2, scale*Vector2.One, SpriteEffects.None, 1, TextAlignment.Center);
SpriteBatch.DrawString(
Font,
TitleText,
desiredFontSize,
thumbnailSize / 2,
FontColor,
0,
typeNameSize / 2,
scale * Vector2.One,
SpriteEffects.None,
1,
TextAlignment.Center);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Stride.Assets.SpriteFont;
using Stride.Core.Assets;
using Stride.Core.Reflection;
using Stride.Core.Yaml;
using Xunit;
using Xunit.Abstractions;

namespace Stride.Assets.Tests.SpriteFont
{
public class RuntimeSignedDistanceFieldSpriteFontDiagnosticsTests
{
private readonly ITestOutputHelper output;

public RuntimeSignedDistanceFieldSpriteFontDiagnosticsTests(ITestOutputHelper output)
{
this.output = output;
}

private static string GetLogPath(string name)
{
var dir = Path.Combine(AppContext.BaseDirectory, "DiagnosticLogs");
Directory.CreateDirectory(dir);
return Path.Combine(dir, $"{name}.txt");
}

private void WriteLog(string name, string content)
{
var path = GetLogPath(name);
File.WriteAllText(path, content);
output.WriteLine($"Diagnostic log written: {path}");
}

[Fact]
public void RuntimeSdfType_Should_Exist_And_Have_Expected_DataContract_Name()
{
var type = typeof(RuntimeSignedDistanceFieldSpriteFontType);

Assert.Equal("RuntimeSignedDistanceFieldSpriteFontType", type.Name);
}

[Fact]
public void AssemblyRegistry_Should_Contain_RuntimeSdfType_Assembly()
{
var allAssemblies = AssemblyRegistry.FindAll().ToList();
var runtimeAssembly = typeof(RuntimeSignedDistanceFieldSpriteFontType).Assembly;

Assert.Contains(runtimeAssembly, allAssemblies);
}

[Fact]
public void AssetYamlSerializer_Should_Deserialize_RuntimeSdf_SpriteFontAsset()
{
var yaml = """
!SpriteFont
Id: cad320b3-3f55-43a4-bb8b-46097724ba24
SerializedVersion: {Stride: 2.0.0.0}
Tags: []
FontSource: !FileFontProvider
Source: !file ../../../../../Downloads/NotoSansCJK-Regular.ttc
FontType: !RuntimeSignedDistanceFieldSpriteFontType
Size: 64.0
PixelRange: 10
Spacing: 2.0
""";

var serializer = new AssetYamlSerializer();
try
{
using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(yaml));
var result = serializer.Deserialize(stream, typeof(SpriteFontAsset));

var asset = Assert.IsType<SpriteFontAsset>(result);
var fontType = Assert.IsType<RuntimeSignedDistanceFieldSpriteFontType>(asset.FontType);

Assert.Equal(64.0f, fontType.Size);
Assert.Equal(10, fontType.PixelRange);
Assert.Equal(2.0f, asset.Spacing);
}
catch (Exception)
{
throw;
}
}

[Fact]
public void AssetYamlSerializer_Settings_Should_Know_RuntimeSdfType_Assembly()
{
var serializer = new AssetYamlSerializer();
var settings = serializer.GetSerializerSettings();
var runtimeAssembly = typeof(RuntimeSignedDistanceFieldSpriteFontType).Assembly;

Assert.NotNull(settings);
}

[Fact]
public void SpriteFontAssetCompiler_Dispatch_Precondition_Should_See_RuntimeSdf_FontType()
{
var asset = new SpriteFontAsset
{
FontType = new RuntimeSignedDistanceFieldSpriteFontType
{
Size = 64,
PixelRange = 10,
Padding = 2
},
Spacing = 2.0f
};

Assert.True(asset.FontType is RuntimeSignedDistanceFieldSpriteFontType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<Compile Include="..\..\assets\Stride.Core.Assets.Tests\Helpers\GuidGenerator.cs">
<Link>GuidGenerator.cs</Link>
</Compile>
<Compile Include="SpriteFont\RuntimeSignedDistanceFieldDiagnosticsTests.cs" />
<Compile Include="TestMemberRequiredComponentChecks.cs" />
<Compile Include="XunitAttributes.cs" />
<Compile Include="TestMaterialGenerator.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using System.ComponentModel;
using Stride.Core;
using Stride.Core.Annotations;
using Stride.Core.Mathematics;

namespace Stride.Assets.SpriteFont
{
[DataContract("RuntimeSignedDistanceFieldSpriteFontType")]
[Display("Runtime SDF")]
public class RuntimeSignedDistanceFieldSpriteFontType : SpriteFontTypeBase
{
/// <inheritdoc/>
[DataMember(30)]
[DataMemberRange(MathUtil.ZeroTolerance, 2)]
[DefaultValue(20)]
[Display("Default Size")]
public override float Size { get; set; } = 64;

/// <summary>
/// Distance field range/spread (in pixels) used during MSDF generation.
/// </summary>
[DataMember(40)]
[DefaultValue(8)]
[DataMemberRange(1, 64, 1, 4, 0)]
[Display("Pixel Range")]
public int PixelRange { get; set; } = 8;

/// <summary>
/// Extra padding around each glyph inside the atlas (in pixels).
/// </summary>
[DataMember(50)]
[DefaultValue(2)]
[DataMemberRange(0, 16, 1, 2, 0)]
[Display("Padding")]
public int Padding { get; set; } = 2;
}
}
Loading