Last active
December 31, 2020 05:15
-
-
Save mao-test-h/8d464cf9a746dd00d1ff0f67f1eb0074 to your computer and use it in GitHub Desktop.
Unityでのネイティブメモリ確保用のラッパー
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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