Unity 6.3
0 онлайн 111 гостей 3 в системе
Вход
UI системы Шаг 207 из 317

Параллельная тесселяция

Чтобы повысить производительность при тесселлировании большой сетки, используйте систему заданий. Это предотвращает блокировку основного потока в течение длительного периода времени. При создании задания тесселлирования используйте MeshGenerationContext.AddMeshGenerationJob API, чтобы предоставить обработчик задания UI Toolkit. Это гарантирует, что UI Toolkit будет ждать завершения задания перед доступом к сетке.

Тесселяция с распределением по основному потоку

Если вы можете быстро вычислить количество вершин или индексов, используйте главный поток для выделения сетки и добавления ее в последовательность рисования. Вы можете сделать это следующим APIs:

Примечание: Вы можете добавить сетку в последовательность рисования в любой момент во время вызова 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);
    }
}

Jobified тесселяция с распределением в работе

Если вычисление числа вершин или индексов является вычислительно дорогостоящим, вставьте MeshGenerationNode в последовательность рисования. Затем предоставьте узел вашему заданию вместе с TempMeshAllocator. Вы можете сделать это с помощью следующего APIs:

В этом подходе выделение выполняется из задания. Используйте 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);
    }
}

Дополнительные ресурсы