Unity 6.3
0 онлайн 60 гостей 3 в системе
Вход

IJobParallelForTransform

интерфейс в UnityEngine.Jobs

Описание

Интерфейс, позволяющий выполнять одну и ту же независимую операцию для позиции, поворота и масштаба каждой трансформации, переданной в задание.

Этот тип задания концептуально похож на IJobFor, но вместо того, чтобы итерировать по диапазону произвольных данных, он итерирует по массиву данных преобразования. Поскольку UnityEngine.Transform является управляемым типом и не может быть безопасно изменен кодом задания, этот тип задания предлагает обход этого ограничения.

Transform данные предоставляются заданию через UnityEngine.Jobs.TransformAccessArray, который обворачивает массив экземпляров UnityEngine.Transform в неуправляемый тип UnityEngine.TransformAccess и позволяет безопасно обрабатывать базовые данные параллельно рабочими потоками.

Этот тип задания имеет следующие параметры для планирования работы:

  • IJobParallelForTransformExtensions.RunReadOnlyByRef запускает задание на главном потоке и немедленно завершает его, с доступом только для чтения к преобразованным данным.
  • IJobParallelForTransformExtensions.ScheduleReadOnlyByRef планирует задание на асинхронный запуск, распределяя работу между несколькими рабочими потоками. Этот вариант планирования может дать наилучшую производительность, но условия гонки могут возникнуть, если задание записывает в общие или глобальные данные без надлежащей синхронизации.
  • IJobForExtensions.ScheduleByRef то же самое, что выше, но задание имеет доступ чтения/записи к преобразованным данным.

Execute(int index, TransformAccess transform) выполняется один раз для каждого индекса от 0 до указанной длины, с соответствующими данными преобразования доступными через указанный параметр.

RunReadOnlyByRef гарантирует, что работа Execute(int index, TransformAccess transform) метод вызывается последовательно. ScheduleReadOnlyByRef и ScheduleByRef не призывайте работу Execute метод последовательно, потому что он вызывается из нескольких рабочих потоков параллельно друг другу.

Каждая итерация должна быть независимой от других итераций, и система безопасности обеспечивает соблюдение этого правила. Индексы не имеют гарантированного порядка и выполняются на нескольких ядрах параллельно.

Для заданий, требующих доступа для чтения/записи для преобразования данных, Unity автоматически разделяет и сортирует предоставленные данные преобразования на куски, которые могут быть безопасно обработаны параллельно. Все преобразования из одной и той же иерархии преобразований будут обрабатываться одним рабочим потоком, чтобы обеспечить, что запись данных преобразования мирового пространства не вызывает условия гонки. Однако порядок, в котором преобразования в иерархии обрабатываются, не гарантируется.

Вы можете воспользоваться возвращенным JobHandle чтобы проверить, что задание завершено, или передать его другим заданиям в качестве зависимости. Когда вы передаете задание, JobHandle как зависимость, она обеспечивает выполнение заданий одно за другим на рабочих потоках.

Дополнительные ресурсы: UnityEngine.Jobs.IJobParallelForTransformExtensions.

public struct ApplyVelocityJobParallelForTransform : IJobParallelForTransform
{
    // Jobs declare all data that will be accessed in the job
    // By declaring it as read only, multiple jobs are allowed to access the data in parallel
    [ReadOnly]
    public NativeArray<Vector3> velocity;

    // Delta time must be copied to the job since jobs generally don't have a concept of a frame.
    // The main thread waits for the job same frame or next frame, but the job should do work deterministically
    // independent on when the job happens to run on the worker threads.
    public float deltaTime;

    // The code actually running on the job
    public void Execute(int index, TransformAccess transform)
    {
        // Move the transforms based on delta time and velocity
        var pos = transform.position;
        pos += velocity[index] * deltaTime;
        transform.position = pos;
    }
}

public void ApplyVelocity_ParallelForTransform()
{
    // Create some objects whose transforms will be processed
    var gameObjects = new GameObject[500];
    var transforms = new Transform[500];
    for (int i = 0; i < transforms.Length; ++i)
    {
        gameObjects[i] = new GameObject($"Test Object {i}");
        transforms[i] = gameObjects[i].transform;
    }

    var transformAccessArray = new TransformAccessArray(transforms);

    var velocity = new NativeArray<Vector3>(transforms.Length, Allocator.Persistent);

    for (var i = 0; i < velocity.Length; ++i)
        velocity[i] = new Vector3(0f, 10f, 0f);

    // Initialize the job data
    var job = new ApplyVelocityJobParallelForTransform()
    {
        deltaTime = Time.deltaTime,
        velocity = velocity
    };

    // If this job required a previous job to complete before it could safely begin execution,
    // we'd use its handle here. For this simple case, there are no job dependencies,
    // so a default JobHandle is sufficient.
    JobHandle dependencyJobHandle = default;

    // Schedule a parallel-for-transform job.
    // The method takes a TransformAccessArray which contains the Transforms that will be acted on in the job.
    JobHandle jobHandle = job.ScheduleByRef(transformAccessArray, dependencyJobHandle);

    // Ensure the job has completed.
    // It is not recommended to Complete a job immediately,
    // since that reduces the chance of having other jobs run in parallel with this one.
    // You optimally want to schedule a job early in a frame and then wait for it later in the frame.
    // Ideally this job's JobHandle would be passed as a dependency to another job that consumes the
    // output of this one. If the output of this job must be read from the main thread, you should call
    // Complete() on this job handle just before reading it.
    jobHandle.Complete();

    // Once the job is complete, the expected processing should have occurred.
    // Note that accessing the value of a Transform component on the main thread automatically
    // completes any IJobParallelForTransform which is currently writing to that Transform.
    for (var i = 0; i < velocity.Length; i++)
        Assert.AreEqual(Time.deltaTime * velocity[i].y, transforms[i].position.y);

    // Native containers must be disposed manually.
    velocity.Dispose();
    transformAccessArray.Dispose();
}

Открытые методы

Метод Описание
ExecuteВыполняет работу по определенному индексу итерации и преобразованию.