Merge pull request #451 from chromee/load-balance-build-mesh

BuildMesh load balancing in ImporterContext
This commit is contained in:
hiroj 2020-07-02 13:52:25 +09:00 committed by GitHub
commit 12715778f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 202 additions and 39 deletions

View File

@ -75,10 +75,18 @@ namespace DepthFirstScheduler
#region CoroutineFunctor
public class CoroutineFunctor<T> : IFunctor<T>
{
T m_result;
public T GetResult()
{
return m_result;
if (m_last?.Current == null) return default;
try
{
return (T)m_last.Current;
}
catch
{
return default;
}
}
Exception m_error;
@ -87,12 +95,11 @@ namespace DepthFirstScheduler
return m_error;
}
Func<T> m_arg;
Func<T, IEnumerator> m_starter;
Func<IEnumerator> m_starter;
Stack<IEnumerator> m_it;
public CoroutineFunctor(Func<T> arg, Func<T, IEnumerator> starter)
private IEnumerator m_last;
public CoroutineFunctor(Func<IEnumerator> starter)
{
m_arg = arg;
m_starter = starter;
}
@ -100,9 +107,8 @@ namespace DepthFirstScheduler
{
if (m_it == null)
{
m_result = m_arg();
m_it = new Stack<IEnumerator>();
m_it.Push(m_starter(m_result));
m_it.Push(m_starter());
}
try
@ -119,7 +125,7 @@ namespace DepthFirstScheduler
}
else
{
m_it.Pop();
m_last = m_it.Pop();
}
return ExecutionStatus.Continue;
}
@ -139,9 +145,11 @@ namespace DepthFirstScheduler
public static class CoroutineFunctor
{
public static CoroutineFunctor<T> Create<T>(Func<T> arg, Func<T, IEnumerator> starter)
/// <typeparam name="S">引数の型</typeparam>
/// <typeparam name="T">結果の型</typeparam>
public static CoroutineFunctor<T> Create<S, T>(Func<S> arg, Func<S, IEnumerator> starter)
{
return new CoroutineFunctor<T>(arg, starter);
return new CoroutineFunctor<T>(() => starter(arg()));
}
}
#endregion

View File

@ -137,10 +137,10 @@ namespace DepthFirstScheduler
return schedulable;
}
public Schedulable<T> AddCoroutine(IScheduler scheduler, Func<IEnumerator> starter)
public Schedulable<Unit> AddCoroutine(IScheduler scheduler, Func<IEnumerator> starter)
{
var func = CoroutineFunctor.Create(() => default(T), _ => starter());
var schedulable = new Schedulable<T>(scheduler, func);
var func = CoroutineFunctor.Create<Unit, Unit>(() => default(Unit), _ => starter());
var schedulable = new Schedulable<Unit>(scheduler, func);
AddChild(schedulable);
return schedulable;
}
@ -168,15 +168,26 @@ namespace DepthFirstScheduler
return schedulable;
}
public Schedulable<T> ContinueWithCoroutine(IScheduler scheduler, Func<IEnumerator> starter)
public Schedulable<Unit> ContinueWithCoroutine(IScheduler scheduler, Func<IEnumerator> starter)
{
return ContinueWithCoroutine<Unit>(scheduler, _ => starter());
}
public Schedulable<U> ContinueWithCoroutine<U>(IScheduler scheduler, Func<T, IEnumerator> starter)
{
if (Parent == null)
{
throw new NoParentException();
}
var func = CoroutineFunctor.Create(() => default(T), _ => starter());
var schedulable = new Schedulable<T>(scheduler, func);
Func<T> getResult = null;
if (Func != null)
{
getResult = Func.GetResult;
}
var func = CoroutineFunctor.Create<T, U>(getResult, starter);
var schedulable = new Schedulable<U>(scheduler, func);
Parent.AddChild(schedulable);
return schedulable;
}

View File

