Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3880741
feat: font_src on TextShape, UiText, UiInput and UiDropdown
eordano Sep 15, 2026
a05c42e
Merge branch 'dev' into feat/font-src
popuz Sep 16, 2026
2fd622f
fix: restrict custom fonts to scene content
popuz Sep 16, 2026
9142740
refactor: simplify scene font asset ownership
popuz Sep 16, 2026
c7db4f8
feat: restore Fontsource families while rejecting external font URLs
popuz Sep 17, 2026
55d6cfd
fix: preserve cached font format and cover OpenType loading
popuz Sep 17, 2026
1d8ed3f
fix: restrict font files to TrueType per protocol
popuz Sep 17, 2026
10188df
Merge branch 'dev' into feat/font-src
popuz Sep 17, 2026
9b079ec
fix: tighten scene font resource ownership and ECS lifecycle
popuz Sep 17, 2026
c70e8ff
fix: respect deferred deletion when releasing UI and pooled components
popuz Sep 17, 2026
899edc6
Merge remote-tracking branch 'origin/feat/font-src' into feat/font-src
popuz Sep 17, 2026
d8e9089
chore: move shared cache ownership fix to PR #10144
popuz Sep 17, 2026
8a6f22c
fix: bound font downloads and follow threading conventions
popuz Sep 17, 2026
236b2b9
Merge remote-tracking branch 'origin/dev' into feat/font-src
popuz Sep 22, 2026
6ac7057
Merge branch 'dev' into feat/font-src
popuz Sep 23, 2026
06f2cc4
Merge branch 'dev' into feat/font-src
popuz Sep 23, 2026
8774df3
compilation fix - imported using
popuz Sep 23, 2026
b26d961
Merge branch 'dev' into feat/font-src
popuz Sep 25, 2026
a69a152
fix: resolve lint warnings in font_src changes
popuz Sep 25, 2026
86e4ca0
Merge remote-tracking branch 'origin/feat/font-src' into feat/font-src
popuz Sep 25, 2026
13370a2
Merge branch 'dev' into feat/font-src
popuz Sep 25, 2026
9e7cc84
Merge branch 'dev' into feat/font-src
popuz Sep 28, 2026
4966c7b
chore: add profiler markers to runtime font asset creation
popuz Sep 28, 2026
753b1d1
chore: add profiler markers to scene font request and receipt
popuz Oct 1, 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
5 changes: 5 additions & 0 deletions Explorer/Assets/AddressableAssetsData/AssetGroups/UI.asset
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,11 @@ MonoBehaviour:
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 96ae0a2159a39234f858ea23bdcc74ad
m_Address: Assets/TextMesh Pro/Fonts & Materials/Inter-SemiBold SDF.asset
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 989cf1173881645c598573447b2b56d8
m_Address: HidesRow
m_ReadOnly: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,18 @@ public ReleaseReferenceComponentsSystem(World world, IComponentPoolsRegistry com
protected override void Update(float _)
{
Query query = World.Query(in queryDescription);
ReleaseComponentsToPool(in query);
ReleaseComponentsToPool(in query, componentPoolsRegistry, respectDeferredDeletion: true);
}

public void FinalizeComponents(in Query query)
{
ReleaseComponentsToPool(in query);
ReleaseComponentsToPool(in query, componentPoolsRegistry);
}

private void ReleaseComponentsToPool(in Query query)
{
ReleaseComponentsToPool(query, componentPoolsRegistry);
}
public static void ReleaseComponentsToPool(in Query query, IComponentPoolsRegistry componentPoolsRegistry) =>

@dalkia dalkia Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReleaseReferenceComponentsSystem: please move this change to its own PR

The respectDeferredDeletion change isn't related to font_src, but it changes cleanup for every pooled reference component in every scene world. I don't think it should be hidden inside a 3.7k-line feature PR.

As far as I can tell, it also has no effect at runtime today:

  • ReleaseReferenceComponentsSystem is only added to scene worlds (ECSWorldFactory.cs:95).
  • Nothing in a scene world sets DeferDeletion = true. The only places that set it are in the global world (AvatarShapeHandlerSystem, ResolveSceneStateByIncreasingRadiusSystem, RapidSceneReloadDebugSystem, and AvatarCleanUpSystem
    resetting it to false). This PR only reads the flag outside of tests.
  • So the new continue only runs in ReleaseReferenceComponentsSystemShould.

The fix itself makes sense as a guard for the future. It lines up with ReleasePoolableComponentSystem and DestroyEntitiesSystem. It would also stop repeated double-releases to the pool if a scene-world system ever defers deletion. But
that's a speculative change to shared ECS cleanup, and it deserves its own PR and review: something like fix: don't release pooled components of deferred-deletion entities, together with ReleaseReferenceComponentsSystemShould. It also
keeps the font feature easy to revert or bisect.

A small nit for when it moves: the public 2-argument overload (which means false) next to a private 3-argument one is a bit awkward. Putting the check inline in Update(), or giving the flag a default value, would be simpler.

The same applies, less strongly, to the if (deleteEntityIntention.DeferDeletion) return; early-outs in the new ReleaseTextShapeSystem / UITextReleaseSystem / UIInputReleaseSystem / UIDropdownReleaseSystem. They can't trigger in
scene worlds either, though at least they follow the existing ReleasePoolableComponentSystem pattern.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2 — R24] Scope: respectDeferredDeletion change is unrelated to font_src. This change modifies ReleaseReferenceComponentsSystem to skip entities with DeferDeletion = true during normal updates, which is a correctness fix for component pooling — but it is orthogonal to the font_src feature. As dalkia noted in a prior comment, this should ideally be in its own PR.

