Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yield timeout should be common to all loaders as framerate is an appl… #379

Merged
merged 4 commits into from
Mar 22, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
52 changes: 41 additions & 11 deletions UnityGLTF/Assets/UnityGLTF/Scripts/Async/AsyncCoroutineHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@
using UnityEngine;
namespace UnityGLTF
{
public interface IAsyncCoroutineHelper
{
Task RunAsTask(IEnumerator coroutine, string name);
}
public interface IAsyncCoroutineHelper
{
Task RunAsTask(IEnumerator coroutine, string name);
}

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)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
var tcs = new TaskCompletionSource<bool>();
botrif marked this conversation as resolved.
Show resolved Hide resolved
lock (_actions)
{
_actions.Enqueue(
Expand All @@ -31,15 +35,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 +68,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
73 changes: 26 additions & 47 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,7 +136,6 @@ protected struct GLBStream
public long StartPosition;
}

protected float _timeAtLastYield = 0f;
protected AsyncCoroutineHelper _asyncCoroutineHelper;
botrif marked this conversation as resolved.
Show resolved Hide resolved

protected GameObject _lastLoadedScene;
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();
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();
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();
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();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.normals = unityMeshData.Normals;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.uv = unityMeshData.Uv1;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.uv2 = unityMeshData.Uv2;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.uv3 = unityMeshData.Uv3;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.uv4 = unityMeshData.Uv4;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.colors = unityMeshData.Colors;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.triangles = unityMeshData.Triangles;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.tangents = unityMeshData.Tangents;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();
mesh.boneWeights = unityMeshData.BoneWeights;
await TryYieldOnTimeout();
await _asyncCoroutineHelper.YieldOnTimeout();

if (!hasNormals)
{
Expand Down Expand Up @@ -1726,15 +1706,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 @@ -1758,7 +1738,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