Version: Unity 6.5 (6000.5)
Language : English
Structures, classes, and unions
Strings

NativeArrays

NativeArrays and other types in the Unity.Collections namespace store data in memory that isn’t managed by the scripting runtime. When you pass a pointer to this data to native code, you don’t have to “pin” the buffer in memory. However, you still need to consider the memory lifetime of the data, which is determined by the Allocator you use:

Allocator Lifetime Typical memory pool size
Allocator.Temp The memory is automatically cleaned up at the end of each frame. 4–16 MB main thread 256 KB worker threads
Allocator.Persistent The memory persists until the object that owns the allocation is disposed of.
Allocator.Domain The memory persists until the C# domain is unloaded.
Allocator.TempJob The memory persists until the object that owns the allocation is disposed of. TempJob allocations shouldn’t be kept for more than four frames. If this type of memory is exhausted, then Unity falls back to a slower memory allocation method. 16–64 MB
Allocator.None The None allocator is used when the instance does not own the memory in the buffer it references.

Allocator.Temp is generally the fastest way to allocate memory, but you can only use it until the end of the frame. If you allocate more memory than the amount available in the memory pool, Unity falls back to a slower type of allocation. Refer to Unmanaged C# memory for more information.

The layout of a NativeArray struct is an internal detail, so you can’t control how its fields are marshalled. However, you can pass a pointer to the buffer inside the array using an explicit pointer in an unsafe context.

Use unsafe to get an array pointer

Use the NativeArray.GetUnsafePtr() method or the NativeArrayUnsafeUtility class to get a pointer to the buffer in a NativeArray. When you pass the pointer to an unmanaged function, you can treat it like a C array.

The following example is an unmanaged function that takes an array of Color (unsigned 32-bit integers) as a parameter:

typedef int32_t Color;

extern "C" {
    void CheckerFill(Color* texture, int width, int height, int numSquares, const Color colors[], int numColors) {

        const int squareSize = width / numSquares;

        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                // Select a palette entry, staying within the bounds of the colors array
                const int colorIndex = ((y / squareSize) + (x / squareSize)) % numColors;
                texture[y * width + x] = colors[colorIndex];
            }
        }
    }
}

Note: Refer to Call unmanaged functions from managed code for information about the annotations needed to compile and call functions such as this as part of a dynamically loaded library.

You can pass a pointer to a NativeArray’s buffer to this function. Using pointers in C# requires an unsafe context. Refer to Compiling unsafe C# code for information about enabling unsafe code compilation in Unity.

The following code demonstrates how to call the unmanaged CheckerFill() function. The example performs the following steps:

  1. Gets a NativeArray containing the pixelThe smallest unit in a computer image. Pixel size depends on your screen resolution. Pixel lighting is calculated at every screen pixel. More info
    See in Glossary
    data of a Texture2D object.
  2. Gets a pointer to the NativeArray’s buffer with GetUnsafePtr().
  3. Calls the unmanaged, CheckerFill(), function to fill the texture with a checker pattern.
  4. Applies the result to the Texture2D object.
using UnityEngine;
using Unity.Collections;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;

public class NativeArrayExamples
{
    // Change DllImport to use library name for
    // precompiled, dynamically linked libraries.
    [DllImport("__Internal")]
    static extern unsafe void CheckerFill(Color32* pixelDataPtr, int width, int height, int squares, [In] Color32[] colors, int numColors);

    public static void FillTextureWithCheckerboard(Texture2D texture, Color32 one, Color32 two, int squaresPerSide)
    {
        unsafe
        {
            // Get the pixel data as a NativeArray
            NativeArray<Color32> pixelData = texture.GetPixelData<Color32>(0);
            // Get a pointer to the NativeArray's buffer
            void* pixelBuffer = pixelData.GetUnsafePtr();
            // The color palette to choose from
            var palette = new Color32[] { one, two };
            // Call the unmanaged function, passing the palette length so it stays within the colors array
            CheckerFill((Color32*)pixelBuffer, texture.width, texture.height, squaresPerSide, palette, palette.Length);
            // Apply the changes to the texture
            texture.Apply(false);
        }
    }
}

Note: to safely access an array from unmanaged code, you must pass the number of elements it contains. In this example, the code passes numColors (the length of the color palette) so the function never reads past the end of the colors array. The texture array length can be calculated from the width and height parameters, so the length isn’t passed explicitly in this example.

Demonstration

You can use the following MonoBehaviour class to call the NativeArrayExamples FillTextureWithCheckerboard method. The example adds two GameObjectThe fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject’s functionality is defined by the Components attached to it. More info
See in Glossary
primitives to the sceneA Scene contains the environments and menus of your game. Think of each unique Scene file as a unique level. In each Scene, you place your environments, obstacles, and decorations, essentially designing and building your game in pieces. More info
See in Glossary
with textures generated using the unmanaged CheckerFill function.

using UnityEngine;

public class DemoNativeArray : MonoBehaviour
{
    void Start()
    {
        AddCheckeredObject(PrimitiveType.Sphere, Color.blue,  Color.yellow, Vector3.zero);
        AddCheckeredObject(PrimitiveType.Cube, Color.red,  Color.white, Vector3.one);
    }

    void AddCheckeredObject(PrimitiveType primitive, Color colorOne, Color colorTwo, Vector3 position)
    {
        // Create a new texture
        Texture2D checkerboard = new Texture2D(256, 256, TextureFormat.RGBA32, false);
        // Fill the texture with a checkerboard pattern using the NativeArrayExamples class
        NativeArrayExamples.FillTextureWithCheckerboard(checkerboard, colorOne, colorTwo, 8);

        var go = GameObject.CreatePrimitive(primitive);
        go.transform.position = position;

        var litShader = Shader.Find("Universal Render Pipeline/Lit");
        if (litShader != null)
        {
            // Create a new material using the URP Lit shader
            Material material = new Material(litShader);
            // Assign the texture to the material
            material.SetTexture("_BaseMap", checkerboard);
            go.GetComponent<MeshRenderer>().material = material;
        }
        else
            Debug.LogError("Unable to load the Universal Render Pipeline/Lit shader.");
    }
}

Notes:

  • The example requires IL2CPPA Unity-developed scripting back-end which you can use as an alternative to Mono when building projects for some platforms. More info
    See in Glossary
    and doesn’t work in the Editor Play mode when you use [DllImport("__Internal")]. To run the example in Play Mode, you can compile the unmanaged example code as a library and change the DLLImport statements to use the library name. Refer to DllImport attribute for more information.
  • The demonstration loads the URP Lit shaderA program that runs on the GPU. More info
    See in Glossary
    at runtime. The build process strips unused shaders and variants from a build, and can’t detect runtime usage such as this. The easiest workaround to run this example is to add a primitive object to the scene before building.
  • Texture2D.GetPixelData<T> returns a NativeArray that points to the texture’s existing memory. It doesn’t allocate memory, so you don’t need to dispose the NativeArray. (Disposing it doesn’t do anything.)
Structures, classes, and unions
Strings