Skip to content

Commit

Permalink
Yield timeout should be common to all loaders as framerate is an appl… (
Browse files Browse the repository at this point in the history
#379)

* Yield timeout should be common to all loaders as framerate is an application-wide concern

* Resolve comments

* .

* .
  • Loading branch information
botrif authored and Blake Gross committed Mar 22, 2019
1 parent b1ded04 commit 67eb55e
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 59 deletions.
51 changes: 41 additions & 10 deletions UnityGLTF/Assets/UnityGLTF/Scripts/Async/AsyncCoroutineHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
using UnityEngine;
namespace UnityGLTF
{
public interface IAsyncCoroutineHelper
{
Task RunAsTask(IEnumerator coroutine, string name);
}
public interface IAsyncCoroutineHelper
{
Task RunAsTask(IEnumerator coroutine, string name);
Task YieldOnTimeout();
}

public class AsyncCoroutineHelper : MonoBehaviour, IAsyncCoroutineHelper
{
{
public float BudgetPerFrameInSeconds = 0.01f;

private Queue<CoroutineInfo> _actions = new Queue<CoroutineInfo>();
private WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
private float _timeout;

public Task RunAsTask(IEnumerator coroutine, string name)
{
Expand All @@ -31,15 +36,24 @@ public Task RunAsTask(IEnumerator coroutine, string name)
return tcs.Task;
}

private IEnumerator CallMethodOnMainThread(CoroutineInfo coroutineInfo)
public async Task YieldOnTimeout()
{
yield return coroutineInfo.Coroutine;
coroutineInfo.Tcs.SetResult(true);
if (Time.realtimeSinceStartup > _timeout)
{
await RunAsTask(EmptyYieldEnum(), nameof(EmptyYieldEnum));
}
}

private void Start()
{
_timeout = Time.realtimeSinceStartup + BudgetPerFrameInSeconds;
}

private void Update()
private void Update()
{
CoroutineInfo? coroutineInfo = null;
StartCoroutine(ResetFrameTimeout());

CoroutineInfo ? coroutineInfo = null;

lock (_actions)
{
Expand All @@ -55,6 +69,23 @@ private void Update()
}
}

private IEnumerator CallMethodOnMainThread(CoroutineInfo coroutineInfo)
{
yield return coroutineInfo.Coroutine;
coroutineInfo.Tcs.SetResult(true);
}

private IEnumerator EmptyYieldEnum()
{
yield break;
}

private IEnumerator ResetFrameTimeout()
{
yield return _waitForEndOfFrame;
_timeout = Time.realtimeSinceStartup + BudgetPerFrameInSeconds;
}

private struct CoroutineInfo
{
public IEnumerator Coroutine;
Expand Down
1 change: 0 additions & 1 deletion UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,6 @@ private GameObject CreateGLTFScene(string projectFilePath)
GLTFRoot gLTFRoot;
GLTFParser.ParseJson(stream, out gLTFRoot);
var loader = new GLTFSceneImporter(gLTFRoot, fileLoader, null, stream);
loader.BudgetPerFrameInMilliseconds = float.MaxValue;
loader.MaximumLod = _maximumLod;
loader.IsMultithreaded = true;

Expand Down
75 changes: 27 additions & 48 deletions UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,6 @@ public bool IsMultithreaded
/// </summary>
public string CustomShaderName { get; set; }


public float BudgetPerFrameInMilliseconds = 10f;

/// <summary>
/// Whether to keep a CPU-side copy of the mesh after upload to GPU (for example, in case normals/tangents need recalculation)
/// </summary>
Expand Down Expand Up @@ -139,8 +136,7 @@ protected struct GLBStream
public long StartPosition;
}

protected float _timeAtLastYield = 0f;
protected AsyncCoroutineHelper _asyncCoroutineHelper;
protected IAsyncCoroutineHelper _asyncCoroutineHelper;

protected GameObject _lastLoadedScene;
protected readonly GLTFMaterial DefaultMaterial = new GLTFMaterial();
Expand Down Expand Up @@ -169,7 +165,10 @@ public GLTFSceneImporter(GLTFRoot rootNode, ILoader externalDataLoader, AsyncCor
{
_gltfRoot = rootNode;
_loader = externalDataLoader;
if (gltfStream != null) _gltfStream = new GLBStream {Stream = gltfStream, StartPosition = gltfStream.Position};
if (gltfStream != null)
{
_gltfStream = new GLBStream {Stream = gltfStream, StartPosition = gltfStream.Position};
}
}

private GLTFSceneImporter(ILoader externalDataLoader, AsyncCoroutineHelper asyncCoroutineHelper)
Expand Down Expand Up @@ -212,7 +211,6 @@ public async Task LoadSceneAsync(int sceneIndex = -1, bool showSceneObj = true,
_isRunning = true;
}

_timeAtLastYield = Time.realtimeSinceStartup;
if (_gltfRoot == null)
{
await LoadJson(_gltfFileName);
Expand Down Expand Up @@ -275,7 +273,6 @@ public async Task LoadNodeAsync(int nodeIndex)
_assetCache = new AssetCache(_gltfRoot);
}

_timeAtLastYield = Time.realtimeSinceStartup;
await _LoadNode(nodeIndex);
CreatedObject = _assetCache.NodeCache[nodeIndex];
InitializeGltfTopLevelObject();
Expand Down Expand Up @@ -323,7 +320,6 @@ public virtual async Task<Material> LoadMaterialAsync(int materialIndex)
_assetCache = new AssetCache(_gltfRoot);
}

_timeAtLastYield = Time.realtimeSinceStartup;
if (_assetCache.MaterialCache[materialIndex] == null)
{
var def = _gltfRoot.Materials[materialIndex];
Expand Down Expand Up @@ -454,11 +450,6 @@ protected IEnumerator WaitUntilEnum(WaitUntil waitUntil)
yield return waitUntil;
}

protected IEnumerator EmptyYieldEnum()
{
yield break;
}

private async Task LoadJson(string jsonFilePath)
{
#if !WINDOWS_UWP
Expand Down Expand Up @@ -631,7 +622,7 @@ protected async Task ConstructImage(GLTFImage image, int imageCacheIndex, bool m
}
}

await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
await ConstructUnityTexture(stream, markGpuOnly, isLinear, image, imageCacheIndex);
}
}
Expand Down Expand Up @@ -661,7 +652,7 @@ protected virtual async Task ConstructUnityTexture(Stream stream, bool markGpuOn
stream.Read(buffer, 0, (int)stream.Length);
}

await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
// NOTE: the second parameter of LoadImage() marks non-readable, but we can't mark it until after we call Apply()
texture.LoadImage(buffer, markGpuOnly);
}
Expand Down Expand Up @@ -1393,17 +1384,6 @@ protected virtual async Task ConstructMeshPrimitive(MeshPrimitive primitive, int
}
}

