要在曲面细分大型网格时提高性能,请使用作业系统。这样可以防止主线程被长时间阻塞。创建曲面细分作业时,请使用 MeshGenerationContext.AddMeshGenerationJob API 为__ UI__(即用户界面,User Interface)让用户能够与您的应用程序进行交互。Unity 目前支持三种 UI 系统。更多信息
See in Glossary 工具包提供作业句柄。这可确保 UI 工具包在访问网格之前等待作业完成。
如果能够快速计算顶点或索引的数量,请使用主线程分配网格并将其添加到绘制序列中。您可以使用以下 API 执行此操作:
注意:可以在调用 generateVisualContent 期间的任何时间将网格添加到绘制序列中。
以下示例演示了在主线程上分配的作业化曲面细分,其中作业执行实际曲面细分。
using System;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.UIElements;
class CheckerboardElement : VisualElement
{
struct CheckerboardJob : IJob
{
[WriteOnly] public NativeSlice<Vertex> vertices;
[WriteOnly] public NativeSlice<ushort> indices;
public int horizontalCount;
public int verticalCount;
public float divisions;
public Color32 color1;
public Color32 color2;
public void Execute()
{
Span<Color32> colors = stackalloc Color32[2];
colors[0] = color1;
colors[1] = color2;
int colorIndex = 0;
int vCount = 0;
int iCount = 0;
float left = 0;
float right = divisions;
for (int i = 0; i < horizontalCount; ++i)
{
float top = 0;
float bottom = divisions;
for (int j = 0; j < verticalCount; ++j)
{
colorIndex = (i ^ j) & 1;
Color32 color = colors[colorIndex];
vertices[vCount + 0] = new Vertex { position = new Vector3(left, bottom), tint = color };
vertices[vCount + 1] = new Vertex { position = new Vector3(left, top), tint = color };
vertices[vCount + 2] = new Vertex { position = new Vector3(right, top), tint = color };
vertices[vCount + 3] = new Vertex { position = new Vector3(right, bottom), tint = color };
indices[iCount + 0] = (ushort)(vCount + 0);
indices[iCount + 1] = (ushort)(vCount + 1);
indices[iCount + 2] = (ushort)(vCount + 2);
indices[iCount + 3] = (ushort)(vCount + 2);
indices[iCount + 4] = (ushort)(vCount + 3);
indices[iCount + 5] = (ushort)(vCount + 0);
vCount += 4;
iCount += 6;
top += divisions;
bottom += divisions;
}
left += divisions;
right += divisions;
}
}
}
int m_Divisions;
Color32 m_Color1;
Color32 m_Color2;
public CheckerboardElement(int divisions, Color32 color1, Color32 color2)
{
generateVisualContent = OnGenerateVisualContent;
m_Divisions = divisions;
m_Color1 = color1;
m_Color2 = color2;
}
void OnGenerateVisualContent(MeshGenerationContext mgc)
{
int horizontalCount = Mathf.FloorToInt(layout.width / m_Divisions);
int verticalCount = Mathf.FloorToInt(layout.height / m_Divisions);
if (horizontalCount == 0 || verticalCount == 0)
return;
var job = new CheckerboardJob
{
horizontalCount = horizontalCount,
verticalCount = verticalCount,
divisions = m_Divisions,
color1 = m_Color1,
color2 = m_Color2
};
int quads = horizontalCount * verticalCount;
mgc.AllocateTempMesh(quads * 4, quads * 6, out job.vertices, out job.indices);
mgc.DrawMesh(job.vertices, job.indices);
JobHandle jobHandle = job.Schedule();
mgc.AddMeshGenerationJob(jobHandle);
}
}
如果计算顶点或索引的数量时计算开销很高,请将 MeshGenerationNode 插入到绘制序列中。然后将节点以及 TempMeshAllocator 提供给作业。您可以使用以下 API 执行此操作:
在此方法中,从作业中执行分配。使用 TempMeshAllocator.AllocateTempMesh 可从作业中安全分配临时网格。MeshGenerationNode.DrawMesh 充当由作业填充的嵌套绘制序列的容器。
基于前面的示例,以下演示了如何在作业中进行分配和曲面细分:
using System;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.UIElements;
class CheckerboardElement_AllocFromJob : VisualElement
{
struct CheckerboardJob : IJob
{
[ReadOnly]
public TempMeshAllocator allocator;
public MeshGenerationNode node;
public int horizontalCount;
public int verticalCount;
public float divisions;
public Color32 color1;
public Color32 color2;
public void Execute()
{
// Let's pretend that the number of vertices/indices is difficult to compute...
int quads = horizontalCount * verticalCount;
int totalVertexCount = quads * 4;
int totalIndexCount = quads * 6;
// Allocate the mesh from the job
allocator.AllocateTempMesh(totalVertexCount, totalIndexCount, out NativeSlice<Vertex> vertices, out NativeSlice<ushort> indices);
Span<Color32> colors = stackalloc Color32[2];
colors[0] = color1;
colors[1] = color2;
int colorIndex = 0;
int vCount = 0;
int iCount = 0;
float left = 0;
float right = divisions;
for (int i = 0; i < horizontalCount; ++i)
{
float top = 0;
float bottom = divisions;
for (int j = 0; j < verticalCount; ++j)
{
colorIndex = (i ^ j) & 1;
Color32 color = colors[colorIndex];
vertices[vCount + 0] = new Vertex { position = new Vector3(left, bottom), tint = color };
vertices[vCount + 1] = new Vertex { position = new Vector3(left, top), tint = color };
vertices[vCount + 2] = new Vertex { position = new Vector3(right, top), tint = color };
vertices[vCount + 3] = new Vertex { position = new Vector3(right, bottom), tint = color };
indices[iCount + 0] = (ushort)(vCount + 0);
indices[iCount + 1] = (ushort)(vCount + 1);
indices[iCount + 2] = (ushort)(vCount + 2);
indices[iCount + 3] = (ushort)(vCount + 2);
indices[iCount + 4] = (ushort)(vCount + 3);
indices[iCount + 5] = (ushort)(vCount + 0);
vCount += 4;
iCount += 6;
top += divisions;
bottom += divisions;
}
left += divisions;
right += divisions;
}
// Add the mesh to the draw sequence of the node
node.DrawMesh(vertices, indices);
}
}
int m_Divisions;
Color32 m_Color1;
Color32 m_Color2;
public CheckerboardElement_AllocFromJob(int divisions, Color32 color1, Color32 color2)
{
generateVisualContent = OnGenerateVisualContent;
m_Divisions = divisions;
m_Color1 = color1;
m_Color2 = color2;
}
void OnGenerateVisualContent(MeshGenerationContext mgc)
{
int horizontalCount = Mathf.FloorToInt(layout.width / m_Divisions);
int verticalCount = Mathf.FloorToInt(layout.height / m_Divisions);
if (horizontalCount == 0 || verticalCount == 0)
return;
var job = new CheckerboardJob
{
horizontalCount = horizontalCount,
verticalCount = verticalCount,
divisions = m_Divisions,
color1 = m_Color1,
color2 = m_Color2
};
mgc.GetTempMeshAllocator(out job.allocator); // Provide the job with the allocator
mgc.InsertMeshGenerationNode(out job.node); // Insert a node in the element draw sequence
JobHandle jobHandle = job.Schedule();
mgc.AddMeshGenerationJob(jobHandle);
}
}