Skip to content

Commit

Permalink
Improving performance by adding multithreading support (KhronosGroup#221
Browse files Browse the repository at this point in the history
)

* Added multithreading support.

* Added multithreading support.

* Fixed spacing.

* fixes from PR

* fixed compile error
  • Loading branch information
Zichuan-Wang authored and Blake Gross committed Sep 21, 2018
1 parent d76615a commit 21f73d6
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 33 deletions.
4 changes: 2 additions & 2 deletions GLTFSerialization/GLTFSerialization/GLTFParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal struct GLBHeader
public uint FileLength { get; set; }
}

public static GLTFRoot ParseJson(Stream stream, long startPosition = 0)
public static void ParseJson(Stream stream, out GLTFRoot gltfRoot, long startPosition = 0)
{
stream.Position = startPosition;
// Check for binary format magic bytes
Expand All @@ -32,7 +32,7 @@ public static GLTFRoot ParseJson(Stream stream, long startPosition = 0)
stream.Position = startPosition;
}

return GLTFRoot.Deserialize(new StreamReader(stream));
gltfRoot = GLTFRoot.Deserialize(new StreamReader(stream));
}

// Moves stream position to binary chunk location
Expand Down
3 changes: 2 additions & 1 deletion GLTFSerialization/GLTFSerializationCLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ static void Main(string[] args)
goto exit;
}

GLTFRoot root = GLTFParser.ParseJson(stream);
GLTFRoot root;
GLTFParser.ParseJson(stream, out root);
ExtTextureTransformExtension ext = (ExtTextureTransformExtension)
root.Materials[1].PbrMetallicRoughness.BaseColorTexture.Extensions["EXT_texture_transform"];
root.Serialize(Console.Out);
Expand Down
11 changes: 7 additions & 4 deletions GLTFSerialization/Tests/GLTFSerializationTests/GLTFLoaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public void LoadGLTFFromStream()
FileStream gltfStream = File.OpenRead(GLTF_PATH);

GLTFRoot.RegisterExtension(new TestExtensionFactory());
GLTFRoot gltfRoot = GLTFParser.ParseJson(gltfStream);
GLTFRoot gltfRoot = null;
GLTFParser.ParseJson(gltfStream, out gltfRoot);
GLTFJsonLoadTestHelper.TestGLTF(gltfRoot);
}

Expand All @@ -33,8 +34,9 @@ public void LoadKHRSpecGlossGLTFFromStream()
{
Assert.IsTrue(File.Exists(GLTF_PBR_SPECGLOSS_PATH));
FileStream gltfStream = File.OpenRead(GLTF_PBR_SPECGLOSS_PATH);

GLTFRoot gltfRoot = GLTFParser.ParseJson(gltfStream);

GLTFRoot gltfRoot;
GLTFParser.ParseJson(gltfStream, out gltfRoot);

Assert.IsNotNull(gltfRoot.ExtensionsUsed);
Assert.IsTrue(gltfRoot.ExtensionsUsed.Contains(KHR_materials_pbrSpecularGlossinessExtensionFactory.EXTENSION_NAME));
Expand All @@ -57,7 +59,8 @@ public void LoadGLBFromStream()
{
Assert.IsTrue(File.Exists(GLB_PATH));
FileStream gltfStream = File.OpenRead(GLB_PATH);
GLTFRoot gltfRoot = GLTFParser.ParseJson(gltfStream);
GLTFRoot gltfRoot;
GLTFParser.ParseJson(gltfStream, out gltfRoot);
GLTFJsonLoadTestHelper.TestGLB(gltfRoot);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void Initialize()
writer.Flush();
stream.Position = 0;

_testRoot = GLTFParser.ParseJson(stream);
GLTFParser.ParseJson(stream, out _testRoot);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ public void MergeNodes()
Assert.IsTrue(File.Exists(GLTF_LANTERN_PATH));

FileStream gltfBoomBoxStream = File.OpenRead(GLTF_BOOMBOX_PATH);
GLTFRoot boomBoxRoot = GLTFParser.ParseJson(gltfBoomBoxStream);
GLTFRoot boomBoxRoot;
GLTFParser.ParseJson(gltfBoomBoxStream, out boomBoxRoot);

FileStream gltfLanternStream = File.OpenRead(GLTF_LANTERN_PATH);
GLTFRoot lanternRoot = GLTFParser.ParseJson(gltfLanternStream);
GLTFRoot lanternRoot;
GLTFParser.ParseJson(gltfLanternStream, out lanternRoot);

GLTFRoot boomBoxCopy = new GLTFRoot(boomBoxRoot);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public async Task LoadGLTFFromStreamUWP()
StorageFile sampleFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(GLTF_PATH));

IRandomAccessStream gltfStream = await sampleFile.OpenAsync(FileAccessMode.Read);
GLTFRoot gltfRoot = GLTFParser.ParseJson(gltfStream.AsStream());
GLTFRoot gltfRoot;
GLTFParser.ParseJson(gltfStream.AsStream(), out gltfRoot);
GLTFJsonLoadTestHelper.TestGLTF(gltfRoot);
}

Expand All @@ -36,7 +37,8 @@ public async Task LoadKHRSpecGlossGLTFFromStreamUWP()


IRandomAccessStream gltfStream = await sampleFile.OpenAsync(FileAccessMode.Read);
GLTFRoot gltfRoot = GLTFParser.ParseJson(gltfStream.AsStreamForRead());
GLTFRoot gltfRoot;
GLTFParser.ParseJson(gltfStream.AsStreamForRead(), out gltfRoot);

Assert.IsNotNull(gltfRoot.ExtensionsUsed);
Assert.IsTrue(gltfRoot.ExtensionsUsed.Contains(KHR_materials_pbrSpecularGlossinessExtensionFactory.EXTENSION_NAME));
Expand Down
10 changes: 6 additions & 4 deletions UnityGLTF/Assets/UnityGLTF/Examples/RootMergeComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ IEnumerator Start()

yield return loader0.LoadStream(Path.GetFileName(asset0Path));
var asset0Stream = loader0.LoadedStream;
var asset0Root = GLTFParser.ParseJson(asset0Stream);
GLTFRoot asset0Root;
GLTFParser.ParseJson(asset0Stream, out asset0Root);

yield return loader1.LoadStream(Path.GetFileName(asset1Path));
var asset1Stream = loader1.LoadedStream;
var asset1Root = GLTFParser.ParseJson(asset1Stream);
GLTFRoot asset1Root;
GLTFParser.ParseJson(asset0Stream, out asset1Root);

string newPath = "../../" + URIHelper.GetDirectoryName(asset0Path);

Expand Down Expand Up @@ -70,8 +72,8 @@ IEnumerator Start()
);

importer.MaximumLod = MaximumLod;