protected async Task TryYieldOnTimeout()
{
if ((Time.realtimeSinceStartup - _timeAtLastYield) > BudgetPerFrameInMilliseconds * 1000f)
{
_timeAtLastYield = Time.realtimeSinceStartup;

// empty yield
await _asyncCoroutineHelper.RunAsTask(EmptyYieldEnum(), nameof(EmptyYieldEnum));
}
}

protected UnityMeshData ConvertAccessorsToUnityTypes(MeshConstructionData meshConstructionData)
{
// todo optimize: There are multiple copies being performed to turn the buffer data into mesh data. Look into reducing them
Expand Down Expand Up @@ -1538,7 +1518,7 @@ protected async Task ConstructUnityMesh(MeshConstructionData meshConstructionDat
int vertexCount = (int)primitive.Attributes[SemanticProperties.POSITION].Value.Count;
bool hasNormals = unityMeshData.Normals != null;

await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
Mesh mesh = new Mesh
{

Expand All @@ -1548,25 +1528,25 @@ protected async Task ConstructUnityMesh(MeshConstructionData meshConstructionDat
};

mesh.vertices = unityMeshData.Vertices;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.normals = unityMeshData.Normals;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.uv = unityMeshData.Uv1;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.uv2 = unityMeshData.Uv2;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.uv3 = unityMeshData.Uv3;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.uv4 = unityMeshData.Uv4;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.colors = unityMeshData.Colors;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.triangles = unityMeshData.Triangles;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.tangents = unityMeshData.Tangents;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();
mesh.boneWeights = unityMeshData.BoneWeights;
await TryYieldOnTimeout();
if (_asyncCoroutineHelper != null) await _asyncCoroutineHelper.YieldOnTimeout();

if (!hasNormals)
{
Expand Down Expand Up @@ -1783,15 +1763,15 @@ protected virtual int GetTextureSourceId(GLTFTexture texture)
return texture.Source.Id;
}

/// <summary>
/// Creates a texture from a glTF texture
/// </summary>
/// <param name="texture">The texture to load</param>
/// <param name="textureIndex">The index in the texture cache</param>
/// <param name="markGpuOnly">Whether the texture is GPU only, instead of keeping a CPU copy</param>
/// <param name="isLinear">Whether the texture is linear rather than sRGB</param>
/// <returns>The loading task</returns>
public virtual async Task LoadTextureAsync(GLTFTexture texture, int textureIndex, bool markGpuOnly, bool isLinear)
/// <summary>
/// Creates a texture from a glTF texture
/// </summary>
/// <param name="texture">The texture to load</param>
/// <param name="textureIndex">The index in the texture cache</param>
/// <param name="markGpuOnly">Whether the texture is GPU only, instead of keeping a CPU copy</param>
/// <param name="isLinear">Whether the texture is linear rather than sRGB</param>
/// <returns>The loading task</returns>
public virtual async Task LoadTextureAsync(GLTFTexture texture, int textureIndex, bool markGpuOnly, bool isLinear)
{
try
{
Expand All @@ -1815,7 +1795,6 @@ public virtual async Task LoadTextureAsync(GLTFTexture texture, int textureIndex
_assetCache = new AssetCache(_gltfRoot);
}

_timeAtLastYield = Time.realtimeSinceStartup;
await ConstructImageBuffer(texture, textureIndex);
await ConstructTexture(texture, textureIndex, markGpuOnly, isLinear);
}
Expand Down

0 comments on commit 67eb55e

Please sign in to comment.