@ -372,6 +372,9 @@ namespace UniGLTF
#endregion
#region Load. Build unity objects
public bool EnableLoadBalancing;
/// <summary>
/// ReadAllBytes, Parse, Create GameObject
/// </summary>
@ -560,28 +563,7 @@ namespace UniGLTF
return meshImporter.ReadMesh(this, index);
}
})
.ContinueWith(Scheduler.MainThread, x =>
{
using (MeasureTime("BuildMesh"))
{
var meshWithMaterials = MeshImporter.BuildMesh(this, x);
var mesh = meshWithMaterials.Mesh;
// mesh name
if (string.IsNullOrEmpty(mesh.name))
{
mesh.name = string.Format("UniGLTF import#{0}", i);
}
var originalName = mesh.name;
for (int j = 1; Meshes.Any(y => y.Mesh.name == mesh.name); ++j)
{
mesh.name = string.Format("{0}({1})", originalName, j);
}
return meshWithMaterials;
}
})
.ContinueWithCoroutine<MeshWithMaterials>(Scheduler.MainThread, x => BuildMesh(x, index))
.ContinueWith(Scheduler.ThreadPool, x => Meshes.Add(x))
;
}
@ -654,6 +636,39 @@ namespace UniGLTF
}
yield return null;
}
IEnumerator BuildMesh(MeshImporter.MeshContext x, int i)
{
using (MeasureTime("BuildMesh"))
{
MeshWithMaterials meshWithMaterials;
if (EnableLoadBalancing)
{
var buildMesh = MeshImporter.BuildMeshCoroutine(this, x);
yield return buildMesh;
meshWithMaterials = buildMesh.Current as MeshWithMaterials;
}
else
{
meshWithMaterials = MeshImporter.BuildMesh(this, x);
}
var mesh = meshWithMaterials.Mesh;
// mesh name
if (string.IsNullOrEmpty(mesh.name))
{
mesh.name = string.Format("UniGLTF import#{0}", i);
}
var originalName = mesh.name;
for (int j = 1; Meshes.Any(y => y.Mesh.name == mesh.name); ++j)
{
mesh.name = string.Format("{0}({1})", originalName, j);
}
yield return meshWithMaterials;
}
}
IEnumerator LoadMeshes()
{

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
@ -656,6 +657,134 @@ namespace UniGLTF
return result;
}
public static IEnumerator BuildMeshCoroutine(ImporterContext ctx, MeshImporter.MeshContext meshContext)
{
if (!meshContext.materialIndices.Any())
{
meshContext.materialIndices.Add(0);
}
var mesh = new Mesh();
mesh.name = meshContext.name;
if (meshContext.positions.Length > UInt16.MaxValue)
{
#if UNITY_2017_3_OR_NEWER
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#else
Debug.LogWarningFormat("vertices {0} exceed 65535. not implemented. Unity2017.3 supports large mesh",
meshContext.positions.Length);
#endif
}
mesh.vertices = meshContext.positions;
bool recalculateNormals = false;
if (meshContext.normals != null && meshContext.normals.Length > 0)
{
mesh.normals = meshContext.normals;
}
else
{
recalculateNormals = true;
}
if (meshContext.uv != null && meshContext.uv.Length > 0)
{
mesh.uv = meshContext.uv;
}
bool recalculateTangents = true;
#if UNIGLTF_IMPORT_TANGENTS
if (meshContext.tangents != null && meshContext.tangents.Length > 0)
{
mesh.tangents = meshContext.tangents;
recalculateTangents = false;
}
#endif
if (meshContext.colors != null && meshContext.colors.Length > 0)
{
mesh.colors = meshContext.colors;
}
if (meshContext.boneWeights != null && meshContext.boneWeights.Count > 0)
{
mesh.boneWeights = meshContext.boneWeights.ToArray();
}
mesh.subMeshCount = meshContext.subMeshes.Count;
for (int i = 0; i < meshContext.subMeshes.Count; ++i)
{
mesh.SetTriangles(meshContext.subMeshes[i], i);
}
if (recalculateNormals)
{
mesh.RecalculateNormals();
}
if (recalculateTangents)
{
#if UNITY_5_6_OR_NEWER
yield return null;
mesh.RecalculateTangents();
yield return null;
#else
CalcTangents(mesh);
#endif
}
var result = new MeshWithMaterials
{
Mesh = mesh,
Materials = meshContext.materialIndices.Select(x => ctx.GetMaterial(x)).ToArray()
};
yield return null;
if (meshContext.blendShapes != null)
{
Vector3[] emptyVertices = null;
foreach (var blendShape in meshContext.blendShapes)
{
if (blendShape.Positions.Count > 0)
{
if (blendShape.Positions.Count == mesh.vertexCount)
{
mesh.AddBlendShapeFrame(blendShape.Name, FRAME_WEIGHT,
blendShape.Positions.ToArray(),
(meshContext.normals != null && meshContext.normals.Length == mesh.vertexCount && blendShape.Normals.Count() == blendShape.Positions.Count()) ? blendShape.Normals.ToArray() : null,
null
);
yield return null;
}
else
{
Debug.LogWarningFormat("May be partial primitive has blendShape. Require separate mesh or extend blend shape, but not implemented: {0}", blendShape.Name);
}
}
else
{
if (emptyVertices == null)
{
emptyVertices = new Vector3[mesh.vertexCount];
}
// Debug.LogFormat("empty blendshape: {0}.{1}", mesh.name, blendShape.Name);
// add empty blend shape for keep blend shape index
mesh.AddBlendShapeFrame(blendShape.Name, FRAME_WEIGHT,
emptyVertices,
null,
null
);
yield return null;
}
}
}
yield return result;
}
/// <summary>
/// Meshの法線を元にタンジェントを計算する。