The change itself is correct and well-tested (ReleaseReferenceComponentsSystemShould covers both paths), but bundling it here makes the font_src PR harder to revert independently.

ReleaseComponentsToPool(in query, componentPoolsRegistry, respectDeferredDeletion: false);

public static void ReleaseComponentsToPool(in Query query, IComponentPoolsRegistry componentPoolsRegistry)
private static void ReleaseComponentsToPool(in Query query, IComponentPoolsRegistry componentPoolsRegistry, bool respectDeferredDeletion)
{
// Profiling required, O(N^4)
foreach (ref Chunk chunk in query.GetChunkIterator())
Expand All @@ -52,6 +50,9 @@ public static void ReleaseComponentsToPool(in Query query, IComponentPoolsRegist

foreach (int entityIndex in chunk)
{
if (respectDeferredDeletion && chunk.Get<DeleteEntityIntention>(entityIndex).DeferDeletion)
continue;

for (var i = 0; i < array2D.Length; i++)
{
// if it is called on a value type it will cause an allocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"GUID:3c7b57a14671040bd8c549056adc04f5",
"GUID:fa7b3fdbb04d67549916da7bd2af58ab",
"GUID:e25ef972de004615a22937e739de2def",
"GUID:0f4c0f120707fb74497f5d581b9858f8"
"GUID:0f4c0f120707fb74497f5d581b9858f8",
"GUID:6055be8ebefd69e48b49212b09b47b2f"
],
"includePlatforms": [
"Editor"
Expand Down
23 changes: 23 additions & 0 deletions Explorer/Assets/DCL/Infrastructure/ECS/TestSuite/TestFonts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.IO;
using TMPro;
using UnityEngine;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.TextCore.Text;

namespace ECS.TestSuite
{
public static class TestFonts
{
public static readonly string PATH = Path.Combine(Application.dataPath, "DCL/SDKComponents/Fonts/LiberationSans-Regular.ttf");

private const int SAMPLING_POINT_SIZE = 90;
private const int ATLAS_PADDING = 9;
private const int ATLAS_SIZE = 1024;

public static TMP_FontAsset CreateTextMeshProFont() =>
TMP_FontAsset.CreateFontAsset(PATH, 0, SAMPLING_POINT_SIZE, ATLAS_PADDING, GlyphRenderMode.SDFAA, ATLAS_SIZE, ATLAS_SIZE);

public static FontAsset CreateUIToolkitFont() =>
FontAsset.CreateFontAsset(PATH, 0, SAMPLING_POINT_SIZE, ATLAS_PADDING, GlyphRenderMode.SDFAA, ATLAS_SIZE, ATLAS_SIZE);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,16 @@ public CommonLoadingArguments(string url, URLSubdirectory customEmbeddedSubDirec
int attempts = StreamableLoadingDefaults.ATTEMPTS_COUNT,
AssetSource permittedSources = AssetSource.Web,
AssetSource currentSource = AssetSource.Web,
CancellationTokenSource cancellationTokenSource = null) :
CancellationTokenSource? cancellationTokenSource = null) :
this(URLAddress.FromString(url), customEmbeddedSubDirectory, timeout, attempts, permittedSources, currentSource, cancellationTokenSource) { }

public CommonLoadingArguments WithURL(URLAddress url)
{
CommonLoadingArguments copy = this;
copy.URL = url;
return copy;
}

// Always override attempts count for streamable assets as repetitions are handled in LoadSystemBase
public static implicit operator CommonArguments(in CommonLoadingArguments commonLoadingArguments) =>
new (commonLoadingArguments.URL, RetryPolicy.WithRetries(1), timeout: commonLoadingArguments.Timeout);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using ECS.Groups;
using ECS.StreamableLoading.AssetBundles;
using ECS.StreamableLoading.AudioClips;
using ECS.StreamableLoading.Fonts;
using ECS.StreamableLoading.GLTF;
using ECS.StreamableLoading.NFTShapes;
using ECS.StreamableLoading.Textures;
Expand All @@ -29,6 +30,7 @@ static AssetsDeferredLoadingSystem()
CreateQuery<GetTextureIntention, TextureData>(),
CreateQuery<GetNFTTypeIntention, NftTypeResult>(),
CreateQuery<GetAudioClipIntention, AudioClipData>(),
CreateQuery<GetFontIntention, FontData>(),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using DCL.Optimization.PerformanceBudgeting;
using ECS.Prioritization.Components;
using ECS.StreamableLoading.Common.Components;
using ECS.StreamableLoading.Fonts;
using ECS.StreamableLoading.Textures;
using ECS.TestSuite;
using NSubstitute;
Expand Down Expand Up @@ -123,5 +124,26 @@ public void IntentionsAllowedWhenBudgetIsReleased()
Assert.AreEqual(i < 8,
world.Get<StreamableLoadingState>(entities[i]).Value == StreamableLoadingState.Status.Allowed);
}

[Test]
public void AllowFontIntentions()
{
Entity entity = world.Create(
new GetFontIntention
{
Src = "fonts/Roboto.ttf",
CommonArguments = new CommonLoadingArguments(""),
},
(IPartitionComponent)new PartitionComponent
{
Bucket = 0,
IsBehind = false,
IsDirty = true,
}, new StreamableLoadingState());

system.Update(0);

Assert.That(world.Get<StreamableLoadingState>(entity).Value, Is.EqualTo(StreamableLoadingState.Status.Allowed));
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Cysharp.Threading.Tasks;
using DCL.Diagnostics;
using DCL.Profiling;
using Unity.Profiling;

namespace ECS.StreamableLoading.Fonts
{
public class FontData : StreamableRefCountData<FontFamilyAssets>
{
private readonly FontFileStore.Lease?[] files;

public FontData(FontFamilyAssets assets, params FontFileStore.Lease?[] files) : base(assets, ReportCategory.SDK_FONTS)
{
this.files = files;
ProfilingCounters.FontsAmount.Value++;
}

protected override ref ProfilerCounterValue<int> totalCount => ref ProfilingCounters.FontsAmount;

protected override ref ProfilerCounterValue<int> referencedCount => ref ProfilingCounters.FontsReferenced;

protected override void DestroyObject()
{
Asset.Destroy();
FontFileStore.ReleaseAfterDestructionAsync(files).Forget(e => ReportHub.LogException(e, ReportCategory.SDK_FONTS));
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Text;
using UnityEngine.Networking;

namespace ECS.StreamableLoading.Fonts
{
public class FontDownloadHandler : DownloadHandlerScript
{
private const int CHUNK_BYTES = 64 * 1024;

private readonly int maxBytes;
private byte[] buffer = Array.Empty<byte>();
private int length;

public bool LimitExceeded { get; private set; }

public FontDownloadHandler(int maxBytes) : base(new byte[CHUNK_BYTES])
{
this.maxBytes = maxBytes;
}

protected override void ReceiveContentLengthHeader(ulong contentLength) =>
LimitExceeded |= contentLength > (ulong)maxBytes;

protected override bool ReceiveData(byte[] receivedData, int dataLength)
{
if (LimitExceeded || dataLength > maxBytes - length)
{
LimitExceeded = true;
return false;
}

int required = length + dataLength;

if (required > buffer.Length)
Array.Resize(ref buffer, Math.Min(maxBytes, Math.Max(required, Math.Max(CHUNK_BYTES, buffer.Length * 2))));

Buffer.BlockCopy(receivedData, 0, buffer, length, dataLength);
length = required;
return true;
}

protected override byte[] GetData()
{
var result = new byte[length];
Buffer.BlockCopy(buffer, 0, result, 0, length);
return result;
}

protected override string GetText() =>
Encoding.UTF8.GetString(buffer, 0, length);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using TMPro;
using UnityEngine.TextCore.Text;
using Utility;

namespace ECS.StreamableLoading.Fonts
{
public class FontFamilyAssets
{
private readonly List<TMP_FontAsset> textMeshProAssets;
private readonly List<FontAsset> uiToolkitAssets;

public TMP_FontAsset TextMeshProFont { get; }

public FontAsset UIToolkitFont { get; }

public FontFamilyAssets(TMP_FontAsset textMeshProFont, FontAsset uiToolkitFont, List<TMP_FontAsset> textMeshProAssets, List<FontAsset> uiToolkitAssets)
{
TextMeshProFont = textMeshProFont;
UIToolkitFont = uiToolkitFont;
this.textMeshProAssets = textMeshProAssets;
this.uiToolkitAssets = uiToolkitAssets;
}

public void Destroy() =>
Destroy(textMeshProAssets, uiToolkitAssets);

public static void Destroy(List<TMP_FontAsset> textMeshProAssets, List<FontAsset> uiToolkitAssets)
{
foreach (TMP_FontAsset asset in textMeshProAssets)
{
TMP_ResourceManager.RemoveFontAsset(asset);
UnityObjectUtils.SafeDestroy(asset);
}

foreach (FontAsset asset in uiToolkitAssets)
UnityObjectUtils.SafeDestroy(asset);

textMeshProAssets.Clear();
uiToolkitAssets.Clear();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading