Skip to content

Commit faed95d

Browse files
committed
Decode large glTF-binary JSON chunks off the main thread
ReadToEndAsync completes synchronously on a memory stream, stalling the main thread ~100 ms on a 12.6 MB JSON chunk. Gate the decode through the defer agent like the parse already is.
1 parent 4fe3391 commit faed95d

2 files changed

Lines changed: 35 additions & 3 deletions

File tree

Packages/com.unity.cloud.gltfast/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
### Fixed
10+
- Main thread stall when loading glTF-binary files with large JSON chunks: the JSON string decode now runs on a background thread (gated by the defer agent, like the JSON parse), instead of always executing synchronously on the calling thread.
11+
12+
## [6.19.0] - 2026-05-19
13+
914
### Added
1015
- (Add-Ons) Import glTF animations to custom animation systems.
1116
- [IAnimationProcessor](xref:GLTFast.Animations.IAnimationProcessor) — animation clips conversion

Packages/com.unity.cloud.gltfast/Runtime/Scripts/GltfImport.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2075,11 +2075,38 @@ CancellationToken cancellationToken
20752075
{
20762076
Assert.IsNull(Root);
20772077

2078-
Profiler.BeginSample("GetJSON");
20792078
var bytesStream = bytes.ToUnmanagedMemoryStream((uint)index, chLength);
20802079
var reader = new StreamReader(bytesStream);
2081-
var json = await reader.ReadToEndAsync();
2082-
Profiler.EndSample();
2080+
string json = null;
2081+
var predictedTime = chLength / (float)k_JsonParseSpeed;
2082+
#if GLTFAST_THREADS && !MEASURE_TIMINGS
2083+
if (DeferAgent.ShouldDefer(predictedTime))
2084+
{
2085+
// ReadToEndAsync completes synchronously on a memory stream
2086+
// => decode in a thread.
2087+
try
2088+
{
2089+
json = await Task.Run(() =>
2090+
{
2091+
Profiler.BeginSample("GetJSON");
2092+
var result = reader.ReadToEnd();
2093+
Profiler.EndSample();
2094+
return result;
2095+
}, cancellationToken);
2096+
}
2097+
catch (OperationCanceledException)
2098+
{
2099+
cancellationToken.ThrowIfCancellationRequestedWithTracking();
2100+
}
2101+
}
2102+
else
2103+
#endif
2104+
{
2105+
// Chunk is small enough to decode within the frame budget.
2106+
Profiler.BeginSample("GetJSON");
2107+
json = await reader.ReadToEndAsync();
2108+
Profiler.EndSample();
2109+
}
20832110

20842111
var success = await ParseJsonAndLoadBuffers(json, cancellationToken);
20852112

0 commit comments

Comments
 (0)