[Unity Native Container] 自定义Native Container [第 3 部分]:使用 Min Max 的Parallel Job

drawing

介绍

  在本系列文章的前几部分,我们研究了如何创建一个基本的、可用于Job的自定义Native container。这篇文章将改进我们的NativeIntArray容器,增加对并行Job的支持。这是通过使用一种模式来实现的,在这种模式下,Job被分割成多个范围,每个Job只允许在这个范围内操作。这将数组的访问限制在通过Execute(int index)传递的索引。更多关于这些Job在幕后如何调度的信息可以在这里的Unity文档中找到。

1) 启用支持

  我们添加了 [NativeContainerSupportsMinMaxWriteRestriction] 标签来支持这种并行Job。我们还必须创建 m_MinIndex 和 m_MaxIndex 变量,并用我们数组的整个范围初始化它们。这些变量是安全检查所必需的。注意,变量的命名和顺序在这里非常重要!

  我们也将利用这个机会快速提醒一下我们的容器大致是什么样子的:一个简单的整数数组。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;

// 这样就可以支持并行Job的检测,每个Job线程
// 只允许对最小和最大之间的指数范围进行操作。
[NativeContainerSupportsMinMaxWriteRestriction]
[NativeContainerSupportsDeallocateOnJobCompletion]
[NativeContainer]
[StructLayout(LayoutKind.Sequential)] 
public unsafe struct NativeIntArray : IDisposable
{
    [NativeDisableUnsafePtrRestriction] internal void* m_Buffer;
    internal int m_Length;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
	// NativeContainerSupportsMinMaxWriteRestriction 期望对其可以操作的传递范围进行安全检查。
	// 当一个并行Job安排它的批处理Job时,该范围被传递给容器。
	internal int m_MinIndex;
    internal int m_MaxIndex;

    internal AtomicSafetyHandle m_Safety;
    [NativeSetClassTypeToNullOnSchedule] internal DisposeSentinel m_DisposeSentinel;
#endif

    internal Allocator m_AllocatorLabel;

    public NativeIntArray(int length, Allocator allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory){ /* More Code */ }

    static void Allocate(int length, Allocator allocator, out NativeIntArray array)
    {
        long size = UnsafeUtility.SizeOf<int>() * (long)length;

		/* More Code */

        array = default(NativeIntArray);
        array.m_Buffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf<int>(), allocator);
        array.m_Length = length;
        array.m_AllocatorLabel = allocator;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
        // 默认情况下,Job可以在整个范围内运行。
        array.m_MinIndex = 0;
        array.m_MaxIndex = length - 1;

        DisposeSentinel.Create(out array.m_Safety, out array.m_DisposeSentinel, 1, allocator);
#endif
    }

	/*
	 * ... Next Code ...
	 */
2) 范围检查

  我们需要对代码进行的唯一改动是在访问数组中的一个元素时检查我们是否在范围内。在这个容器中访问数组的所有其他函数都使用[]操作符,因此只需在这个操作符上添加我们的范围检查即可。

	/*
	 * ... Previous Code ...
	 */

	// 如果安全被禁用,则删除对此函数的调用。
	[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
	private void CheckRangeAccess(int index)
    {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
		// 检查我们是否在此并行批处理Job操作的索引范围内。
		if (index < m_MinIndex || index > m_MaxIndex)
        {
            if (index < Length && (m_MinIndex != 0 || m_MaxIndex != Length - 1))
                throw new IndexOutOfRangeException(string.Format(
                    "Index {0} is out of restricted IJobParallelFor range [{1}...{2}] in ReadWriteBuffer.\n" +
                    "ReadWriteBuffers are restricted to only read & write the element at the job index. " +
                    "You can use double buffering strategies to avoid race conditions due to " +
                    "reading & writing in parallel to the same elements from a job.",
                    index, m_MinIndex, m_MaxIndex));

			// 这不是并行Job,但索引仍然超出范围。
			throw new IndexOutOfRangeException(string.Format("Index {0} is out of range of '{1}' Length.", index, Length));
        }
#endif
    }

    public int this[int index]
    {
        get
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
#endif
            CheckRangeAccess(index);
            return UnsafeUtility.ReadArrayElement<int>(m_Buffer, index);
        }

        [WriteAccessRequired]
        set
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
#endif
            CheckRangeAccess(index);
            UnsafeUtility.WriteArrayElement(m_Buffer, index, value);
        }
    }

	/*
	* ... More Code ...
	*/
