대규모 메시를 테셀레이션할 때 성능을 개선하려면 잡 시스템을 사용하십시오. 이렇게 하면 메인 스레드가 장기간 차단되지 않습니다. 테셀레이션 잡을 생성할 때 MeshGenerationContext.AddMeshGenerationJob API를 사용하여__ UI__(사용자 인터페이스) 사용자가 애플리케이션과 상호 작용하도록 해 줍니다. Unity는 현재 3개의 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);
}
}