im.pala.nativetexture is a Unity package that provides Burst/job-friendly native containers over texture memory with direct pointer access. It enables high-performance CPU-side texture generation, processing, and efficient upload to Unity Texture2D or GPU buffers without unnecessary copies.
- Native Containers:
NativeTexture2D<T>,NativeTexture3D<T>,NativeTexture4D<T>— own a raw buffer or wrap an existingTexture2D - Unsafe Variants:
UnsafeTexture2D<T>,UnsafeTexture3D<T>,UnsafeTexture4D<T>— minimal-overhead access with bounds-checkedTryRead/TryWrite - Format Types:
byte2/3/4,sbyte2/3/4,short2/3/4,ushort2/3/4with operator overloads,FromNormalized()/ToNormalized(), and swizzle properties - Read-Only Views:
.AsReadOnly()on 2D, 3D, and 4D containers - Normalization: Burst-compiled
NormalizeTextureJobfor[min,max]→[0,1]remapping - Sampling Extensions:
ReadPixel,ReadPixelBilinear,Contains,Swap - Utilities:
MipUtility,NativeTextureUnsafeUtility,IndexCoordUtils,TextureBoundsUtility - FastNoise2 Integration: Optional bridge assembly with extension methods for noise generation into native textures
- Open Unity and navigate to
Window → Package Manager. - Click the
+button and selectAdd package from git URL.... - Paste the following URL and click
Add:
https://github.com/api-haus/im.pala.nativetexture.git
| Dependency | Version |
|---|---|
com.unity.collections |
2.6.3 |
com.unity.mathematics |
1.3.3 |
Runtime references: Unity.Burst, Unity.Collections, Unity.Mathematics
Namespace: NativeTexture
Construction:
// Wrap an existing Unity texture
var pixels = new NativeTexture2D<float4>(unityTexture);
// Own a raw buffer
var pixels = new NativeTexture2D<float>(new int2(512, 512), mipCount: 1, TextureFormat.RFloat, Allocator.Persistent);
// Shorthand (single mip, format inferred from sizeof(T))
var pixels = new NativeTexture2D<float>(new int2(512, 512), Allocator.TempJob);Properties: Width, Height, Resolution (int2), MipCount, Length, TexelSize, IsCreated
Access:
- Indexers:
texture[int index],texture[int2 coord],texture[int x, int y],texture[int x, int y, int mip] AsArray()— returnsNativeArray<T>over the pixel bufferGetUnsafePtr()/GetUnsafeReadOnlyPtr()AsReadOnly()— returnsNativeTexture2D<T>.ReadOnlySliceMip(int mipLevel)— extract a specific mip level
Upload:
ApplyTo(Texture2D texture, bool updateMipmaps = false)
Disposal:
Dispose()/Dispose(JobHandle)for scheduled cleanup
Namespace: NativeTexture
Construction:
// Own a raw 3D buffer
var voxels = new NativeTexture3D<float>(new int3(64, 64, 64), Allocator.Persistent);
// Wrap an existing Texture2D as a 3D volume
var voxels = new NativeTexture3D<float>(unityTexture, new int3(64, 64, 64));Properties: Width, Height, Depth, Resolution (int3), Length, TexelSize, IsCreated
Access:
- Indexers:
texture[int index],texture[int3 coord] Load(int3 coord),Load(int index, out int3 coord)AsArray()— returnsNativeArray<T>GetUnsafePtr()/GetUnsafeReadOnlyPtr()AsReadOnly()— returnsNativeTexture3D<T>.ReadOnly
Upload:
ApplyTo(Texture2D texture, bool updateMipmaps = false)
Disposal:
Dispose()/Dispose(JobHandle)
Namespace: NativeTexture
4D native container for noise fields, animation volumes, parameterized textures, etc.
Construction:
var volume = new NativeTexture4D<float>(new int4(32, 32, 32, 8), Allocator.Persistent);Properties: Width, Height, Depth, WSize, Resolution (int4), Length, TexelSize, IsCreated
Access:
- Indexers:
texture[int index],texture[int4 coord] Load(int4 coord),Load(int index, out int4 coord)AsArray()— returnsNativeArray<T>GetUnsafePtr()/GetUnsafeReadOnlyPtr()AsReadOnly()— returnsNativeTexture4D<T>.ReadOnly
Disposal:
Dispose()/Dispose(JobHandle)
Namespace: NativeTexture
Minimal-overhead variants with no safety handles. Created from their corresponding native container via factory methods. All provide bounds-checked TryRead/TryWrite.
| Type | Factory | Coordinate |
|---|---|---|
UnsafeTexture2D<T> |
UnsafeTexture2DFactory.FromNativeTexture(native2D) |
int2 |
UnsafeTexture3D<T> |
UnsafeTexture3DFactory.FromNativeTexture(native3D) |
int3 |
UnsafeTexture4D<T> |
UnsafeTexture4DFactory.FromNativeTexture(native4D) |
int4 |
var native = new NativeTexture2D<float>(new int2(256, 256), Allocator.TempJob);
UnsafeTexture2D<float> unsafeTex = UnsafeTexture2DFactory.FromNativeTexture(native);
if (unsafeTex.TryRead(new int2(10, 20), out float value))
Debug.Log(value);
unsafeTex.TryWrite(new int2(10, 20), in someValue);Common properties: Width, Height, Length, IsCreated (3D adds Depth; 4D adds Depth, WSize)
Namespace: NativeTexture.Formats
Custom pixel format types with full operator support:
| Type | Components | Conversions |
|---|---|---|
byte2, byte3, byte4 |
unsigned 8-bit | FromNormalized(floatN), ToNormalized() |
sbyte2, sbyte3, sbyte4 |
signed 8-bit | FromNormalized(floatN), ToNormalized() |
short2, short3, short4 |
signed 16-bit | FromNormalized(floatN), ToNormalized() |
ushort2, ushort3, ushort4 |
unsigned 16-bit | FromNormalized(floatN), ToNormalized() |
All types include:
- Arithmetic operators:
+,-,*,/,% - Comparison operators:
<,<=,>,>=,==,!= - Increment/decrement:
++,-- - Implicit/explicit conversions to and from Unity.Mathematics types
- Swizzle properties (e.g.,
xy,yx,xyz,wzyx)
Namespace: NativeTexture.Jobs
Burst-compiled IJobParallelFor that normalizes float texture values from [min, max] to [0, 1].
using NativeTexture.Jobs;
// Schedule for a 2D texture
JobHandle handle = NormalizeTextureJob.Schedule(texture2D, min: -1f, max: 1f);
// Schedule for a 3D texture
JobHandle handle = NormalizeTextureJob.Schedule(texture3D, min: -1f, max: 1f);Namespace: NativeTexture.Extensions
using NativeTexture.Extensions;
// Bilinear sampling in normalized UV space
float sample = tex.ReadPixelBilinear(new float2(0.3f, 0.6f));
// Direct pixel read
float pixel = tex.ReadPixel(new int2(100, 200));
// Bounds check
bool inBounds = tex.Contains(new int2(x, y));
// Swap two pixels
tex.Swap(index1, index2);Assembly: NativeTexture.FastNoise2 — auto-compiles when com.auburn.fastnoise2 is present (via versionDefines → HAS_FASTNOISE2).
Namespace: NativeTexture.FastNoise2
// 2D uniform grid generation
fn.GenUniformGrid2D(
NativeTexture2D<T> nativeTexture,
out FastNoise.OutputMinMax minMax,
float xOffset, float yOffset,
int xCount, int yCount,
float xStepSize, float yStepSize,
int seed);
// 2D tileable generation
fn.GenTileable2D(
NativeTexture2D<T> nativeTexture,
out FastNoise.OutputMinMax minMax,
int xSize, int ySize,
float xStepSize, float yStepSize,
int seed);
// 3D uniform grid generation
fn.GenUniformGrid3D(
NativeTexture3D<T> nativeTexture,
out FastNoise.OutputMinMax minMax,
float xOffset, float yOffset, float zOffset,
int xCount, int yCount, int zCount,
float xStepSize, float yStepSize, float zStepSize,
int seed);// Normalize using FastNoise.OutputMinMax directly
JobHandle handle = texture2D.ScheduleNormalize(minMax, dependency);
JobHandle handle = texture3D.ScheduleNormalize(minMax, dependency);using FastNoise2.Bindings;
using NativeTexture;
using NativeTexture.FastNoise2;
using Unity.Collections;
using UnityEngine;
using FastNoise fn = FastNoise.FromEncodedNodeTree("DQAFAAAAAAAAQAgAAAAAAD8AAAAAAA==");
var noiseTexture = new NativeTexture2D<float>(new int2(512, 512), Allocator.TempJob);
fn.GenUniformGrid2D(
noiseTexture,
out FastNoise.OutputMinMax minMax,
0, 0,
noiseTexture.Width, noiseTexture.Height,
0.02f, 0.02f,
1337);
// Normalize to [0,1] range
noiseTexture.ScheduleNormalize(minMax).Complete();
var texture = new Texture2D(512, 512, TextureFormat.RFloat, false);
noiseTexture.ApplyTo(texture);
noiseTexture.Dispose();using NativeTexture;
using Unity.Mathematics;
using UnityEngine;
var unityTex = new Texture2D(256, 256, TextureFormat.RGBAFloat, false, true);
var pixels = new NativeTexture2D<float4>(unityTex);
pixels[new int2(128, 128)] = new float4(1, 0, 0, 1);
pixels.ApplyTo(unityTex, updateMipmaps: false);using NativeTexture;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
int w = 256, h = 256;
var pixels = new NativeTexture2D<float>(new int2(w, h), mipCount: 1, TextureFormat.RFloat, Allocator.Persistent);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
pixels[new int2(x, y)] = math.saturate(math.sin(x * 0.05f) * math.cos(y * 0.05f));
var target = new Texture2D(w, h, TextureFormat.RFloat, false, true);
pixels.ApplyTo(target);
pixels.Dispose();using NativeTexture;
using NativeTexture.Extensions;
using Unity.Mathematics;
NativeTexture2D<float> tex = /* created elsewhere */;
float sample = tex.ReadPixelBilinear(new float2(0.3f, 0.6f));Tmust be unmanaged. Use a matchingTextureFormatfor correctsizeof(T)mapping.- When wrapping an existing
Texture2D, memory ownership remains with Unity; dispose only buffers you own. - Burst/jobs ready: safety handles and min/max index constraints;
Dispose(JobHandle)is available for scheduled cleanup. - Both runtime assemblies have
autoReferenced: false— consumers must add explicit assembly references. NativeTexture.FastNoise2assembly usesdefineConstraints: ["HAS_FASTNOISE2"]andversionDefinesto auto-compile only whencom.auburn.fastnoise2is present.
| Assembly | Path | Notes |
|---|---|---|
NativeTexture |
Runtime/NativeTexture.asmdef |
Core containers, formats, jobs, sampling |
NativeTexture.FastNoise2 |
Runtime/FastNoise2Integration/NativeTexture.FastNoise2.asmdef |
FN2 bridge, requires HAS_FASTNOISE2 |
Submodule path: Packages/im.pala.nativetexture