yield return importer.LoadScene(-1, Multithreaded);
importer.isMultithreaded = Multithreaded;
yield return importer.LoadScene(-1);
}
#endif
}
Expand Down
6 changes: 4 additions & 2 deletions UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,14 +336,16 @@ private GameObject CreateGLTFScene(string projectFilePath)
ILoader fileLoader = new FileLoader(Path.GetDirectoryName(projectFilePath));
using (var stream = File.OpenRead(projectFilePath))
{
GLTFRoot gLTFRoot = GLTFParser.ParseJson(stream);
GLTFRoot gLTFRoot;
GLTFParser.ParseJson(stream, out gLTFRoot);
var loader = new GLTFSceneImporter(gLTFRoot, fileLoader, stream);

loader.MaximumLod = _maximumLod;
loader.isMultithreaded = true;

// HACK: Force the coroutine to run synchronously in the editor
var stack = new Stack<IEnumerator>();
stack.Push(loader.LoadScene(isMultithreaded: true));
stack.Push(loader.LoadScene());

while (stack.Count > 0)
{
Expand Down
5 changes: 4 additions & 1 deletion UnityGLTF/Assets/UnityGLTF/Scripts/GLTFComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class GLTFComponent : MonoBehaviour
private bool loadOnStart = true;

public int MaximumLod = 300;
public int Timeout = 8;
public GLTFSceneImporter.ColliderType Collider = GLTFSceneImporter.ColliderType.None;

[SerializeField]
Expand Down Expand Up @@ -69,8 +70,10 @@ public IEnumerator Load()
sceneImporter.SceneParent = gameObject.transform;
sceneImporter.Collider = Collider;
sceneImporter.MaximumLod = MaximumLod;
sceneImporter.Timeout = Timeout;
sceneImporter.isMultithreaded = Multithreaded;
sceneImporter.CustomShaderName = shaderOverride ? shaderOverride.name : null;
yield return sceneImporter.LoadScene(-1, Multithreaded);
yield return sceneImporter.LoadScene(-1);

// Override the shaders on all materials if a shader is provided
if (shaderOverride != null)
Expand Down
80 changes: 68 additions & 12 deletions UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using GLTF;
using GLTF.Schema;
using UnityEngine;
Expand All @@ -13,6 +14,7 @@
using Matrix4x4 = GLTF.Math.Matrix4x4;
using Object = UnityEngine.Object;
using WrapMode = UnityEngine.WrapMode;
using ThreadPriority = System.Threading.ThreadPriority;

#if WINDOWS_UWP
using System.Threading.Tasks;
Expand Down Expand Up @@ -41,6 +43,16 @@ public enum ColliderType
/// </summary>
public int MaximumLod = 300;

/// <summary>
/// Timeout for certain threading operations
/// </summary>
public int Timeout = 8;

/// <summary>
/// Use Multithreading or not
/// </summary>
public bool isMultithreaded = false;

/// <summary>
/// The parent transform for the created GameObject
/// </summary>
Expand Down Expand Up @@ -79,6 +91,7 @@ protected struct GLBStream
protected ILoader _loader;
private bool _isRunning = false;


/// <summary>
/// Creates a GLTFSceneBuilder object which will be able to construct a scene based off a url
/// </summary>
Expand Down Expand Up @@ -121,10 +134,9 @@ public GameObject LastLoadedScene
/// Loads a glTF Scene into the LastLoadedScene field
/// </summary>
/// <param name="sceneIndex">The scene to load, If the index isn't specified, we use the default index in the file. Failing that we load index 0.</param>
/// <param name="isMultithreaded">Whether all components should be loaded on a background thread</param>
/// <param name="onLoadComplete">Callback function for when load is completed</param>
/// <returns></returns>
public IEnumerator LoadScene(int sceneIndex = -1, bool isMultithreaded = false, Action<GameObject> onLoadComplete = null)
public IEnumerator LoadScene(int sceneIndex = -1, Action<GameObject> onLoadComplete = null)
{
try
{
Expand All @@ -142,7 +154,7 @@ public IEnumerator LoadScene(int sceneIndex = -1, bool isMultithreaded = false,
{
yield return LoadJson(_gltfFileName);
}
yield return _LoadScene(sceneIndex, isMultithreaded);
yield return _LoadScene(sceneIndex);

Cleanup();
}
Expand Down Expand Up @@ -291,11 +303,33 @@ protected IEnumerator ConstructImageBuffer(GLTFTexture texture, int textureIndex

private IEnumerator LoadJson(string jsonFilePath)
{
yield return _loader.LoadStream(jsonFilePath);
if (isMultithreaded && _loader.HasSyncLoadMethod)
{
Thread loadThread = new Thread(() => _loader.LoadStreamSync(jsonFilePath));
loadThread.Priority = ThreadPriority.Highest;
loadThread.Start();
yield return new WaitUntil(() => !loadThread.IsAlive);
}
else
{
yield return _loader.LoadStream(jsonFilePath);
}

_gltfStream.Stream = _loader.LoadedStream;
_gltfStream.StartPosition = 0;
_gltfRoot = GLTFParser.ParseJson(_gltfStream.Stream, _gltfStream.StartPosition);

if (isMultithreaded)
{
Thread parseJsonThread = new Thread(() => GLTFParser.ParseJson(_gltfStream.Stream, out _gltfRoot, _gltfStream.StartPosition));
parseJsonThread.Priority = ThreadPriority.Highest;
parseJsonThread.Start();
yield return new WaitUntil(() => !parseJsonThread.IsAlive);
}
else
{
GLTFParser.ParseJson(_gltfStream.Stream, out _gltfRoot, _gltfStream.StartPosition);
yield return null;
}
}

private IEnumerator _LoadNode(int nodeIndex)
Expand Down Expand Up @@ -327,9 +361,8 @@ protected void InitializeAssetCache()
/// Creates a scene based off loaded JSON. Includes loading in binary and image data to construct the meshes required.
/// </summary>
/// <param name="sceneIndex">The bufferIndex of scene in gltf file to load</param>
/// <param name="isMultithreaded">Whether to use a thread to do loading</param>
/// <returns></returns>
protected IEnumerator _LoadScene(int sceneIndex = -1, bool isMultithreaded = false)
protected IEnumerator _LoadScene(int sceneIndex = -1)
{
GLTFScene scene;
InitializeAssetCache(); // asset cache currently needs initialized every time due to cleanup logic
Expand Down Expand Up @@ -482,10 +515,19 @@ protected virtual IEnumerator ConstructUnityTexture(Stream stream, bool markGpuO
throw new Exception("Stream is larger than can be copied into byte array");
}

stream.Read(buffer, 0, (int)stream.Length);
}

yield return null;
if (isMultithreaded)
{
Thread readThread = new Thread(() => stream.Read(buffer, 0, (int)stream.Length));
readThread.Priority = ThreadPriority.Highest;
readThread.Start();
yield return new WaitUntil(() => !readThread.IsAlive);
}
else
{
stream.Read(buffer, 0, (int)stream.Length);
yield return null;
}
}

// NOTE: the second parameter of LoadImage() marks non-readable, but we can't mark it until after we call Apply()
texture.LoadImage(buffer, false);
Expand Down Expand Up @@ -562,7 +604,21 @@ protected virtual IEnumerator ConstructMeshAttributes(MeshPrimitive primitive, i
attributeAccessors[SemanticProperties.INDICES] = indexBuilder;
}

GLTFHelpers.BuildMeshAttributes(ref attributeAccessors);
if (isMultithreaded)
{
Thread buildMeshAttributesThread = new Thread(() => GLTFHelpers.BuildMeshAttributes(ref attributeAccessors));
buildMeshAttributesThread.Priority = ThreadPriority.Highest;
buildMeshAttributesThread.Start();
while (!buildMeshAttributesThread.Join(Timeout))
{
yield return null;
}
}
else
{
GLTFHelpers.BuildMeshAttributes(ref attributeAccessors);
}

TransformAttributes(ref attributeAccessors);
_assetCache.MeshCache[meshID][primitiveIndex].MeshAttributes = attributeAccessors;
}
Expand Down
24 changes: 24 additions & 0 deletions UnityGLTF/Assets/UnityGLTF/Scripts/Loader/FileLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ public class FileLoader : ILoader
private string _rootDirectoryPath;
public Stream LoadedStream { get; private set; }

public bool HasSyncLoadMethod { get; private set; }

public FileLoader(string rootDirectoryPath)
{
_rootDirectoryPath = rootDirectoryPath;
HasSyncLoadMethod = true;
}

public IEnumerator LoadStream(string gltfFilePath)
Expand All @@ -41,5 +44,26 @@ private IEnumerator LoadFileStream(string rootPath, string fileToLoad)
yield return null;
LoadedStream = File.OpenRead(pathToLoad);
}

public void LoadStreamSync(string gltfFilePath)
{
if (gltfFilePath == null)
{
throw new ArgumentNullException("gltfFilePath");
}

LoadFileStreamSync(_rootDirectoryPath, gltfFilePath);
}

private void LoadFileStreamSync(string rootPath, string fileToLoad)
{
string pathToLoad = Path.Combine(rootPath, fileToLoad);
if (!File.Exists(pathToLoad))
{
throw new FileNotFoundException("Buffer file not found", fileToLoad);
}

LoadedStream = File.OpenRead(pathToLoad);
}
}
}
4 changes: 4 additions & 0 deletions UnityGLTF/Assets/UnityGLTF/Scripts/Loader/ILoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public interface ILoader
{
IEnumerator LoadStream(string relativeFilePath);

void LoadStreamSync(string jsonFilePath);

Stream LoadedStream { get; }

bool HasSyncLoadMethod { get; }
}
}
Loading

0 comments on commit 21f73d6

Please sign in to comment.