使用方法

  而这一切!我们现在在 NativeIntArray 中添加了对Job Parallel的支持。这方面的一个例子如下所示。

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;

public class NativeIntArraySystem : SystemBase
{
	[BurstCompile]
    struct ParallelWriteRangeJob : IJobParallelFor
    {
        public Random random;
		// 请参阅上一部分,了解如何添加对[DeallocateOnJobCompletion].
        [DeallocateOnJobCompletion] public NativeIntArray array;

        public void Execute(int index)
        {
            array[index] = random.NextInt();
        }
    }
	
	protected override void OnUpdate()
    {
        NativeIntArray myArray = new NativeIntArray(1024, Allocator.TempJob);

		// Fill myArray with random values.
        JobHandle jobHandle = new ParallelWriteRangeJob()
        {
            random = new Random((uint)UnityEngine.Random.Range(0, int.MaxValue)),
            array = myArray
        }.Schedule(myArray.Length, 64, Dependency); // 批处理大小为 64 的计划。

		Dependency = jobHandle;
	}
}

总结

  这篇文章展示了如何使用一种模式来增加对并行Job的支持,在这种模式下,Job被分割成多个范围。但是这种模式的一个局限性是它不允许多个Job到同一个索引。在下一部分中,我们将看看如何通过添加对ParallelWriter的支持来实现这一点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Native OpenGL渲染Unity场景需要以下步骤: 1. 创建一个Native Plugin项目,并将OpenGL渲染代码添加到其中。 2. 在Unity中导入该Native Plugin,并在需要渲染的场景对象或摄像机上添加一个脚本,该脚本将调用Native Plugin中的OpenGL渲染函数。 3. 在渲染过程中,您需要将Unity场景中的模型、纹理、材质等信息转换为OpenGL可用的数据格式,并在OpenGL上下文中进行渲染。 下面是具体实现的步骤: 1. 创建一个Native Plugin项目 在Visual Studio中创建一个C++项目,选择“DLL”作为项目类型。然后将OpenGL渲染代码添加到该项目中。 2. 导入Native Plugin到UnityNative Plugin生成的DLL文件和相关头文件拷贝到Unity项目的Assets/Plugins目录下,然后在Unity中创建一个脚本,用于调用Native Plugin中的OpenGL渲染函数。 在脚本中使用DllImport特性引入Native Plugin中的函数,并在需要渲染的场景对象或摄像机上添加该脚本。 ``` using UnityEngine; using System.Runtime.InteropServices; public class OpenGLRenderer : MonoBehaviour { [DllImport("NativePlugin")] private static extern void RenderFrame(); void OnPostRender() { GL.PushMatrix(); GL.LoadOrtho(); RenderFrame(); GL.PopMatrix(); } } ``` 3. 渲染Unity场景 在OpenGL渲染函数中,您需要将Unity场景中的模型、纹理、材质等信息转换为OpenGL可用的数据格式,并在OpenGL上下文中进行渲染。 以下是一个简单的OpenGL渲染函数实现,用于渲染Unity场景: ``` void RenderFrame() { // 清空颜色缓冲区和深度缓冲区 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 获取摄像机矩阵 Matrix4x4 cameraMatrix = Camera.main.worldToCameraMatrix; float[] cameraMatrixArray = Matrix4x4ToArray(cameraMatrix); // 获取投影矩阵 Matrix4x4 projectionMatrix = Camera.main.projectionMatrix; float[] projectionMatrixArray = Matrix4x4ToArray(projectionMatrix); // 绑定Unity场景中的纹理 Texture2D texture = GetComponent<Renderer>().material.mainTexture; int textureID = texture.GetNativeTexturePtr().ToInt32(); glBindTexture(GL_TEXTURE_2D, textureID); // 绘制模型 glBegin(GL_TRIANGLES); // 顶点坐标 glVertex3f(0.0f, 0.5f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.0f); glVertex3f(0.5f, -0.5f, 0.0f); // 纹理坐标 glTexCoord2f(0.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glEnd(); // 刷新OpenGL缓冲区 glFlush(); } ``` 以上是使用Native OpenGL渲染Unity场景的基本步骤,您可以根据自己的需求进行相应的调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值