Netcode Bootstrap
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Unity.Assertions;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
// This struct intended to be used as bitfield over existing memory
|
||||
public unsafe struct BitFieldN
|
||||
{
|
||||
uint* ptr;
|
||||
int sizeInInts;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BitFieldN(uint* ptr, int sizeInInts, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
|
||||
{
|
||||
this.ptr = ptr;
|
||||
this.sizeInInts = sizeInInts;
|
||||
|
||||
if (options == NativeArrayOptions.ClearMemory)
|
||||
Clear();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BitFieldN Clone(uint* ptr)
|
||||
{
|
||||
var rv = new BitFieldN(ptr, sizeInInts, NativeArrayOptions.UninitializedMemory);
|
||||
rv.CopyFrom(this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void CopyFrom(BitFieldN o)
|
||||
{
|
||||
UnsafeUtility.MemCpy(ptr, o.ptr, sizeInInts * sizeof(int));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static int CalculateUIntsCountForGivenBitCount(int bitCount)
|
||||
{
|
||||
var rv = (bitCount >> 5);
|
||||
var k = math.min(bitCount & 0x1f, 1);
|
||||
return rv + k;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool IsSet(int pos)
|
||||
{
|
||||
CheckArgs(pos, 1);
|
||||
var intIdx = GetIntIndexForBitPos(pos);
|
||||
var rv = Bitwise.ExtractBits(ptr[intIdx], pos, 1);
|
||||
return rv != 0u;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Set(int pos, bool value)
|
||||
{
|
||||
CheckArgs(pos, 1);
|
||||
var intIdx = GetIntIndexForBitPos(pos);
|
||||
ptr[intIdx] = Bitwise.SetBits(ptr[intIdx], pos, 1, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
UnsafeUtility.MemClear(ptr, sizeInInts * 4);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool TestAny()
|
||||
{
|
||||
var v = 0u;
|
||||
for (var i = 0; i < sizeInInts; ++i)
|
||||
{
|
||||
v |= ptr[i];
|
||||
}
|
||||
return v != 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int GetIntIndexForBitPos(int pos) => pos >> 5;
|
||||
public bool IsCreated => ptr != null;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int SizeInInts() => sizeInInts;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int Length
|
||||
{
|
||||
get => sizeInInts << 3;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
|
||||
void CheckArgs(int pos, int numBits)
|
||||
{
|
||||
if (pos + numBits < 0 || pos + numBits > sizeInInts * 32)
|
||||
{
|
||||
throw new ArgumentException($"BitFieldN invalid arguments: pos {pos} and numBits {numBits} must be within array capacity {sizeInInts * 32}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f2d2f39855664745bb1d7e3d9596f3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/BitFieldN.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,55 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class BlobStringExtensions
|
||||
{
|
||||
public unsafe static Hash128 CalculateHash128(ref this BlobString s)
|
||||
{
|
||||
var hasher = new xxHash3.StreamingState();
|
||||
// BlobString internally is just BlobArray, so do a little C++ magic here and reinterpret BlobString as BlobArray (with hope that first member of former will remain as its data)
|
||||
ref var stringAsArr = ref UnsafeUtility.As<BlobString, BlobArray<byte>>(ref s);
|
||||
// Ignoring trailing zero byte
|
||||
hasher.Update(stringAsArr.GetUnsafePtr(), stringAsArr.Length - 1);
|
||||
var rv = new Hash128(hasher.DigestHash128());
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static FixedStringName ToFixedString(ref this BlobString s)
|
||||
{
|
||||
var rv = new FixedStringName();
|
||||
if (s.Length > 0)
|
||||
s.CopyTo(ref rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Truncate string if needed
|
||||
public static unsafe ConversionError CopyToWithTruncate<T>(ref this BlobString bs, ref T dest) where T : INativeList<byte>
|
||||
{
|
||||
fixed (BlobString* blobStringPtr = &bs)
|
||||
{
|
||||
var ba = (BlobArray<byte>*)blobStringPtr;
|
||||
byte* srcBuffer = (byte*)ba->GetUnsafePtr();
|
||||
int srcLength = bs.Length;
|
||||
dest.Length = math.min(srcLength, dest.Capacity);
|
||||
byte* destBuffer = (byte*)UnsafeUtility.AddressOf(ref dest.ElementAt(0));
|
||||
int destCapacity = dest.Capacity;
|
||||
var err = Unicode.Utf8ToUtf8(srcBuffer, srcLength, destBuffer, out var destLength, destCapacity);
|
||||
dest.Length = destLength;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 310ed281f38d25a4e9ef7e6a70713c08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/BlobStringExtensions.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Unity.Collections;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class BurstAssert
|
||||
{
|
||||
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||||
[Conditional("UNITY_DOTS_DEBUG")]
|
||||
public static void IsTrue(bool c, in FixedString128Bytes errorMessage)
|
||||
{
|
||||
if (c) return;
|
||||
|
||||
Debug.LogError(errorMessage);
|
||||
throw new Exception("Assertion Failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3506a2f7d3b6b4b4d9a5a1a755e47af7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/BurstAssert.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Collections;
|
||||
using System;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class CollectionUtils
|
||||
{
|
||||
public static unsafe NativeArray<T> AsArray<T>(this NativeSlice<T> v) where T: unmanaged
|
||||
{
|
||||
var ptr = v.GetUnsafePtr();
|
||||
var rv = CollectionHelper.ConvertExistingDataToNativeArray<T>(ptr, v.Length, Allocator.None, true);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static unsafe UnsafeList<T> AsUnsafeList<T>(this NativeSlice<T> v) where T: unmanaged
|
||||
{
|
||||
var ptr = (T*)v.GetUnsafePtr();
|
||||
var rv = new UnsafeList<T>(ptr, v.Length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void ValidateSpanCreationParameters<T>(this NativeList<T> v, int startIndex, int length) where T: unmanaged
|
||||
{
|
||||
if (startIndex >= v.Length)
|
||||
{
|
||||
throw new InvalidOperationException($"Requested span start index exceed list size (Start index {startIndex}, list length {v.Length})!");
|
||||
}
|
||||
|
||||
if (startIndex + length > v.Length)
|
||||
{
|
||||
throw new InvalidOperationException($"Requested span exceed end of list (Start index {startIndex}, requested length {length}, list length {v.Length})!");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static unsafe Span<T> GetSpan<T>(this NativeList<T> v, int startIndex, int length) where T: unmanaged
|
||||
{
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
ValidateSpanCreationParameters(v, startIndex, length);
|
||||
#endif
|
||||
var rv = new Span<T>(v.GetUnsafePtr() + startIndex, length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static unsafe ReadOnlySpan<T> GetReadOnlySpan<T>(this NativeList<T> v, int startIndex, int length) where T: unmanaged
|
||||
{
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
ValidateSpanCreationParameters(v, startIndex, length);
|
||||
#endif
|
||||
var rv = new ReadOnlySpan<T>(v.GetUnsafeReadOnlyPtr() + startIndex, length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static unsafe Span<T> AsSpan<T>(this UnsafeList<T> l) where T: unmanaged
|
||||
{
|
||||
var rv = new Span<T>(l.Ptr, l.Length);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b14200f7f9ddc2a4ebfa9a99269c35c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/CollectionUtils.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,49 @@
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class ColorTools
|
||||
{
|
||||
public static int ToInt(this Color32 c)
|
||||
{
|
||||
var rv = c.r << 24 | c.g << 16 | c.b << 8 | c.a;
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Color32 FromInt(int v)
|
||||
{
|
||||
var rv = new Color32((byte)(v >> 24 & 0xff), (byte)(v >> 16 & 0xff), (byte)(v >> 8 & 0xff), (byte)(v & 0xff));
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static string ToWebColor(Color c)
|
||||
{
|
||||
Color32 c32 = c;
|
||||
var rv = $"#{c32.r:X2}{c32.g:X2}{c32.b:X2}";
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static uint ToUint(float4 cl)
|
||||
{
|
||||
uint rv = (uint)(cl.x * 255) << 24 | (uint)(cl.y * 255) << 16 | (uint)(cl.z * 255) << 8 | (uint)(cl.w * 255);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static uint ToUint(Color cl)
|
||||
{
|
||||
uint rv = (uint)(cl.r * 255) << 24 | (uint)(cl.g * 255) << 16 | (uint)(cl.b * 255) << 8 | (uint)(cl.a * 255);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df6f54b178b181e4ba2608c3f2899494
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/ColorTools.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class CommonTools
|
||||
{
|
||||
public static string FormatMemory(long mem)
|
||||
{
|
||||
float floatMem = mem;
|
||||
string[] suffixes = {"B", "KB", "MB", "GB", "TB"};
|
||||
var suffixIndex = 0;
|
||||
while (floatMem > 1024.0f)
|
||||
{
|
||||
++suffixIndex;
|
||||
floatMem /= 1024.0f;
|
||||
}
|
||||
|
||||
return $"{floatMem:F2}{suffixes[suffixIndex]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31bbab48ee3d07f45816e00fff8be401
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/CommonTools.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,23 @@
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class EntityTools
|
||||
{
|
||||
public static unsafe bool TryGetChunkComponentData<T>(ArchetypeChunk chunk, Entity otherChunkEntity, ref ComponentTypeHandle<T> typeHandle, out T outChunkData) where T : unmanaged, IComponentData
|
||||
{
|
||||
outChunkData = default;
|
||||
var otherChunkIndex = chunk.m_EntityComponentStore->GetChunk(otherChunkEntity);
|
||||
var otherChunk = new ArchetypeChunk(otherChunkIndex, chunk.m_EntityComponentStore);
|
||||
if (!otherChunk.HasChunkComponent<T>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outChunkData = otherChunk.GetChunkComponentData(ref typeHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73bfa6708a643fb4ea22e6434a330862
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/EntityTools.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,55 @@
|
||||
using Unity.Collections;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
using Unity.Core;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class FixedStringExtensions
|
||||
{
|
||||
public static unsafe Hash128 CalculateHash128(in this FixedStringName s)
|
||||
{
|
||||
if (s.IsEmpty)
|
||||
return default;
|
||||
|
||||
var hasher = new xxHash3.StreamingState();
|
||||
hasher.Update(s.GetUnsafePtr(), s.Length);
|
||||
var rv = new Hash128(hasher.DigestHash128());
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Hash128 CalculateHash128(this string s)
|
||||
{
|
||||
if (s == null)
|
||||
return default;
|
||||
var fs = new FixedStringName(s);
|
||||
return fs.CalculateHash128();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static unsafe uint CalculateHash32(in this FixedStringName s)
|
||||
{
|
||||
if (s.IsEmpty)
|
||||
return default;
|
||||
|
||||
var rv = XXHash.Hash32(s.GetUnsafePtr(), s.Length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static uint CalculateHash32(this string s)
|
||||
{
|
||||
if (s == null)
|
||||
return 0;
|
||||
var fs = new FixedStringName(s);
|
||||
return fs.CalculateHash32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f06bf9c3c6ae8754e8de05db3e5de1ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/FixedStringExtensions.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fafff41a2421b6478b0143d886c205e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,169 @@
|
||||
using Unity.Assertions;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class ComputeBufferTools
|
||||
{
|
||||
static ComputeShader copyCS;
|
||||
static ComputeKernel copyKernel;
|
||||
static ComputeKernel clearKernel;
|
||||
|
||||
static readonly string COMPUTE_SHADER_NAME = "RukhankaGPUBufferManipulation";
|
||||
static readonly string COPY_KERNEL_NAME = "CopyBuffer";
|
||||
static readonly string CLEAR_KERNEL_NAME = "ClearBuffer";
|
||||
|
||||
static readonly int ShaderID_copyBufferElementsCount = Shader.PropertyToID("copyBufferElementsCount");
|
||||
static readonly int ShaderID_srcBuf = Shader.PropertyToID("srcBuf");
|
||||
static readonly int ShaderID_dstBuf = Shader.PropertyToID("dstBuf");
|
||||
static readonly int ShaderID_srcOffset = Shader.PropertyToID("srcOffset");
|
||||
static readonly int ShaderID_dstOffset = Shader.PropertyToID("dstOffset");
|
||||
static readonly int ShaderID_clearValue = Shader.PropertyToID("clearValue");
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static GraphicsBuffer GrowNoCopy(GraphicsBuffer gb, int newElementCount)
|
||||
{
|
||||
Assert.IsNotNull(gb);
|
||||
if (gb == null)
|
||||
return null;
|
||||
|
||||
if (newElementCount <= gb.count)
|
||||
return gb;
|
||||
|
||||
var newBuf = new GraphicsBuffer(gb.target, gb.usageFlags, newElementCount, gb.stride);
|
||||
gb.Dispose();
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void Copy<T>(GraphicsBuffer src, GraphicsBuffer dst, uint srcOffsetInElements, uint dstOffsetInElements, uint copyCount) where T: unmanaged
|
||||
{
|
||||
var elementSizeInBytes = UnsafeUtility.SizeOf<T>();
|
||||
Copy(src, dst, (uint)(srcOffsetInElements * elementSizeInBytes), (uint)(dstOffsetInElements * elementSizeInBytes), (uint)(copyCount * elementSizeInBytes));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void Copy(GraphicsBuffer src, GraphicsBuffer dst, uint srcOffsetInBytes, uint dstOffsetInBytes, uint numBytesToCopy)
|
||||
{
|
||||
Assert.IsTrue(numBytesToCopy % 4 == 0);
|
||||
Assert.IsTrue(srcOffsetInBytes % 4 == 0);
|
||||
Assert.IsTrue(dstOffsetInBytes % 4 == 0);
|
||||
|
||||
var numIntsToCopy = numBytesToCopy / 4;
|
||||
if (numIntsToCopy > 0)
|
||||
{
|
||||
copyCS ??= Resources.Load<ComputeShader>(COMPUTE_SHADER_NAME);
|
||||
copyKernel ??= new ComputeKernel(copyCS, COPY_KERNEL_NAME);
|
||||
|
||||
const uint maxDispatchCount = 0xffff;
|
||||
uint maxCopyOpsForSingleDispatch = maxDispatchCount * copyKernel.numThreadGroups.x;
|
||||
uint copyByteCounter = 0;
|
||||
while (numIntsToCopy > 0)
|
||||
{
|
||||
var iterationNumCopyOps = math.min(maxCopyOpsForSingleDispatch, numIntsToCopy);
|
||||
numIntsToCopy -= iterationNumCopyOps;
|
||||
copyCS.SetBuffer(copyKernel, ShaderID_srcBuf, src);
|
||||
copyCS.SetBuffer(copyKernel, ShaderID_dstBuf, dst);
|
||||
copyCS.SetInt(ShaderID_copyBufferElementsCount, (int)iterationNumCopyOps);
|
||||
copyCS.SetInt(ShaderID_dstOffset, (int)(dstOffsetInBytes + copyByteCounter));
|
||||
copyCS.SetInt(ShaderID_srcOffset, (int)(srcOffsetInBytes + copyByteCounter));
|
||||
copyKernel.Dispatch((int)iterationNumCopyOps, 1, 1);
|
||||
copyByteCounter += iterationNumCopyOps * 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// We cannot resize compute buffer. The only way is to create new, copy data, destroy old and return created one.
|
||||
public static GraphicsBuffer Resize(GraphicsBuffer gb, int newElementCount)
|
||||
{
|
||||
Assert.IsNotNull(gb);
|
||||
if (gb == null)
|
||||
return null;
|
||||
|
||||
// In case of new and old sizes match simply return
|
||||
if (newElementCount == gb.count)
|
||||
return gb;
|
||||
|
||||
Assert.IsTrue((gb.target & GraphicsBuffer.Target.Raw) != 0, "Graphics buffer must be created with 'GraphicsBuffer.Target.Raw' flag");
|
||||
|
||||
var newBuf = new GraphicsBuffer(gb.target, gb.usageFlags, newElementCount, gb.stride);
|
||||
var elementsToCopy = math.min(newBuf.count, gb.count);
|
||||
Copy(gb, newBuf, 0, 0, (uint)(elementsToCopy * gb.stride));
|
||||
gb.Dispose();
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void Clear(GraphicsBuffer gb, uint startByteOffset, uint clearBytesCount, uint clearValue = 0)
|
||||
{
|
||||
Assert.IsNotNull(gb);
|
||||
Assert.IsTrue(clearBytesCount % 4 == 0);
|
||||
if (gb == null)
|
||||
return;
|
||||
|
||||
Assert.IsTrue((gb.target & GraphicsBuffer.Target.Raw) != 0, "Partial clear only for raw buffers");
|
||||
|
||||
if (clearBytesCount > 0)
|
||||
{
|
||||
copyCS ??= Resources.Load<ComputeShader>(COMPUTE_SHADER_NAME);
|
||||
clearKernel ??= new ComputeKernel(copyCS, CLEAR_KERNEL_NAME);
|
||||
|
||||
const uint maxDispatchCount = 0xffff;
|
||||
uint maxClearOpsForSingleDispatch = maxDispatchCount * clearKernel.numThreadGroups.x;
|
||||
var numIntsToClear = clearBytesCount / 4;
|
||||
uint clearByteCounter = 0;
|
||||
while (numIntsToClear > 0)
|
||||
{
|
||||
var iterationNumClearOps = math.min(maxClearOpsForSingleDispatch, numIntsToClear);
|
||||
numIntsToClear -= iterationNumClearOps;
|
||||
copyCS.SetBuffer(clearKernel, ShaderID_dstBuf, gb);
|
||||
copyCS.SetInt(ShaderID_copyBufferElementsCount, (int)iterationNumClearOps);
|
||||
copyCS.SetInt(ShaderID_dstOffset, (int)(startByteOffset + clearByteCounter));
|
||||
copyCS.SetInt(ShaderID_clearValue, (int)clearValue);
|
||||
clearKernel.Dispatch((int)iterationNumClearOps, 1, 1);
|
||||
clearByteCounter += iterationNumClearOps * 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static GraphicsBuffer CreateOrGrowGraphicsBuffer<T>
|
||||
(
|
||||
GraphicsBuffer gb,
|
||||
GraphicsBuffer.Target target,
|
||||
GraphicsBuffer.UsageFlags usage,
|
||||
int elementCount,
|
||||
bool preserveContents,
|
||||
int extraCountAfterResize = 0x1000
|
||||
) where T: unmanaged
|
||||
{
|
||||
if (gb == null)
|
||||
{
|
||||
// In case of zero input size, increase it to make a buffer in any case
|
||||
elementCount = math.max(0xff, elementCount);
|
||||
gb = new GraphicsBuffer(target, usage, elementCount, UnsafeUtility.SizeOf<T>());
|
||||
return gb;
|
||||
}
|
||||
|
||||
if (elementCount <= gb.count)
|
||||
return gb;
|
||||
|
||||
// To prevent frequent buffer recreations, resize buffer with some additional capacity
|
||||
elementCount += extraCountAfterResize;
|
||||
|
||||
gb = preserveContents ? Resize(gb, elementCount) : GrowNoCopy(gb, elementCount);
|
||||
return gb;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed8ef19ce487f1945a429a413ea24b6c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/GPU/ComputeBufferTools.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,76 @@
|
||||
using Unity.Assertions;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using SystemInfo = UnityEngine.Device.SystemInfo;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public class ComputeKernel
|
||||
{
|
||||
public readonly uint3 numThreadGroups;
|
||||
public readonly int kernelIndex;
|
||||
public readonly string kernelName;
|
||||
public readonly ComputeShader computeShader;
|
||||
readonly uint MAX_WORKGROUP_COUNT = 0xffff;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ComputeKernel(ComputeShader cs, string kernelName)
|
||||
{
|
||||
kernelIndex = cs.FindKernel(kernelName);
|
||||
this.kernelName = kernelName;
|
||||
cs.GetKernelThreadGroupSizes(kernelIndex, out numThreadGroups.x, out numThreadGroups.y, out numThreadGroups.z);
|
||||
Assert.IsTrue(numThreadGroups.x <= SystemInfo.maxComputeWorkGroupSizeX, $"Kernel '{kernelName}({kernelIndex})' of shader '{cs.name}' work group size X '{numThreadGroups.x}' exceeds hardware limit of '{SystemInfo.maxComputeWorkGroupSizeX}'");
|
||||
Assert.IsTrue(numThreadGroups.y <= SystemInfo.maxComputeWorkGroupSizeY, $"Kernel '{kernelName}({kernelIndex})' of shader '{cs.name}' work group size Y '{numThreadGroups.y}' exceeds hardware limit of '{SystemInfo.maxComputeWorkGroupSizeY}'");
|
||||
Assert.IsTrue(numThreadGroups.z <= SystemInfo.maxComputeWorkGroupSizeZ, $"Kernel '{kernelName}({kernelIndex})' of shader '{cs.name}' work group size Z '{numThreadGroups.z}' exceeds hardware limit of '{SystemInfo.maxComputeWorkGroupSizeZ}'");
|
||||
computeShader = cs;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispatch(int workGroupSizeX, int workgroupSizeY, int workgroupSizeZ)
|
||||
{
|
||||
var workGroupSize = new int3(workGroupSizeX, workgroupSizeY, workgroupSizeZ);
|
||||
var numDispatches = (int3)math.ceil(workGroupSize / (float3)numThreadGroups);
|
||||
#if UNITY_ASSERTIONS
|
||||
if (ValidateDispatch(workGroupSizeX, workgroupSizeY, workgroupSizeZ))
|
||||
#endif
|
||||
computeShader.Dispatch(kernelIndex, numDispatches.x, numDispatches.y, numDispatches.z);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispatch(uint workGroupSizeX, uint workgroupSizeY, uint workgroupSizeZ)
|
||||
{
|
||||
Dispatch((int)workGroupSizeX, (int)workgroupSizeY, (int)workgroupSizeZ);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public uint3 GetMaxWorkGroupSize()
|
||||
{
|
||||
return MAX_WORKGROUP_COUNT * numThreadGroups;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool ValidateDispatch(int workGroupSizeX, int workgroupSizeY, int workgroupSizeZ)
|
||||
{
|
||||
var workGroupSize = new int3(workGroupSizeX, workgroupSizeY, workgroupSizeZ);
|
||||
var numDispatches = (int3)math.ceil(workGroupSize / (float3)numThreadGroups);
|
||||
if (numDispatches.x > MAX_WORKGROUP_COUNT)
|
||||
Debug.LogError($"Kernel '{kernelName}({kernelIndex})' of shader '{computeShader.name}' dispatch thread group count X '{numDispatches.x}' exceeds hardware limit of '{MAX_WORKGROUP_COUNT}'. Try to increase kernel work group size.");
|
||||
if (numDispatches.y > MAX_WORKGROUP_COUNT)
|
||||
Debug.LogError($"Kernel '{kernelName}({kernelIndex})' of shader '{computeShader.name}' dispatch thread group count Y '{numDispatches.y}' exceeds hardware limit of '{MAX_WORKGROUP_COUNT}'. Try to increase kernel work group size.");
|
||||
if (numDispatches.z > MAX_WORKGROUP_COUNT)
|
||||
Debug.LogError($"Kernel '{kernelName}({kernelIndex})' of shader '{computeShader.name}' dispatch thread group count Z '{numDispatches.z}' exceeds hardware limit of '{MAX_WORKGROUP_COUNT}'. Try to increase kernel work group size.");
|
||||
return math.all(numDispatches <= (int)MAX_WORKGROUP_COUNT);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static implicit operator int(ComputeKernel c) => c.kernelIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e59641556c7d860438f1848a81d5e0ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/GPU/ComputeKernel.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Rendering;
|
||||
using Unity.Assertions;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
|
||||
// This class purpose to wrap compute buffer which updated every frame
|
||||
public class FrameFencedGPUBufferPool<T>: IDisposable where T: unmanaged
|
||||
{
|
||||
BufferPool bufferPool;
|
||||
NativeQueue<int> busyBuffers;
|
||||
int currentFrameBufferIndex = -1;
|
||||
int elementCount;
|
||||
readonly GraphicsBuffer.Target bufferTargetFlags;
|
||||
readonly GraphicsBuffer.UsageFlags bufferUsageFlags;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public FrameFencedGPUBufferPool(int count, GraphicsBuffer.Target target, GraphicsBuffer.UsageFlags usageFlags)
|
||||
{
|
||||
elementCount = count;
|
||||
bufferTargetFlags = target;
|
||||
bufferUsageFlags = usageFlags;
|
||||
bufferPool = new BufferPool(count, UnsafeUtility.SizeOf<T>(), target, usageFlags);
|
||||
busyBuffers = new (Allocator.Persistent);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
busyBuffers.Dispose();
|
||||
bufferPool?.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void BeginFrame()
|
||||
{
|
||||
Assert.IsTrue(currentFrameBufferIndex == -1);
|
||||
RecoverBuffers();
|
||||
currentFrameBufferIndex = bufferPool.GetBufferId();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
Assert.IsFalse(currentFrameBufferIndex == -1);
|
||||
busyBuffers.Enqueue(currentFrameBufferIndex);
|
||||
currentFrameBufferIndex = -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RecoverBuffers()
|
||||
{
|
||||
var totalLiveFrames = SparseUploader.NumFramesInFlight + 1;
|
||||
for (var i = totalLiveFrames; i < busyBuffers.Count; ++i)
|
||||
{
|
||||
var bufID = busyBuffers.Dequeue();
|
||||
bufferPool.PutBufferId(bufID);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool Grow(int requiredElementsCount)
|
||||
{
|
||||
var chunkSizeInBytes = 0x1000 * 16;
|
||||
var elementSizeInBytes = UnsafeUtility.SizeOf<T>();
|
||||
var chunkSizeInElements = chunkSizeInBytes / elementSizeInBytes;
|
||||
var newSizeInElements = requiredElementsCount + chunkSizeInElements;
|
||||
if (elementCount < newSizeInElements)
|
||||
{
|
||||
var newSizeInBytes = newSizeInElements * elementSizeInBytes;
|
||||
|
||||
if (newSizeInBytes > SystemInfo.maxGraphicsBufferSize)
|
||||
{
|
||||
var formattedRequiredBytes = CommonTools.FormatMemory(newSizeInBytes);
|
||||
var formattedMaxComputeBufferBytes = CommonTools.FormatMemory(SystemInfo.maxGraphicsBufferSize);
|
||||
throw new InvalidOperationException($"Requested buffer size ({requiredElementsCount} elements, {formattedRequiredBytes}) exceeded maximum compute buffer capacity of '{formattedMaxComputeBufferBytes}'");
|
||||
}
|
||||
|
||||
bufferPool?.Dispose();
|
||||
busyBuffers.Clear();
|
||||
bufferPool = new BufferPool(newSizeInElements, UnsafeUtility.SizeOf<T>(), bufferTargetFlags, bufferUsageFlags);
|
||||
elementCount = newSizeInElements;
|
||||
currentFrameBufferIndex = -1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public NativeArray<T> LockBufferForWrite(int startIndex, int count)
|
||||
{
|
||||
var b = bufferPool.GetBufferFromId(currentFrameBufferIndex);
|
||||
var rv = b.LockBufferForWrite<T>(startIndex, count);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void UnlockBufferAfterWrite(int count)
|
||||
{
|
||||
var b = bufferPool.GetBufferFromId(currentFrameBufferIndex);
|
||||
b.UnlockBufferAfterWrite<T>(count);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public GraphicsBuffer GetBuffer()
|
||||
{
|
||||
if (currentFrameBufferIndex < 0)
|
||||
return null;
|
||||
|
||||
var b = bufferPool.GetBufferFromId(currentFrameBufferIndex);
|
||||
return b;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static implicit operator GraphicsBuffer(FrameFencedGPUBufferPool<T> b) => b.GetBuffer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 348ddec2996af1d44bc38079d7dfa13e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/GPU/FrameFencedGPUBufferPool.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f33d6ad918265a147aaf4ae0b3655ab8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#pragma kernel CopyBuffer
|
||||
#pragma kernel ClearBuffer
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ByteAddressBuffer srcBuf;
|
||||
RWByteAddressBuffer dstBuf;
|
||||
int copyBufferElementsCount;
|
||||
int srcOffset, dstOffset;
|
||||
int clearValue;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[numthreads(128, 1, 1)]
|
||||
void CopyBuffer(uint tid: SV_DispatchThreadID)
|
||||
{
|
||||
if (tid >= (uint)copyBufferElementsCount)
|
||||
return;
|
||||
|
||||
uint inDataOffset = tid * 4 + srcOffset;
|
||||
int v = srcBuf.Load(inDataOffset);
|
||||
uint outDataOffset = tid * 4 + dstOffset;
|
||||
dstBuf.Store(outDataOffset, v);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[numthreads(128, 1, 1)]
|
||||
void ClearBuffer(uint tid: SV_DispatchThreadID)
|
||||
{
|
||||
if (tid >= (uint)copyBufferElementsCount)
|
||||
return;
|
||||
|
||||
uint outDataOffset = tid * 4 + dstOffset;
|
||||
dstBuf.Store(outDataOffset, clearValue);
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2f4338f751d23e4db39523b314244b9
|
||||
ComputeShaderImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/GPU/Resources/RukhankaGPUBufferManipulation.compute
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Rendering;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public class SparseUploaderPool: IDisposable
|
||||
{
|
||||
Stack<SparseUploader> freeUploaders;
|
||||
List<SparseUploader> allUploaders;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public SparseUploaderPool()
|
||||
{
|
||||
freeUploaders = new ();
|
||||
allUploaders = new ();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var u in allUploaders)
|
||||
{
|
||||
u.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void FrameCleanup()
|
||||
{
|
||||
foreach (var u in allUploaders)
|
||||
{
|
||||
u.FrameCleanup();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public SparseUploader GetUploader(GraphicsBuffer gb)
|
||||
{
|
||||
SparseUploader rv;
|
||||
if (freeUploaders.Count > 0)
|
||||
{
|
||||
rv = freeUploaders.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = new SparseUploader(gb);
|
||||
allUploaders.Add(rv);
|
||||
Assert.IsTrue(allUploaders.Count < 0xff, "Looks like 'PutUploader' call is forgotten somewhere! There are too much of created uploaders.");
|
||||
}
|
||||
|
||||
rv.ReplaceBuffer(gb);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void PutUploader(SparseUploader su)
|
||||
{
|
||||
freeUploaders.Push(su);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18f595187c7f2c141b045a24d8360f70
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/GPU/SparseUploaderPool.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,18 @@
|
||||
using Unity.Entities;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class HashUtils
|
||||
{
|
||||
public static uint Hash128To32(in Hash128 hash128)
|
||||
{
|
||||
var rv = hash128.Value.x;
|
||||
rv ^= hash128.Value.y + 0x9e3779b9 + (rv << 6) + (rv >> 2);
|
||||
rv ^= hash128.Value.z + 0x9e3779b9 + (rv << 6) + (rv >> 2);
|
||||
rv ^= hash128.Value.w + 0x9e3779b9 + (rv << 6) + (rv >> 2);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ecc559e328d07a478f5e6f597b5860e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/HashUtils.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Threading;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public static class InterlockedExtensions
|
||||
{
|
||||
public static bool InterlockedMax(ref int location, int newValue)
|
||||
{
|
||||
int l;
|
||||
do
|
||||
{
|
||||
l = location;
|
||||
if (l >= newValue)
|
||||
return false;
|
||||
}
|
||||
while (Interlocked.CompareExchange(ref location, newValue, l) != l);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abb6be35e7d18c04bb554b0a84b577f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/InterlockedUtils.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 280bf83f60b05754e9ae4e0f8dc9f467
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9013e38d006f06429e20a36ed21f94d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: InternalsVisibleTo("Rukhanka.Toolbox")]
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d69e8eae4edec124ba32d289cf56329a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/InternalAccess/Unity.Collections/CollectionsAssemblyVisibility.cs
|
||||
uploadId: 897522
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"reference": "GUID:e0cd26848372d4e5c891c569017e11f1"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b77a91909f1e8c3418fb16ed620a1646
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/InternalAccess/Unity.Collections/CollectionsReference.asmref
|
||||
uploadId: 897522
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc5ffebeaee9d964e8d7f2e9c593e08b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: InternalsVisibleTo("Rukhanka.Toolbox")]
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0a7897efcfe796418d2bcb3aa532b9f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/InternalAccess/Unity.Entities.Graphics/EntitiesGraphicsAssemblyVisibility.cs
|
||||
uploadId: 897522
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"reference": "GUID:7a450cf7ca9694b5a8bfa3fd83ec635a"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eff17d048cfa8d14e97492a0bee221b5
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/InternalAccess/Unity.Entities.Graphics/EntitiesGraphicsReference.asmref
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a828d39676ddbd498ea96bb7520fcd6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: InternalsVisibleTo("Rukhanka.Toolbox")]
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4908c8f39a73c4141a088f0095ee0f01
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/InternalAccess/Unity.Entities/EntitiesAssemblyVisibility.cs
|
||||
uploadId: 897522
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"reference": "GUID:734d92eba21c94caba915361bd5ac177"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8377197ae018bdd4da596e63eb682543
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/InternalAccess/Unity.Entities/EntitiesReference.asmref
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,148 @@
|
||||
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// For Bezier formulas reference use great online book: https://pomax.github.io/bezierinfo/
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
[BurstCompile]
|
||||
public static class LineTools
|
||||
{
|
||||
public static float2 Get2DLineEquationFromTwoPoints(float2 p0, float2 p1)
|
||||
{
|
||||
var k = (p1.y - p0.y) / (p1.x - p0.x);
|
||||
var b = p1.y - k * p1.x;
|
||||
return new float2(k, b);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float3 CalculateBoundingCircle(float2 p0, float2 p1, float2 p2)
|
||||
{
|
||||
var pc1 = (p1 + p0) * 0.5f;
|
||||
var pc2 = (p1 + p2) * 0.5f;
|
||||
|
||||
var p1p0 = p1 - p0;
|
||||
var p1p2 = p1 - p2;
|
||||
|
||||
float2 sincosHalfPi = new float2(1, 0);
|
||||
|
||||
// Normals
|
||||
var n1 = new float2(math.dot(p1p0, sincosHalfPi.yx * new float2(1, -1)), math.dot(p1p0, sincosHalfPi));
|
||||
var n2 = new float2(math.dot(p1p2, sincosHalfPi.yx * new float2(1, -1)), math.dot(p1p2, sincosHalfPi));
|
||||
|
||||
var pn1 = pc1 - n1;
|
||||
var pn2 = pc2 + n2;
|
||||
|
||||
// Circle center
|
||||
var dx1 = pc1.x - pn1.x;
|
||||
var dx2 = pc2.x - pn2.x;
|
||||
|
||||
var center = float2.zero;
|
||||
if (math.abs(dx1) < 0.0001f)
|
||||
{
|
||||
center.x = pc1.x;
|
||||
var kb = Get2DLineEquationFromTwoPoints(pc2, pn2);
|
||||
center.y = kb.x * center.x + kb.y;
|
||||
}
|
||||
else if (math.abs(dx2) < 0.0001f)
|
||||
{
|
||||
center.x = pc2.x;
|
||||
var kb = Get2DLineEquationFromTwoPoints(pc1, pn1);
|
||||
center.y = kb.x * center.x + kb.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
var kb1 = Get2DLineEquationFromTwoPoints(pc1, pn1);
|
||||
var kb2 = Get2DLineEquationFromTwoPoints(pc2, pn2);
|
||||
var dk = kb1.x - kb2.x;
|
||||
center.x = dk != 0 ? (kb2.y - kb1.y) / dk : 0;
|
||||
center.y = center.x * kb1.x + kb1.y;
|
||||
}
|
||||
|
||||
var circleR = math.length(p0 - center);
|
||||
|
||||
return new float3(center, circleR);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float4 BezierGetAC(float2 p0, float2 p1, float2 p2, float t)
|
||||
{
|
||||
var t2 = t * t;
|
||||
var t3 = t2 * t;
|
||||
var oneMinusT = 1 - t;
|
||||
var oneMinusT3 = oneMinusT * oneMinusT * oneMinusT;
|
||||
var ratio = math.abs((t3 + oneMinusT3 - 1) / (t3 + oneMinusT3));
|
||||
|
||||
var ut = oneMinusT3 / (t3 + oneMinusT3);
|
||||
var c = math.lerp(p2, p0, ut);
|
||||
var a = p1 + (p1 - c) / ratio;
|
||||
return new float4(a, c);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct CubicBezierCurve
|
||||
{
|
||||
public float2 s, e, c1, c2, e1, e2, a, c;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public static void ConstructBezierApproximation(in float2 p0, in float2 p1, in float2 p2, ref CubicBezierCurve rv)
|
||||
{
|
||||
var p2p0 = p2 - p0;
|
||||
var p1p0 = p1 - p0;
|
||||
var p1p2 = p1 - p2;
|
||||
|
||||
var d1 = math.length(p1p0);
|
||||
var d2 = math.length(p1p2);
|
||||
var t = d1 / (d1 + d2);
|
||||
|
||||
var angle = math.atan2(p2p0.y, p2p0.x) - math.atan2(p1p0.y, p1p0.x);
|
||||
var sgn = angle < 0 || angle > math.PI ? -1 : 1;
|
||||
var bc = math.length(p2p0) * sgn * 0.333f;
|
||||
var de1 = t * bc;
|
||||
var de2 = (1 - t) * bc;
|
||||
|
||||
var boundingCircle = CalculateBoundingCircle(p0, p1, p2);
|
||||
|
||||
var tanL = new float2(p1.x - (p1.y - boundingCircle.y), p1.y + (p1.x - boundingCircle.x));
|
||||
var tanR = new float2(p1.x + (p1.y - boundingCircle.y), p1.y - (p1.x - boundingCircle.x));
|
||||
|
||||
var tanLen = math.length(new float4(tanL, tanR));
|
||||
|
||||
var dx = (tanR.x - tanL.x) / tanLen;
|
||||
var dy = (tanR.y - tanL.y) / tanLen;
|
||||
var dxdy = new float2(dx, dy);
|
||||
|
||||
var e1 = p1 + de1 * dxdy;
|
||||
var e2 = p1 - de2 * dxdy;
|
||||
|
||||
var ac = BezierGetAC(p0, p1, p2, t);
|
||||
var a = ac.xy;
|
||||
var v1 = a + (e1 - a) / (1 - t);
|
||||
var v2 = a + (e2 - a) / t;
|
||||
var c1 = p0 + (v1 - p0) / t;
|
||||
var c2 = p2 + (v2 - p2) / (1 - t);
|
||||
|
||||
rv = new CubicBezierCurve()
|
||||
{
|
||||
c1 = c1,
|
||||
c2 = c2,
|
||||
s = p0,
|
||||
e = p2,
|
||||
e1 = e1,
|
||||
e2 = e2,
|
||||
c = ac.zw,
|
||||
a = ac.xy
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 062f7bdc9c9e8ae41b06fef4851f35f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/LineTools.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class MathUtils
|
||||
{
|
||||
public static quaternion ShortestRotation(quaternion from, quaternion to)
|
||||
{
|
||||
uint4 sign = math.asuint(math.dot(from, to)) & 0x80000000;
|
||||
var rv = math.asfloat(sign ^ math.asuint(to.value));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static quaternion FromToRotation(float3 from, float3 to)
|
||||
{
|
||||
var rv = quaternion.identity;
|
||||
|
||||
var fromNormalized = math.normalizesafe(from);
|
||||
var toNormalized = math.normalizesafe(to);
|
||||
float t = math.dot(fromNormalized, toNormalized);
|
||||
|
||||
if (t < 1 && t > -1)
|
||||
{
|
||||
var ac = math.acos(t);
|
||||
var crossP = math.cross(from, to);
|
||||
crossP = math.normalizesafe(crossP);
|
||||
rv = quaternion.AxisAngle(crossP, ac);
|
||||
}
|
||||
else if (t <= -1)
|
||||
{
|
||||
var crossP = math.cross(from, math.right());
|
||||
if (math.lengthsq(crossP) < math.EPSILON)
|
||||
crossP = math.cross(from, math.up());
|
||||
|
||||
rv = quaternion.AxisAngle(crossP, math.PI * 0.5f);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static quaternion FromToRotationForNormalizedVectors(float3 from, float3 to)
|
||||
{
|
||||
var w = math.cross(from, to);
|
||||
var q = new quaternion(w.x, w.y, w.z, math.dot(from, to) + 1);
|
||||
q = math.normalize(q);
|
||||
return q;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Shuffle array according to given indices
|
||||
public static void ShuffleArray<T>(Span<T> arr, in NativeArray<int> shuffleIndices) where T: unmanaged
|
||||
{
|
||||
if (arr.Length < 2) return;
|
||||
if (arr.Length != shuffleIndices.Length) return;
|
||||
|
||||
Span<T> scatterArr = stackalloc T[arr.Length];
|
||||
for (int i = 0; i < arr.Length; ++i)
|
||||
{
|
||||
var shuffleIndex = shuffleIndices[i];
|
||||
var v = arr[shuffleIndex];
|
||||
scatterArr[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < arr.Length; ++i)
|
||||
{
|
||||
arr[i] = scatterArr[i];
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Result is not normalized
|
||||
public static float3 MakePerpendicularVector(float3 v)
|
||||
{
|
||||
var av = math.abs(v);
|
||||
|
||||
if (av.z < math.min(av.x, av.y))
|
||||
return new float3(v.y, -v.x, 0);
|
||||
|
||||
if (av.y < av.x)
|
||||
return new float3(-v.z, 0, v.x);
|
||||
|
||||
return new float3(0, v.z, -v.y);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static bool PlaneLineIntersection(float3 linePoint, float3 lineVector, float4 plane, ref float3 intersectionPoint)
|
||||
{
|
||||
var fv = math.dot(lineVector, plane.xyz);
|
||||
if (math.abs(fv) > math.EPSILON)
|
||||
{
|
||||
intersectionPoint = linePoint - lineVector * math.dot(plane, new float4(linePoint, 1)) * math.rcp(fv);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static float4 BuildPlaneFromNormalAndPoint(float3 n, float3 p)
|
||||
{
|
||||
var d = -math.dot(n, p);
|
||||
var pln = new float4(n, d);
|
||||
return pln;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static float4 BuildPlaneFromThreePoints(float3 p0, float3 p1, float3 p2)
|
||||
{
|
||||
var dp0 = p1 - p0;
|
||||
var dp1 = p2 - p0;
|
||||
|
||||
var n = math.cross(dp0, dp1);
|
||||
n = math.normalizesafe(n);
|
||||
return BuildPlaneFromNormalAndPoint(n, p0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 560e6f6251a941447be0ee9a4c653c42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/MathUtils.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Threading;
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class NativeExtensions
|
||||
{
|
||||
public static unsafe void SetBitThreadSafe(this UnsafeBitArray ba, int pos)
|
||||
{
|
||||
#if UNITY_BURST_EXPERIMENTAL_ATOMIC_INTRINSICS
|
||||
var idx = pos >> 6;
|
||||
var shift = pos & 0x3f;
|
||||
var value = 1ul << shift;
|
||||
Common.InterlockedOr(ref UnsafeUtility.AsRef<ulong>(ba.Ptr + idx), value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static unsafe void InterlockedMax(int* ptr, int cmpVal)
|
||||
{
|
||||
ref var r = ref UnsafeUtility.ArrayElementAsRef<int>(ptr, 0);
|
||||
int maxVal;
|
||||
int v;
|
||||
|
||||
do
|
||||
{
|
||||
v = r;
|
||||
maxVal = math.max(v, cmpVal);
|
||||
} while (r != Interlocked.CompareExchange(ref r, maxVal, v));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4cab40a62cceca4bb8542c175862553
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/NativeExtensions.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,227 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Assertions;
|
||||
using Unity.Burst;
|
||||
using Unity.Burst.CompilerServices;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Random = Unity.Mathematics.Random;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
|
||||
// This is semi-perfect hash table tuned for query performance but with reasonable build time in mind
|
||||
// True perfect hash table over known data can be made by brute forcing seed that produces hashes with all-unique
|
||||
// positions for input data elements. But such brute forcing can be very lengthy and/or produce to big hash map
|
||||
// data containers (array with many unoccupied slots). So, to reduce build time and space requirements, I've made
|
||||
// a perfect hash table with a relaxed restriction: each slot can have maximum 1 collision (2 elements). With this
|
||||
// constraint in mind table query can be very performant. It is also GPU friendly. I am suggesting very interesting
|
||||
// perfect hash optimization research: https://www.youtube.com/watch?v=DMQ_HcNSOAI
|
||||
[BurstCompile]
|
||||
public static class Perfect2HashTable
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe int Query(uint v, uint seed, uint2* phtPtr, int phtLen)
|
||||
{
|
||||
var modMask = phtLen - 1;
|
||||
var index = GetValueIndex(v, seed, modMask);
|
||||
var phtv = phtPtr[index];
|
||||
if (Hint.Likely(phtv.x == v))
|
||||
return (int)(phtv.y & 0xffff);
|
||||
var nextIndex = (int)(phtv.y >> 16);
|
||||
var phtv2 = phtPtr[nextIndex];
|
||||
if (Hint.Likely(phtv2.x == v))
|
||||
return (int)(phtv2.y & 0xffff);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public static bool Build(in NativeArray<uint> inData, out NativeArray<uint2> outPht, out uint outSeed)
|
||||
{
|
||||
var numTries = 0xffff;
|
||||
var maxPHTSize = 0xffff;
|
||||
var phtSize = math.ceilpow2(inData.Length);
|
||||
BurstAssert.IsTrue(phtSize <= maxPHTSize, $"Maximum table size is {maxPHTSize}. Requested size is {phtSize}");
|
||||
|
||||
var rng = new Random(0x811C9DC5);
|
||||
while (phtSize <= maxPHTSize)
|
||||
{
|
||||
var phtSlotUsage = new NativeArray<int>(phtSize, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
|
||||
var seed = rng.NextUInt();
|
||||
var i = 0;
|
||||
for (; i < numTries; ++i)
|
||||
{
|
||||
if (BuildIteration(inData, ref phtSlotUsage, seed))
|
||||
break;
|
||||
seed = rng.NextUInt();
|
||||
}
|
||||
|
||||
if (i < numTries)
|
||||
{
|
||||
outPht = MakePHTWithSeed(inData, seed, phtSlotUsage);
|
||||
outSeed = seed;
|
||||
return true;
|
||||
}
|
||||
phtSize <<= 1;
|
||||
}
|
||||
outPht = default;
|
||||
outSeed = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static NativeArray<uint2> MakePHTWithSeed(in NativeArray<uint> inData, uint seed, NativeArray<int> phtSlotUsage)
|
||||
{
|
||||
BurstAssert.IsTrue(math.ispow2(phtSlotUsage.Length), "Hash table size must be power of 2");
|
||||
var modMask = phtSlotUsage.Length - 1;
|
||||
var rv = new NativeArray<uint2>(phtSlotUsage.Length, Allocator.Temp);
|
||||
for (var i = 0; i < inData.Length; ++i)
|
||||
{
|
||||
var v = inData[i];
|
||||
var hv = Hash_Lemer(v, seed);
|
||||
var index = (int)(hv & modMask);
|
||||
var slotUsageCount = phtSlotUsage[index];
|
||||
BurstAssert.IsTrue(slotUsageCount == 1 || slotUsageCount == 2, "Slot usage should be one or two");
|
||||
// If slot already occupied, move value to next free slot
|
||||
if (rv[index].x != 0)
|
||||
{
|
||||
for (int k = 0; k < rv.Length; ++k)
|
||||
{
|
||||
var nextIdx = (k + i) & modMask;
|
||||
if (phtSlotUsage[nextIdx] == 0)
|
||||
{
|
||||
rv[nextIdx] = new uint2(v, (uint)i);
|
||||
|
||||
// Make a pointer to this slot from base slot
|
||||
var iv = rv[index];
|
||||
iv.y |= (uint)nextIdx << 16;
|
||||
rv[index] = iv;
|
||||
|
||||
// Mark slot as occupied
|
||||
phtSlotUsage[nextIdx] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rv[index] = new uint2(v, (uint)i);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static uint FNV1aBody(uint v, uint hash)
|
||||
{
|
||||
return (v ^ hash) * 0x01000193;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static uint Hash_FNV1a(uint v, uint seed)
|
||||
{
|
||||
uint4 vb = new uint4(v, v >> 8, v >> 16, v >> 24);
|
||||
seed = FNV1aBody(vb.x, seed);
|
||||
seed = FNV1aBody(vb.y, seed);
|
||||
seed = FNV1aBody(vb.z, seed);
|
||||
seed = FNV1aBody(vb.w, seed);
|
||||
return seed;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static uint Hash_CRC(uint v, uint seed)
|
||||
{
|
||||
const uint poly = 0x82f63b78;
|
||||
for (var i = 0; i < 4; ++i)
|
||||
{
|
||||
seed ^= v >> (i * 8);
|
||||
for (int k = 0; k < 8; k++)
|
||||
seed = (seed & 1) != 0 ? (seed >> 1) ^ poly : seed >> 1;
|
||||
}
|
||||
return ~seed;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// High-Order Half of 64-Bit Product
|
||||
// Ref "Hackers Delight" 8-2
|
||||
static uint MultiplyHighUnsigned(uint u, uint v)
|
||||
{
|
||||
uint u0 = u & 0xffff;
|
||||
uint u1 = u >> 16;
|
||||
uint v0 = v & 0xffff;
|
||||
uint v1 = v >> 16;
|
||||
uint w0 = u0 * v0;
|
||||
uint t = u1*v0 + (w0 >> 16);
|
||||
uint w1 = t & 0xFFFF;
|
||||
uint w2 = t >> 16;
|
||||
w1 = u0 * v1 + w1;
|
||||
return u1 * v1 + w2 + (w1 >> 16);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint Hash_Lemer_32bitAriphmetic(uint v, uint seed)
|
||||
{
|
||||
seed = v ^ seed;
|
||||
uint tmpH = MultiplyHighUnsigned(seed, 0x4a39b70d);
|
||||
uint tmpL = seed * 0x4a39b70d;
|
||||
uint m1 = tmpH ^ tmpL;
|
||||
tmpH = MultiplyHighUnsigned(m1, 0x12fad5c9);
|
||||
tmpL = m1 * 0x12fad5c9;
|
||||
var m2 = tmpH ^ tmpL;
|
||||
return m2;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static uint Hash_Lemer(uint v, uint seed)
|
||||
{
|
||||
seed = v ^ seed;
|
||||
ulong tmp = seed * 0x4a39b70dul;
|
||||
var m1 = (uint)((tmp >> 32) ^ tmp);
|
||||
tmp = m1 * 0x12fad5c9ul;
|
||||
var m2 = (uint)((tmp >> 32) ^ tmp);
|
||||
return m2;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static int GetValueIndex(uint v, uint seed, int modMask)
|
||||
{
|
||||
var hv = Hash_Lemer(v, seed);
|
||||
var rv = (int)(hv & modMask);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static unsafe bool BuildIteration(in NativeArray<uint> inData, ref NativeArray<int> phtSlotUsage, uint hashSeed)
|
||||
{
|
||||
UnsafeUtility.MemClear(phtSlotUsage.GetUnsafePtr(), phtSlotUsage.Length * sizeof(int));
|
||||
|
||||
var modMask = phtSlotUsage.Length - 1;
|
||||
var maxCollisions = 0;
|
||||
|
||||
for (int i = 0; i < inData.Length; ++i)
|
||||
{
|
||||
var v = inData[i];
|
||||
var index = GetValueIndex(v, hashSeed, modMask);
|
||||
phtSlotUsage[index] += 1;
|
||||
maxCollisions = math.max(phtSlotUsage[index], maxCollisions);
|
||||
}
|
||||
return maxCollisions <= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a54992059d29b3342b13734ca3b16683
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/Perfect2HashTable.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "Rukhanka.Toolbox",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:734d92eba21c94caba915361bd5ac177",
|
||||
"GUID:7a450cf7ca9694b5a8bfa3fd83ec635a",
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:2665a8d13d1b3f18800f46e256720795"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01e5f7c036964124aa192bf5e2d021ba
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/Rukhanka.Toolbox.asmdef
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Toolbox
|
||||
{
|
||||
public static class TransformUtils
|
||||
{
|
||||
public static Transform FindChildRecursively(Transform root, string name)
|
||||
{
|
||||
var rv = root.Find(name);
|
||||
if (rv != null)
|
||||
return rv;
|
||||
|
||||
var childCount = root.childCount;
|
||||
for (int i = 0; i < childCount; ++i)
|
||||
{
|
||||
var c = root.GetChild(i);
|
||||
var crv = FindChildRecursively(c, name);
|
||||
if (crv != null)
|
||||
return crv;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dccc5ea66e46944491f59a965353f29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Toolbox/TransformUtils.cs
|
||||
uploadId: 897522
|
||||
Reference in New Issue
Block a user