Skip to content

Instantly share code, notes, and snippets.

@mao-test-h
Last active December 31, 2020 05:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mao-test-h/8d464cf9a746dd00d1ff0f67f1eb0074 to your computer and use it in GitHub Desktop.
Save mao-test-h/8d464cf9a746dd00d1ff0f67f1eb0074 to your computer and use it in GitHub Desktop.
Unityでのネイティブメモリ確保用のラッパー
using System;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
namespace Unity.Collections
{
/// <summary>
/// ネイティブメモリでの2次元配列のメモリ確保用ラッパー
/// </summary>
[NativeContainer]
[NativeContainerSupportsMinMaxWriteRestriction]
[NativeContainerSupportsDeallocateOnJobCompletion]
[NativeContainerSupportsDeferredConvertListToArray]
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Native2DArray<T> : IDisposable, IEquatable<Native2DArray<T>>
where T : unmanaged
{
public struct IndexInfo
{
public int Length;
public int MaxIndex;
public int MinIndex;
}
[NativeDisableUnsafePtrRestriction] T** _buffer;
readonly IndexInfo _info1;
readonly IndexInfo _info2;
readonly Allocator _allocatorLabel;
AtomicSafetyHandle _safety;
[NativeSetClassTypeToNullOnSchedule] DisposeSentinel _disposeSentinel;
public IndexInfo Info1 => _info1;
public IndexInfo Info2 => _info2;
public bool IsCreated => _buffer != null;
public Native2DArray(int length1, int length2, Allocator allocator,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
{
// Native allocation is only valid for Temp, Job and Persistent.
if (allocator <= Allocator.None)
{
throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator));
}
if (length1 < 0 || length2 < 0)
{
throw new ArgumentOutOfRangeException(
nameof(length1) + "/" + nameof(length2), "Length1 / Length2 must be >= 0");
}
var totalSize1 = sizeof(void*) * length1;
var totalSize2 = UnsafeUtility.SizeOf<T>() * length2;
// Make sure we cannot allocate more than int.MaxValue (2,147,483,647 bytes)
// because the underlying UnsafeUtility.Malloc is expecting a int.
// TODO: change UnsafeUtility.Malloc to accept a UIntPtr length instead to match C++ API
if (totalSize1 > int.MaxValue || totalSize2 > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
nameof(length1) + "/" + nameof(length2),
$"Length * sizeof(T) cannot exceed {int.MaxValue} bytes");
}
_buffer = (T**) UnsafeUtility.Malloc(totalSize1, UnsafeUtility.AlignOf<IntPtr>(), allocator);
for (var i = 0; i < length1; i++)
{
_buffer[i] = (T*) UnsafeUtility.Malloc(totalSize2, UnsafeUtility.AlignOf<T>(), allocator);
}
_info1 = new IndexInfo
{
Length = length1,
MinIndex = 0,
MaxIndex = length1 - 1,
};
_info2 = new IndexInfo()
{
Length = length2,
MinIndex = 0,
MaxIndex = length2 - 1,
};
_allocatorLabel = allocator;
DisposeSentinel.Create(out _safety, out _disposeSentinel, 1, allocator);
if ((options & NativeArrayOptions.ClearMemory) != NativeArrayOptions.ClearMemory) return;
for (var i = 0; i < length1; i++)
{
UnsafeUtility.MemClear(_buffer[i], totalSize2);
}
}
[WriteAccessRequired]
public void Dispose()
{
if (!UnsafeUtility.IsValidAllocator(_allocatorLabel))
{
throw new InvalidOperationException(
"The NativeArray can not be Disposed because it was not allocated with a valid allocator.");
}
DisposeSentinel.Dispose(ref _safety, ref _disposeSentinel);
for (var i = 0; i < Info1.Length; i++)
{
UnsafeUtility.Free(_buffer[i], _allocatorLabel);
_buffer[i] = null;
}
UnsafeUtility.Free(_buffer, _allocatorLabel);
_buffer = null;
}
public bool Equals(Native2DArray<T> other) => (_buffer == other._buffer) &&
(_info1.Length == other._info1.Length) &&
(_info2.Length == other._info2.Length);
public T** GetUnsafePtr()
{
AtomicSafetyHandle.CheckWriteAndThrow(_safety);
return _buffer;
}
public T** GetUnsafeReadOnlyPtr()
{
AtomicSafetyHandle.CheckReadAndThrow(_safety);
return _buffer;
}
public T** GetUnsafeBufferPointerWithoutChecks()
{
return _buffer;
}
}
}
using System;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
namespace Unity.Collections
{
/// <summary>
/// ネイティブメモリでの3次元配列のメモリ確保用ラッパー
/// </summary>
[NativeContainer]
[NativeContainerSupportsMinMaxWriteRestriction]
[NativeContainerSupportsDeallocateOnJobCompletion]
[NativeContainerSupportsDeferredConvertListToArray]
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Native3DArray<T> : IDisposable, IEquatable<Native3DArray<T>>
where T : unmanaged
{
public struct IndexInfo
{
public int Length;
public int MaxIndex;
public int MinIndex;
}
[NativeDisableUnsafePtrRestriction] T*** _buffer;
readonly IndexInfo _info1;
readonly IndexInfo _info2;
readonly IndexInfo _info3;
readonly Allocator _allocatorLabel;
AtomicSafetyHandle _safety;
[NativeSetClassTypeToNullOnSchedule] DisposeSentinel _disposeSentinel;
public IndexInfo Info1 => _info1;
public IndexInfo Info2 => _info2;
public IndexInfo Info3 => _info3;
public bool IsCreated => _buffer != null;
public Native3DArray(int length1, int length2, int length3, Allocator allocator,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
{
// Native allocation is only valid for Temp, Job and Persistent.
if (allocator <= Allocator.None)
{
throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator));
}
if (length1 < 0 || length2 < 0 || length3 < 0)
{
throw new ArgumentOutOfRangeException(
nameof(length1) + "/" + nameof(length2) + "/" + nameof(length3),
"Length1 / Length2 / Length3 must be >= 0");
}
var totalSize1 = sizeof(void*) * length1;
var totalSize2 = sizeof(void*) * length2;
var totalSize3 = UnsafeUtility.SizeOf<T>() * length3;
// Make sure we cannot allocate more than int.MaxValue (2,147,483,647 bytes)
// because the underlying UnsafeUtility.Malloc is expecting a int.
// TODO: change UnsafeUtility.Malloc to accept a UIntPtr length instead to match C++ API
if (totalSize1 > int.MaxValue || totalSize2 > int.MaxValue || totalSize3 > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
nameof(length1) + "/" + nameof(length2) + "/" + nameof(length3),
$"Length * sizeof(T) cannot exceed {int.MaxValue} bytes");
}
var ptrAlign = UnsafeUtility.AlignOf<IntPtr>();
_buffer = (T***) UnsafeUtility.Malloc(totalSize1, ptrAlign, allocator);
for (var i = 0; i < length1; i++)
{
_buffer[i] = (T**) UnsafeUtility.Malloc(totalSize2, ptrAlign, allocator);
for (var j = 0; j < length2; j++)
{
_buffer[i][j] = (T*) UnsafeUtility.Malloc(totalSize3, UnsafeUtility.AlignOf<T>(), allocator);
}
}
_info1 = new IndexInfo
{
Length = length1,
MinIndex = 0,
MaxIndex = length1 - 1,
};
_info2 = new IndexInfo()
{
Length = length2,
MinIndex = 0,
MaxIndex = length2 - 1,
};
_info3 = new IndexInfo()
{
Length = length3,
MinIndex = 0,
MaxIndex = length3 - 1,
};
_allocatorLabel = allocator;
DisposeSentinel.Create(out _safety, out _disposeSentinel, 1, allocator);
if ((options & NativeArrayOptions.ClearMemory) != NativeArrayOptions.ClearMemory) return;
for (var i = 0; i < length1; i++)
{
for (var j = 0; j < length2; j++)
{
UnsafeUtility.MemClear(_buffer[i][j], totalSize3);
}
}
}
[WriteAccessRequired]
public void Dispose()
{
if (!UnsafeUtility.IsValidAllocator(_allocatorLabel))
{
throw new InvalidOperationException(
"The NativeArray can not be Disposed because it was not allocated with a valid allocator.");
}
DisposeSentinel.Dispose(ref _safety, ref _disposeSentinel);
for (var i = 0; i < _info1.Length; i++)
{
for (var j = 0; j < _info2.Length; j++)
{
UnsafeUtility.Free(_buffer[i][j], _allocatorLabel);
_buffer[i][j] = null;
}
UnsafeUtility.Free(_buffer[i], _allocatorLabel);
_buffer[i] = null;
}
UnsafeUtility.Free(_buffer, _allocatorLabel);
_buffer = null;
}
public bool Equals(Native3DArray<T> other) => (_buffer == other._buffer) &&
(_info1.Length == other._info1.Length) &&
(_info2.Length == other._info2.Length) &&
(_info3.Length == other._info3.Length);
public T*** GetUnsafePtr()
{
AtomicSafetyHandle.CheckWriteAndThrow(_safety);
return _buffer;
}
public T*** GetUnsafeReadOnlyPtr()
{
AtomicSafetyHandle.CheckReadAndThrow(_safety);
return _buffer;
}
public T*** GetUnsafeBufferPointerWithoutChecks()
{
return _buffer;
}
}
}
using System;
using System.Runtime.InteropServices;
namespace Unity.Collections.LowLevel.Unsafe
{
public static unsafe class UnsafeUtilityHelper
{
#region public methods
public static T* AllocateNativeArray<T>(
int length,
Allocator allocator,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
where T : unmanaged
{
return (T*) Malloc<T>(allocator, length, options);
}
public static void ReleaseNativeArray<T>(T* ptr, Allocator allocator)
where T : unmanaged
{
Free(ptr, allocator);
}
public static T** AllocateNative2DArray<T>(
int length1, int length2,
Allocator allocator,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
where T : unmanaged
{
var ptr = (T**) MallocPtr(allocator, length1, options);
for (var i = 0; i < length1; i++)
{
ptr[i] = (T*) Malloc<T>(allocator, length2, options);
}
return (T**) ptr;
}
public static void ReleaseNative2DArray<T>(T** ptr, int length1, Allocator allocator)
where T : unmanaged
{
for (var i = 0; i < length1; i++)
{
Free(ptr[i], allocator);
}
Free(ptr, allocator);
}
public static T*** AllocateNative3DArray<T>(
int length1, int length2, int length3,
Allocator allocator,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
where T : unmanaged
{
var ptrAlign = UnsafeUtility.AlignOf<IntPtr>();
var ptr = (T***) MallocPtr(allocator, length1, options);
for (var i = 0; i < length1; i++)
{
ptr[i] = (T**) MallocPtr(allocator, length2, options);
for (var j = 0; j < length2; j++)
{
ptr[i][j] = (T*) Malloc<T>(allocator, length3, options);
}
}
return (T***) ptr;
}
public static void ReleaseNative3DArray<T>(T*** ptr, int length1, int length2, Allocator allocator)
where T : unmanaged
{
for (var i = 0; i < length1; i++)
{
for (var j = 0; j < length2; j++)
{
Free(ptr[i][j], allocator);
}
Free(ptr[i], allocator);
}
Free(ptr, allocator);
}
public static T* MallocIndirect<T>(
int length,
Allocator allocator,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
where T : unmanaged
{
return Malloc<T>(allocator, length, options);
}
public static void* MallocPtrIndirect(
int length,
Allocator allocator,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
=> MallocPtr(allocator, length, options);
public static void FreeIndirect(void* ptr, Allocator allocator) => Free(ptr, allocator);
public static T* Copy<T>(T[] src, int length, Allocator allocator)
where T : unmanaged
{
if (length == -1)
{
length = src.Length;
}
var ptr = (T*) Malloc<T>(allocator, length);
#if UNITY_EDITOR
if (src == null)
{
throw new ArgumentNullException(nameof(src));
}
#endif
var handle = GCHandle.Alloc(src, GCHandleType.Pinned);
var addr = handle.AddrOfPinnedObject();
UnsafeUtility.MemCpy(ptr, (void*) addr, length * UnsafeUtility.SizeOf<T>());
handle.Free();
return ptr;
}
#endregion
#region private methods
static T* Malloc<T>(
Allocator allocator,
int length = 1,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
where T : unmanaged
{
var totalSize = UnsafeUtility.SizeOf<T>() * (long) length;
#if UNITY_EDITOR
if (allocator <= Allocator.None)
{
throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator));
}
if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(length), "Length must be >= 0");
}
if (totalSize > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(length),
$"Length * sizeof(T) cannot exceed {int.MaxValue} bytes");
}
#endif
var ptr = UnsafeUtility.Malloc(totalSize, UnsafeUtility.AlignOf<T>(), allocator);
if (options == NativeArrayOptions.ClearMemory)
{
UnsafeUtility.MemClear(ptr, totalSize);
}
return (T*) ptr;
}
static void* MallocPtr(
Allocator allocator,
int length = 1,
NativeArrayOptions options = NativeArrayOptions.ClearMemory)
{
var totalSize = sizeof(void*) * (long) length;
#if UNITY_EDITOR
if (allocator <= Allocator.None)
{
throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator));
}
if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(length), "Length must be >= 0");
}
if (totalSize > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(length),
$"Length * sizeof(T) cannot exceed {int.MaxValue} bytes");
}
#endif
var ptr = UnsafeUtility.Malloc(totalSize, UnsafeUtility.AlignOf<IntPtr>(), allocator);
if (options == NativeArrayOptions.ClearMemory)
{
UnsafeUtility.MemClear(ptr, totalSize);
}
return ptr;
}
static void Free(void* ptr, Allocator allocator)
{
if (ptr == null) return;
UnsafeUtility.Free(ptr, allocator);
ptr = null;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment