Skip to content

Commit

Permalink
Merge pull request #1178 from anatawa12/index-buffer-optimization-wit…
Browse files Browse the repository at this point in the history
…h-base-vertex

feat: optimize index buffer with baseVertex
  • Loading branch information
anatawa12 authored Sep 5, 2024
2 parents d65e588 + 2e0da12 commit 092edb0
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 26 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG-PRERELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ The format is based on [Keep a Changelog].
- Skip Enablement Mismatched Renderers is now disabled by default `#1169`
- You still can enable it in the Inspector.
- This change does not affect the behavior of previously added components.
- Use UInt16 index buffer if possible even when total vertex count is more than 2^16 `#1178`
- With baseVertex in index buffer, we can use UInt16 index buffer even if total vertex count is more than 2^16.
- Of course, if one submeh references wide range of vertices, we cannot use UInt16 index buffer so we still use UInt32 index buffer in such a case.

### Deprecated

Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ The format is based on [Keep a Changelog].
- Skip Enablement Mismatched Renderers is now disabled by default `#1169`
- You still can enable it in the Inspector.
- This change does not affect the behavior of previously added components.
- Use UInt16 index buffer if possible even when total vertex count is more than 2^16 `#1178`
- With baseVertex in index buffer, we can use UInt16 index buffer even if total vertex count is more than 2^16.
- Of course, if one submeh references wide range of vertices, we cannot use UInt16 index buffer so we still use UInt32 index buffer in such a case.

### Deprecated

Expand Down
80 changes: 54 additions & 26 deletions Internal/MeshInfo2/MeshInfo2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -572,50 +572,78 @@ public void WriteToMesh(Mesh destMesh)
for (var i = 0; i < Vertices.Count; i++)
vertexIndices.Add(Vertices[i], i);

var maxIndices = 0;
var totalSubMeshes = 0;
var submeshWithMoreThan65536Verts = false;
var submeshIndexBuffersList = new List<(SubMesh subMesh, int baseVertex, int[] indices)>();

for (var i = 0; i < SubMeshes.Count - 1; i++)
{
maxIndices = Mathf.Max(maxIndices, SubMeshes[i].Vertices.Count);
var subMesh = SubMeshes[i];

// for non-last submesh, we have to duplicate submesh for multi pass rendering
for (var j = 0; j < SubMeshes[i].SharedMaterials.Length; j++)
totalSubMeshes++;
AddSubmesh(subMesh, vertexIndices, submeshIndexBuffersList, ref submeshWithMoreThan65536Verts, subMesh.SharedMaterials.Length);
}

{
maxIndices = Mathf.Max(maxIndices, SubMeshes[SubMeshes.Count - 1].Vertices.Count);
// for last submesh, we can use single submesh for multi pass reendering
totalSubMeshes++;
var subMesh = SubMeshes[^1];
// for last submesh, we can use single submesh for multi pass rendering
AddSubmesh(subMesh, vertexIndices, submeshIndexBuffersList, ref submeshWithMoreThan65536Verts, 1);
}

var indices = new int[maxIndices];
var submeshIndex = 0;

destMesh.indexFormat = Vertices.Count <= ushort.MaxValue ? IndexFormat.UInt16 : IndexFormat.UInt32;
destMesh.subMeshCount = totalSubMeshes;

for (var i = 0; i < SubMeshes.Count - 1; i++)
static void AddSubmesh(SubMesh subMesh, Dictionary<Vertex, int> vertexIndices, List<(SubMesh subMesh, int baseVertex, int[] indices)> submeshIndexBuffers, ref bool submeshWithMoreThan65536Verts, int count)
{
var subMesh = SubMeshes[i];

var indices = new int[subMesh.Vertices.Count];
for (var index = 0; index < subMesh.Vertices.Count; index++)
indices[index] = vertexIndices[subMesh.Vertices[index]];

// general case: for non-last submesh, we have to duplicate submesh for multi pass rendering
for (var j = 0; j < subMesh.SharedMaterials.Length; j++)
destMesh.SetIndices(indices, 0, subMesh.Vertices.Count, subMesh.Topology, submeshIndex++);
var min = indices.Min();
var max = indices.Max();
submeshWithMoreThan65536Verts |= max - min >= ushort.MaxValue;

for (var j = 0; j < count; j++)
submeshIndexBuffers.Add((subMesh, 0, indices));
}

var submeshIndexBuffers = submeshIndexBuffersList.ToArray();

// determine index format
// if all vertices has less than 65536 vertices, we can use UInt16
// if all vertices has more than 65536 vertices but each submesh has less than 65536 vertices, we can use UInt16 with vaseVertex
// otherwise, we have to use UInt32
//
// Please note currently there is no optimization for index buffer to apply this optimization perfectly.
// You may need to reorder meshes in Merge Skinned Mesh. I will implement this optimization in future if I can.

if (Vertices.Count <= ushort.MaxValue)
{
var subMesh = SubMeshes[SubMeshes.Count - 1];
destMesh.indexFormat = IndexFormat.UInt16;
}
else if (!submeshWithMoreThan65536Verts)
{
destMesh.indexFormat = IndexFormat.UInt16;
foreach (ref var submeshIndexBuffer in submeshIndexBuffers.AsSpan())
{
submeshIndexBuffer.baseVertex = submeshIndexBuffer.indices.Min();
for (var i = 0; i < submeshIndexBuffer.indices.Length; i++)
submeshIndexBuffer.indices[i] -= submeshIndexBuffer.baseVertex;
}
}
else
{
destMesh.indexFormat = IndexFormat.UInt32;
}

for (var index = 0; index < subMesh.Vertices.Count; index++)
indices[index] = vertexIndices[subMesh.Vertices[index]];
var submeshIndex = 0;

destMesh.subMeshCount = submeshIndexBuffers.Length;

// for last submesh, we can use single submesh for multi pass reendering
destMesh.SetIndices(indices, 0, subMesh.Vertices.Count, subMesh.Topology, submeshIndex++);
for (var i = 0; i < submeshIndexBuffers.Length; i++)
{
var (subMesh, baseVertex, indices) = submeshIndexBuffers[i];
destMesh.SetIndices(indices, 0, subMesh.Vertices.Count, subMesh.Topology, i,
baseVertex: baseVertex);
}

Debug.Assert(totalSubMeshes == submeshIndex);
Debug.Assert(submeshIndexBuffers.Length == submeshIndex);
}
Profiler.EndSample();

Expand Down

0 comments on commit 092edb0

Please sign in to comment.